#!/usr/bin/env bash
set -euo pipefail

DEFAULT_BASE_URL="https://static.pingcap.com"
DEFAULT_REPO="agentteam/client"
DEFAULT_BINARY="agentteam"
DEFAULT_INSTALL_DIR="${HOME}/.agentteam/bin"
DEFAULT_STABLE_VERSION_URL="https://static.pingcap.com/agentteam/releases/desktop/stable.json"
LEGACY_STABLE_VERSION_URL="https://static.pingcap.com/agentteam/releases/daemon/stable.json"
SHELL_PATH_SNIPPET_HEADER="# Added by AgentTeam CLI"

fail() {
  echo "$*" >&2
  exit 1
}

log() {
  echo "$*" >&2
}

usage() {
  cat <<'EOF_USAGE'
Install the Linux AgentTeam daemon/CLI and its managed builtin runtime from release assets.

Usage:
  install.sh [--version <vX.Y.Z>] [--stable-version-url <url>] [--base-url <url>] [--repo <owner/repo>] [--archive-url <url>] [--checksums-url <url>] [--install-dir <dir>] [--binary <name>] [--install-url-handler] [--no-modify-path]

Examples:
  curl -fsSL https://static.pingcap.com/agentteam/client/install.sh | bash
  curl -fsSL https://static.pingcap.com/agentteam/client/install.sh | bash -s -- --version v0.2.0
  curl -fsSL https://static.pingcap.com/agentteam/client/install.sh | bash -s -- --install-url-handler
  bash install.sh --install-dir "$HOME/.local/bin"

The optional Linux URL handler registers agentteam:// links as:
  agentteam connect <url>
It does not install or open a local Dashboard.
EOF_USAGE
}

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

normalize_version_tag() {
  local raw="$1"
  raw="${raw#v}"
  if [[ -z "$raw" ]]; then
    fail "version is empty"
  fi
  printf 'v%s\n' "$raw"
}

