#!/usr/bin/env sh

set -eu

CHECK=0
UNINSTALL=0
FORCE=0
REMOVE_DATA=0
VERSION_JSON_URL='http://192.168.9.3:48080/releases/build/agentflow-version'
INSTALL_ROOT="${HOME}/.local/share/agentflow"

SCRIPT_NAME='agentflow'
PROFILE_MARKER_START='# >>> agentflow installer >>>'
PROFILE_MARKER_END='# <<< agentflow installer <<<'

log() {
  printf '[agentflow] %s\n' "$1"
}

fail() {
  printf '[agentflow] %s\n' "$1" >&2
  exit 1
}

normalize_install_root() {
  path=$1
  case "$path" in
    /*) printf '%s\n' "${path%/}" ;;
    *) printf '%s/%s\n' "$(pwd -P)" "${path%/}" ;;
  esac
}

usage() {
  cat <<EOF
Usage: install-agentflow.sh [options]

Options:
  --version-json-url URL  Remote version.json URL.
  --install-root PATH     Install root. Binary goes to PATH/bin/agentflow.
  --check                 Print install status and remote version.
  --uninstall             Remove installed binary and profile snippet.
  --remove-data           Also remove the data directory (only with --uninstall).
  --force                 Reinstall even when the local version matches.
  --help                  Show this help.
EOF
}

while [ $# -gt 0 ]; do
  case "$1" in
    --version-json-url)
      [ $# -ge 2 ] || fail 'missing value for --version-json-url'
      VERSION_JSON_URL=$2
      shift 2
      ;;
    --install-root)
      [ $# -ge 2 ] || fail 'missing value for --install-root'
      INSTALL_ROOT=$2
      shift 2
      ;;
    --check)
      CHECK=1
      shift
      ;;
    --uninstall)
      UNINSTALL=1
      shift
      ;;
    --remove-data)
      REMOVE_DATA=1
      shift
      ;;
    --force)
      FORCE=1
      shift
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      fail "unknown argument: $1"
      ;;
  esac
done

INSTALL_ROOT=$(normalize_install_root "$INSTALL_ROOT")
BIN_DIR="${INSTALL_ROOT}/bin"
BIN_PATH="${BIN_DIR}/agentflow"
DATA_DIR="${INSTALL_ROOT}/data"

ensure_version_json_url() {
  [ -n "$VERSION_JSON_URL" ] || fail 'version.json URL is required'
  case "$VERSION_JSON_URL" in
    https://downloads.example.com/*)
      fail 'VersionJsonUrl still points to the placeholder domain. Pass --version-json-url with your real release URL.'
      ;;
  esac
}

require_command() {
  command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}

detect_platform() {
  if [ -n "${AGENTFLOW_INSTALLER_OS:-}" ]; then
    printf '%s\n' "$AGENTFLOW_INSTALLER_OS"
    return
  fi
  case "$(uname -s)" in
    Linux) printf 'linux\n' ;;
    Darwin) printf 'darwin\n' ;;
    *) fail "unsupported platform: $(uname -s)" ;;
  esac
}

detect_arch() {
  if [ -n "${AGENTFLOW_INSTALLER_ARCH:-}" ]; then
    printf '%s\n' "$AGENTFLOW_INSTALLER_ARCH"
    return
  fi
  case "$(uname -m)" in
    x86_64|amd64) printf 'amd64\n' ;;
    arm64|aarch64) printf 'arm64\n' ;;
    *) fail "unsupported architecture: $(uname -m)" ;;
  esac
}

pick_profile_file() {
  shell_name=$(basename "${SHELL:-}")
  case "$shell_name" in
    zsh) printf '%s/.zshrc\n' "$HOME" ;;
    bash) printf '%s/.bashrc\n' "$HOME" ;;
    *) printf '%s/.profile\n' "$HOME" ;;
  esac
}

remove_profile_block_from_file() {
  target_file=$1
  [ -f "$target_file" ] || return 0

  tmp_file=$(mktemp)
  awk -v start="$PROFILE_MARKER_START" -v end="$PROFILE_MARKER_END" '
    $0 == start { skip=1; next }
    $0 == end { skip=0; next }
    !skip { print }
  ' "$target_file" >"$tmp_file"
  mv "$tmp_file" "$target_file"
}

ensure_profile_block() {
  profile_file=$(pick_profile_file)
  mkdir -p "$(dirname "$profile_file")"
  [ -f "$profile_file" ] || : >"$profile_file"

  if grep -F "$PROFILE_MARKER_START" "$profile_file" >/dev/null 2>&1; then
    return 0
  fi

  log "Adding ${BIN_DIR} to PATH in ${profile_file}"
  {
    printf '\n%s\n' "$PROFILE_MARKER_START"
    printf '# agentflow installer\n'
    printf 'export PATH="%s:$PATH"\n' "$BIN_DIR"
    printf 'export AGENT_FLOW_DATA="%s"\n' "$DATA_DIR"
    printf '%s\n' "$PROFILE_MARKER_END"
  } >>"$profile_file"
}

remove_profile_block() {
  remove_profile_block_from_file "${HOME}/.profile"
  remove_profile_block_from_file "${HOME}/.bashrc"
  remove_profile_block_from_file "${HOME}/.zshrc"
}

sha256_file() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
    return
  fi
  if command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
    return
  fi
  if command -v openssl >/dev/null 2>&1; then
    openssl dgst -sha256 "$1" | awk '{print $NF}'
    return
  fi
  fail 'no sha256 tool available'
}

download_to_file() {
  url=$1
  destination=$2

  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$url" -o "$destination"
    return
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -qO "$destination" "$url"
    return
  fi
  fail 'curl or wget is required'
}

get_installed_version() {
  if [ ! -x "$BIN_PATH" ]; then
    return 0
  fi

  if version=$("$BIN_PATH" --version 2>/dev/null); then
    printf '%s\n' "$version" | awk 'NR == 1 { print; exit }'
  fi
}

fetch_metadata() {
  tmp_dir=$1
  metadata_path="${tmp_dir}/version.json"
  ensure_version_json_url
  printf '[agentflow] Fetching version metadata from %s\n' "$VERSION_JSON_URL" >&2
  download_to_file "$VERSION_JSON_URL" "$metadata_path"
  printf '%s\n' "$metadata_path"
}

compact_json() {
  tr -d '\n\r\t ' <"$1"
}

read_version_field() {
  metadata_compact=$1
  printf '%s' "$metadata_compact" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p'
}

read_artifact_field() {
  metadata_path=$1
  platform=$2
  arch=$3
  field=$4

  awk \
    -v target_platform="$platform" \
    -v target_arch="$arch" \
    -v target_field="$field" '
      function count_char(text, char,    idx, total) {
        total = 0
        for (idx = 1; idx <= length(text); idx++) {
          if (substr(text, idx, 1) == char) {
            total++
          }
        }
        return total
      }

      BEGIN {
        depth = 0
        in_platform = 0
        waiting_arch = 0
        in_arch = 0
        platform_depth = -1
        arch_depth = -1
      }

      {
        line = $0

        if (!in_platform && index(line, "\"" target_platform "\"") > 0) {
          in_platform = 1
          platform_depth = depth
        } else if (in_platform && !in_arch && index(line, "\"" target_arch "\"") > 0) {
          waiting_arch = 1
        }

        if (in_platform && waiting_arch && index(line, "{") > 0) {
          in_arch = 1
          waiting_arch = 0
          arch_depth = depth
        }

        if (in_arch && index(line, "\"" target_field "\"") > 0) {
          value = line
          sub("^.*\"" target_field "\":[[:space:]]*\"", "", value)
          sub("\".*$", "", value)
          print value
          exit
        }

        depth += count_char(line, "{")
        depth -= count_char(line, "}")

        if (in_arch && depth <= arch_depth) {
          in_arch = 0
        }
        if (in_platform && depth <= platform_depth) {
          in_platform = 0
          waiting_arch = 0
        }
      }
    ' "$metadata_path"
}

extract_artifact() {
  download_path=$1
  artifact_file=$2
  tmp_dir=$3

  case "$artifact_file" in
    *.tar.gz|*.tgz)
      require_command tar
      extract_dir="${tmp_dir}/extract"
      mkdir -p "$extract_dir"
      tar -xzf "$download_path" -C "$extract_dir"
      found_path=$(find "$extract_dir" -type f -name 'agentflow' | head -n 1)
      [ -n "$found_path" ] || fail "could not find agentflow binary inside ${artifact_file}"
      printf '%s\n' "$found_path"
      ;;
    *)
      printf '%s\n' "$download_path"
      ;;
  esac
}

show_status() {
  installed_version=$1
  remote_version=$2

  if [ -x "$BIN_PATH" ]; then
    log 'Installed: yes'
    log "Local version: ${installed_version:-unknown}"
  else
    log 'Installed: no'
  fi
  if [ -n "$remote_version" ]; then
    log "Remote version: ${remote_version}"
    if [ -x "$BIN_PATH" ] && [ "${installed_version:-}" = "$remote_version" ]; then
      log 'Update available: no'
    elif [ -x "$BIN_PATH" ]; then
      log 'Update available: yes'
    fi
  fi
  log "Install root: ${INSTALL_ROOT}"
  log "Binary: ${BIN_PATH}"
  log "Data dir: ${DATA_DIR}"
}

install_or_update() {
  metadata_path=$1
  platform=$2
  arch=$3
  installed_version=$4
  tmp_dir=$5

  metadata_compact=$(compact_json "$metadata_path")
  remote_version=$(read_version_field "$metadata_compact")
  [ -n "$remote_version" ] || fail 'version.json did not provide a version value.'

  artifact_file=$(read_artifact_field "$metadata_path" "$platform" "$arch" file)
  artifact_sha=$(read_artifact_field "$metadata_path" "$platform" "$arch" sha256)
  artifact_url=$(read_artifact_field "$metadata_path" "$platform" "$arch" url)

  [ -n "$artifact_file" ] || fail "version.json does not contain metadata for ${platform}/${arch}."
  [ -n "$artifact_sha" ] || fail "version.json entry for ${platform}/${arch} is missing sha256."
  [ -n "$artifact_url" ] || fail "version.json entry for ${platform}/${arch} is missing url."

  if [ -x "$BIN_PATH" ] && [ "$FORCE" -ne 1 ] && [ "${installed_version:-}" = "$remote_version" ]; then
    log "AgentFlow is already up to date (${installed_version})"
    return 0
  fi

  if [ -x "$BIN_PATH" ] && [ "$FORCE" -eq 1 ]; then
    log "Force reinstalling AgentFlow ${remote_version}"
  elif [ -x "$BIN_PATH" ]; then
    log "Updating AgentFlow from ${installed_version:-unknown} to ${remote_version}"
  else
    log "Installing AgentFlow ${remote_version}"
  fi

  mkdir -p "$tmp_dir" "$BIN_DIR" "$DATA_DIR"
  download_path="${tmp_dir}/${artifact_file}"
  log "Downloading ${artifact_url}"
  download_to_file "$artifact_url" "$download_path"

  actual_sha=$(sha256_file "$download_path")
  if [ "$actual_sha" != "$artifact_sha" ]; then
    fail "SHA256 mismatch for ${artifact_file}. Expected ${artifact_sha}, got ${actual_sha}."
  fi

  artifact_binary=$(extract_artifact "$download_path" "$artifact_file" "$tmp_dir")
  cp "$artifact_binary" "$BIN_PATH"
  chmod 755 "$BIN_PATH"
  ensure_profile_block

  log "Installed to ${BIN_PATH}"
  log 'Open a new terminal to pick up PATH changes.'
}

uninstall_agentflow() {
  if [ ! -e "$INSTALL_ROOT" ] && [ ! -x "$BIN_PATH" ]; then
    log 'AgentFlow is not installed.'
    return 0
  fi

  log 'Removing AgentFlow profile entries'
  remove_profile_block

  if [ -e "$BIN_PATH" ]; then
    rm -f "$BIN_PATH"
  fi
  if [ -d "$BIN_DIR" ]; then
    rmdir "$BIN_DIR" 2>/dev/null || true
  fi
  if [ "$REMOVE_DATA" -eq 1 ] && [ -d "$DATA_DIR" ]; then
    log "Removing data directory ${DATA_DIR}"
    rm -rf "$DATA_DIR"
  fi
  if [ -d "$INSTALL_ROOT" ]; then
    rmdir "$INSTALL_ROOT" 2>/dev/null || true
  fi

  log 'Uninstall complete.'
}

if [ "$CHECK" -eq 1 ] && [ "$UNINSTALL" -eq 1 ]; then
  fail 'Choose either --check or --uninstall, not both.'
fi

PLATFORM=$(detect_platform)
ARCH=$(detect_arch)
INSTALLED_VERSION=$(get_installed_version || true)

if [ "$UNINSTALL" -eq 1 ]; then
  uninstall_agentflow
  exit 0
fi

tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/agentflow-installer.XXXXXX")
trap 'rm -rf "$tmp_dir"' EXIT INT TERM

metadata_path=$(fetch_metadata "$tmp_dir")
metadata_compact=$(compact_json "$metadata_path")
remote_version=$(read_version_field "$metadata_compact")

if [ "$CHECK" -eq 1 ]; then
  show_status "$INSTALLED_VERSION" "$remote_version"
  exit 0
fi

install_or_update "$metadata_path" "$PLATFORM" "$ARCH" "$INSTALLED_VERSION" "$tmp_dir"