stable_version_is_newer() {
  local candidate="${1#v}"
  local current="${2#v}"
  [[ "${candidate}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
  [[ "${current}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 0
  local candidate_major candidate_minor candidate_patch
  local current_major current_minor current_patch
  IFS=. read -r candidate_major candidate_minor candidate_patch <<<"${candidate}"
  IFS=. read -r current_major current_minor current_patch <<<"${current}"
  candidate_major=$((10#${candidate_major}))
  candidate_minor=$((10#${candidate_minor}))
  candidate_patch=$((10#${candidate_patch}))
  current_major=$((10#${current_major}))
  current_minor=$((10#${current_minor}))
  current_patch=$((10#${current_patch}))
  ((candidate_major > current_major)) && return 0
  ((candidate_major < current_major)) && return 1
  ((candidate_minor > current_minor)) && return 0
  ((candidate_minor < current_minor)) && return 1
  ((candidate_patch > current_patch))
}

resolve_stable_version() {
  local stable_version_url="$1"
  local fallback_stable_version_url="$2"
  local response
  local version
  local fallback_version

  response="$(curl -fsSL --max-time 30 "$stable_version_url")" || response=""
  version="$(printf '%s\n' "$response" | awk -F'"' '/"current_version"[[:space:]]*:/ {print $4; exit}')"
  if [[ -n "${fallback_stable_version_url}" ]]; then
    response="$(curl -fsSL --max-time 30 "$fallback_stable_version_url")" || response=""
    fallback_version="$(printf '%s\n' "$response" | awk -F'"' '/"current_version"[[:space:]]*:/ {print $4; exit}')"
    if [[ -z "${version}" ]] || stable_version_is_newer "${fallback_version}" "${version}"; then
      version="${fallback_version}"
    fi
  fi
  [[ -n "${version}" ]] ||
    fail "failed to fetch stable version metadata from ${stable_version_url}; please retry or pass --version explicitly"
  printf '%s\n' "${version}"
}

detect_os() {
  local uname_s
  uname_s="$(uname -s)"
  case "$uname_s" in
    Linux) printf 'linux\n' ;;
    *)
      fail "this installer supports Linux only; install the AgentTeam application package on macOS or Windows (current system: ${uname_s})"
      ;;
  esac
}

detect_arch() {
  local uname_m
  uname_m="$(uname -m)"
  case "$uname_m" in
    x86_64|amd64) printf 'amd64\n' ;;
    arm64|aarch64) printf 'arm64\n' ;;
    *)
      fail "this installer supports amd64 and arm64 only; current architecture is ${uname_m}"
      ;;
  esac
}

validate_platform_support() {
  local os_name="$1"
  local arch_name="$2"
  case "${os_name}_${arch_name}" in
    linux_amd64|linux_arm64)
      return 0
      ;;
    *)
      fail "agentteam does not support ${os_name}/${arch_name} yet."
      ;;
  esac
}

desktop_entry_escape_exec_arg() {
  local value="$1"
  value="${value//\\/\\\\}"
  value="${value//\"/\\\"}"
  value="${value//\\`/\\\\`}"
  value="${value//\\$/\\\\$}"
  printf '%s' "$value"
}

install_linux_url_handler() {
  local binary_path="$1"
  local data_home="${XDG_DATA_HOME:-${HOME}/.local/share}"
  local applications_dir="${data_home}/applications"
  local handler_path="${applications_dir}/agentteam-url-handler.desktop"
  local escaped_binary

  require_cmd xdg-mime

  escaped_binary="$(desktop_entry_escape_exec_arg "$binary_path")"
  mkdir -p "$applications_dir" || \
    fail "failed to create desktop applications directory: ${applications_dir}"
  cat >"$handler_path" <<EOF_HANDLER || \
    fail "failed to install AgentTeam URL handler: ${handler_path}"
[Desktop Entry]
Type=Application
Name=AgentTeam URL Handler
Comment=Connect this machine to AgentTeam
NoDisplay=true
Terminal=false
Exec="${escaped_binary}" connect %u
MimeType=x-scheme-handler/agentteam;
Categories=Network;
EOF_HANDLER
  [[ -f "$handler_path" ]] || \
    fail "failed to install AgentTeam URL handler: ${handler_path}"
  chmod 0644 "$handler_path" || \
    fail "failed to set AgentTeam URL handler permissions: ${handler_path}"
  xdg-mime default "$(basename "$handler_path")" x-scheme-handler/agentteam || \
    fail "failed to register x-scheme-handler/agentteam"
  log "registered agentteam:// links -> ${binary_path} connect"
}

verify_checksum() {
  local work_dir="$1"
  local checksum_file="$2"
  if command -v shasum >/dev/null 2>&1; then
    (cd "$work_dir" && shasum -a 256 -c "$checksum_file" >/dev/null 2>&1) || return 1
    return 0
  fi
  if command -v sha256sum >/dev/null 2>&1; then
    (cd "$work_dir" && sha256sum -c "$checksum_file" >/dev/null 2>&1) || return 1
    return 0
  fi
  fail "no checksum tool found (need shasum or sha256sum)"
}

preferred_shell_profile() {
  local shell_name
  local bash_profile="${HOME}/.bashrc"
  local zsh_profile="${HOME}/.zshrc"
  local fish_profile="${HOME}/.config/fish/config.fish"
  shell_name="$(basename "${SHELL:-}")"
  case "$shell_name" in
    bash)
      printf '%s\n' "$bash_profile"
      ;;
    zsh)
      printf '%s\n' "$zsh_profile"
      ;;
    fish)
      printf '%s\n' "$fish_profile"
      ;;
    *)
      if [[ -f "$bash_profile" && ! -f "$zsh_profile" ]]; then
        printf '%s\n' "$bash_profile"
      elif [[ -f "$zsh_profile" && ! -f "$bash_profile" ]]; then
        printf '%s\n' "$zsh_profile"
      elif [[ -f "$fish_profile" && ! -f "$bash_profile" && ! -f "$zsh_profile" ]]; then
        printf '%s\n' "$fish_profile"
      elif [[ "$(uname -s)" == "Linux" ]]; then
        printf '%s\n' "$bash_profile"
      else
        printf '%s\n' "$zsh_profile"
      fi
      ;;
  esac
}

escape_double_quoted() {
  local value="$1"
  value="${value//\\/\\\\}"
  value="${value//\"/\\\"}"
  printf '%s' "$value"
}

shell_profile_kind() {
  local profile_path="$1"
  case "$profile_path" in
    */config.fish)
      printf 'fish\n'
      ;;
    *)
      printf 'posix\n'
      ;;
  esac
}

emit_shell_path_snippet() {
  local install_dir="$1"
  local profile_path="$2"
  local escaped_dir

  escaped_dir="$(escape_double_quoted "$install_dir")"
  printf '%s\n' "$SHELL_PATH_SNIPPET_HEADER"
  case "$(shell_profile_kind "$profile_path")" in
    fish)
      printf 'if not contains "%s" $PATH\n' "$escaped_dir"
      printf '    set -gx PATH "%s" $PATH\n' "$escaped_dir"
      printf 'end\n'
      ;;
    *)
      printf 'export PATH="%s:$PATH"\n' "$escaped_dir"
      ;;
  esac
}

log_shell_path_snippet() {
  local install_dir="$1"
  local profile_path="$2"

  emit_shell_path_snippet "$install_dir" "$profile_path" | while IFS= read -r line; do
    log "  ${line}"
  done
}

ensure_shell_path() {
  local install_dir="$1"
  local profile_path="$2"
  local snippet

  [[ -n "$profile_path" ]] || return 0

  if [[ -f "$profile_path" ]] && grep -Fq "$install_dir" "$profile_path"; then
    return 0
  fi

  mkdir -p "$(dirname "$profile_path")" || return 1
  if [[ -f "$profile_path" && -s "$profile_path" ]]; then
    printf '\n' >>"$profile_path" || return 1
  fi
  snippet="$(emit_shell_path_snippet "$install_dir" "$profile_path")"
  printf '%s\n' "$snippet" >>"$profile_path" || return 1
}

try_ensure_shell_path() {
  local install_dir="$1"
  local profile_path="$2"

  if ensure_shell_path "$install_dir" "$profile_path"; then
    return 0
  fi

  log "warning: failed to update shell profile ${profile_path}"
  log "add PATH manually to ${profile_path}:"
  log_shell_path_snippet "$install_dir" "$profile_path"
  return 0
}

detect_command_path() {
  local name="$1"
  command -v "$name" 2>/dev/null || true
}

find_extracted_runtimes() {
  local root="$1"
  if [[ -d "${root}/runtimes" ]]; then
    printf '%s\n' "${root}/runtimes"
    return 0
  fi
  find "$root" -type d -name runtimes -print -quit
}

install_extracted_runtimes() {
  local source_dir="$1"
  local install_dir="$2"
  local use_sudo="$3"

  [[ -n "$source_dir" && -d "$source_dir" ]] || return 0

  local runtime_target="${install_dir}/runtimes"
  local stage_target="${install_dir}/.runtimes.install.$$"
  local backup_target=""

  if [[ "$use_sudo" == "1" ]]; then
    sudo rm -rf "$stage_target"
    sudo cp -R "$source_dir" "$stage_target" || \
      { echo "failed to stage runtimes for ${runtime_target} with sudo" >&2; return 1; }
    if [[ -e "$runtime_target" ]]; then
      backup_target="${install_dir}/.runtimes.previous.$$"
      sudo rm -rf "$backup_target"
      sudo mv "$runtime_target" "$backup_target" || \
        { echo "failed to back up existing runtimes ${runtime_target} with sudo" >&2; return 1; }
    fi
    if ! sudo mv "$stage_target" "$runtime_target"; then
      if [[ -n "$backup_target" && -e "$backup_target" ]]; then
        sudo mv "$backup_target" "$runtime_target" || true
      fi
      sudo rm -rf "$stage_target"
      echo "failed to install runtimes to ${runtime_target} with sudo" >&2
      return 1
    fi
    [[ -z "$backup_target" ]] || sudo rm -rf "$backup_target"
    return 0
  fi

  rm -rf "$stage_target"
  cp -R "$source_dir" "$stage_target" || \
    { echo "failed to stage runtimes for ${runtime_target}" >&2; return 1; }
  if [[ -e "$runtime_target" ]]; then
    backup_target="${install_dir}/.runtimes.previous.$$"
    rm -rf "$backup_target"
    mv "$runtime_target" "$backup_target" || \
      { echo "failed to back up existing runtimes ${runtime_target}" >&2; return 1; }
  fi
  if ! mv "$stage_target" "$runtime_target"; then
    if [[ -n "$backup_target" && -e "$backup_target" ]]; then
      mv "$backup_target" "$runtime_target" || true
    fi
    rm -rf "$stage_target"
    echo "failed to install runtimes to ${runtime_target}" >&2
    return 1
  fi
  [[ -z "$backup_target" ]] || rm -rf "$backup_target"
}

version_input=""
stable_version_url="$DEFAULT_STABLE_VERSION_URL"
fallback_stable_version_url="$LEGACY_STABLE_VERSION_URL"
base_url="$DEFAULT_BASE_URL"
repo="$DEFAULT_REPO"
binary="$DEFAULT_BINARY"
install_dir="$DEFAULT_INSTALL_DIR"
modify_path="1"
install_url_handler="0"
archive_url=""
checksums_url=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --version|-v)
      [[ $# -ge 2 ]] || fail "--version requires a value"
      version_input="$2"
      shift 2
      ;;
    --stable-version-url)
      [[ $# -ge 2 ]] || fail "--stable-version-url requires a value"
      stable_version_url="$2"
      fallback_stable_version_url=""
      shift 2
      ;;
    --base-url)
      [[ $# -ge 2 ]] || fail "--base-url requires a value"
      base_url="$2"
      shift 2
      ;;
    --repo)
      [[ $# -ge 2 ]] || fail "--repo requires a value"
      repo="$2"
      shift 2
      ;;
    --archive-url)
      [[ $# -ge 2 ]] || fail "--archive-url requires a value"
      archive_url="$2"
      shift 2
      ;;
    --checksums-url)
      [[ $# -ge 2 ]] || fail "--checksums-url requires a value"
      checksums_url="$2"
      shift 2
      ;;
    --binary)
      [[ $# -ge 2 ]] || fail "--binary requires a value"
      binary="$2"
      shift 2
      ;;
    --install-dir)
      [[ $# -ge 2 ]] || fail "--install-dir requires a value"
      install_dir="$2"
      shift 2
      ;;
    --no-modify-path)
      modify_path="0"
      shift
      ;;
    --install-url-handler)
      install_url_handler="1"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      fail "unknown argument: $1"
      ;;
  esac
done

[[ -n "${base_url// }" ]] || fail "base url is empty"
[[ -n "${repo// }" ]] || fail "repo is empty"
[[ -n "${stable_version_url// }" ]] || fail "stable version url is empty"

os_name="$(detect_os)"
arch_name="$(detect_arch)"
validate_platform_support "$os_name" "$arch_name"

require_cmd curl
require_cmd tar
require_cmd install
if [[ -z "$version_input" ]]; then
  version_input="$(resolve_stable_version "$stable_version_url" "$fallback_stable_version_url")"
fi
version_tag="$(normalize_version_tag "$version_input")"
version_number="${version_tag#v}"

archive_name="${binary}_${version_number}_${os_name}_${arch_name}.tar.gz"
checksums_name="${binary}_${version_number}_checksums.txt"

trimmed_base_url="${base_url%/}"
trimmed_repo="${repo#/}"
release_base_url="${trimmed_base_url}/${trimmed_repo}/${version_tag}"

if [[ -n "${archive_url// }" ]]; then
  archive_name="$(basename "${archive_url%%\?*}")"
fi
if [[ -n "${checksums_url// }" ]]; then
  checksums_name="$(basename "${checksums_url%%\?*}")"
fi

log "installing ${binary} ${version_tag} for ${os_name}/${arch_name}"

tmp_dir="$(mktemp -d)"
cleanup() {
  rm -rf "$tmp_dir"
}
trap cleanup EXIT

path_dir="${install_dir}"
path_hint_needed="0"
profile_path=""
case ":${PATH:-}:" in
  *":${path_dir}:"*)
    ;;
  *)
    path_hint_needed="1"
    if [[ "$modify_path" == "1" ]]; then
      profile_path="$(preferred_shell_profile)"
    fi
    ;;
esac

archive_path="${tmp_dir}/${archive_name}"
checksums_path="${tmp_dir}/${checksums_name}"
archive_checksum_path="${tmp_dir}/${archive_name}.sha256"

archive_source_url="${archive_url:-${release_base_url}/${archive_name}}"
checksums_source_url="${checksums_url:-${release_base_url}/${checksums_name}}"

curl -fsSL -o "$archive_path" "${archive_source_url}" || \
  fail "failed to download archive from ${archive_source_url}"
curl -fsSL -o "$checksums_path" "${checksums_source_url}" || \
  fail "failed to download checksums from ${checksums_source_url}"

checksum_line="$(awk -v file="$archive_name" 'NF >= 2 && $NF == file {print $1 "  " file}' "$checksums_path")"
[[ -n "$checksum_line" ]] || \
  fail "checksum metadata for ${version_tag} is missing ${archive_name}; please retry later or contact the release owner"
printf '%s\n' "$checksum_line" >"$archive_checksum_path"

verify_checksum "$tmp_dir" "$(basename "$archive_checksum_path")" || \
  fail "checksum validation failed for ${archive_name}"

tar -xzf "$archive_path" -C "$tmp_dir" || fail "failed to extract archive: ${archive_name}"

extracted_binary="${tmp_dir}/${binary}"
if [[ ! -f "$extracted_binary" ]]; then
  extracted_binary="$(find "$tmp_dir" -type f -name "$binary" -print -quit)"
fi
[[ -n "$extracted_binary" && -f "$extracted_binary" ]] || \
  fail "binary ${binary} not found in extracted archive ${archive_name}"
extracted_runtimes="$(find_extracted_runtimes "$tmp_dir")"

mkdir -p "$install_dir" || fail "failed to create install directory: ${install_dir}"

install_target="${install_dir}/${binary}"
stage_target="${install_dir}/.${binary}.install.$$"
backup_target=""
if [[ -w "$install_dir" ]]; then
  install -m 0755 "$extracted_binary" "$stage_target" || \
    fail "failed to stage binary for ${install_target}"
  if [[ -e "$install_target" ]]; then
    backup_target="$(mktemp "${install_dir}/.${binary}.previous.XXXXXX")" || \
      fail "failed to create backup for ${install_target}"
    mv "$install_target" "$backup_target" || \
      fail "failed to back up existing binary ${install_target}"
  fi
  if ! mv "$stage_target" "$install_target"; then
    if [[ -n "$backup_target" && -e "$backup_target" ]]; then
      mv "$backup_target" "$install_target" || true
    fi
    rm -f "$stage_target"
    fail "failed to install binary to ${install_target}"
  fi
  if ! install_extracted_runtimes "$extracted_runtimes" "$install_dir" "0"; then
    rm -f "$install_target"
    if [[ -n "$backup_target" && -e "$backup_target" ]]; then
      mv "$backup_target" "$install_target" || true
    fi
    fail "failed to install bundled runtimes for ${install_target}"
  fi
  rm -f "$backup_target"
else
  require_cmd sudo
  sudo install -m 0755 "$extracted_binary" "$stage_target" || \
    fail "failed to stage binary for ${install_target} with sudo"
  if [[ -e "$install_target" ]]; then
    backup_target="${install_dir}/.${binary}.previous.$$"
    sudo mv "$install_target" "$backup_target" || \
      fail "failed to back up existing binary ${install_target} with sudo"
  fi
  if ! sudo mv "$stage_target" "$install_target"; then
    if [[ -n "$backup_target" && -e "$backup_target" ]]; then
      sudo mv "$backup_target" "$install_target" || true
    fi
    sudo rm -f "$stage_target"
    fail "failed to install binary to ${install_target} with sudo"
  fi
  if ! install_extracted_runtimes "$extracted_runtimes" "$install_dir" "1"; then
    sudo rm -f "$install_target"
    if [[ -n "$backup_target" && -e "$backup_target" ]]; then
      sudo mv "$backup_target" "$install_target" || true
    fi
    fail "failed to install bundled runtimes for ${install_target}"
  fi
  [[ -z "$backup_target" ]] || sudo rm -f "$backup_target"
fi

log "installation succeeded: ${binary} ${version_tag} -> ${install_target}"

if [[ "$install_url_handler" == "1" ]]; then
  install_linux_url_handler "$install_target"
fi

resolved_binary_path="$(detect_command_path "${binary}")"
if [[ -n "$resolved_binary_path" && "$resolved_binary_path" != "$install_target" ]]; then
  log "warning: current shell resolves '${binary}' to ${resolved_binary_path}, not ${install_target}"
  if [[ -n "$profile_path" ]]; then
    log "run 'source ${profile_path}' or open a new terminal window, then run 'hash -r'"
  else
    log "open a new terminal window or update PATH, then run 'hash -r'"
  fi
  log "verify the new binary directly with:"
  log "  ${install_target} version"
fi

if [[ "$path_hint_needed" == "1" ]]; then
  hint_profile_path="$profile_path"
  if [[ -z "$hint_profile_path" ]]; then
    hint_profile_path="$(preferred_shell_profile)"
  fi

  if [[ "$modify_path" == "1" ]]; then
    try_ensure_shell_path "$install_dir" "$profile_path"
    if [[ -n "$profile_path" ]]; then
      if [[ -f "$profile_path" ]] && grep -Fq "$install_dir" "$profile_path"; then
        log "updated shell profile: ${profile_path}"
      fi
      log "run 'source ${profile_path}' or open a new terminal before running '${binary}' directly"
    fi
  fi
  log "note: ${install_dir} is not currently in PATH"
  log "add this to your shell profile if you want to run '${binary}' directly:"
  log_shell_path_snippet "$install_dir" "$hint_profile_path"
fi
