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

AXON_INSTALL_BASE_URL="${AXON_INSTALL_BASE_URL:-https://axon-cli.monar.ai/cli/releases}"
AXON_INSTALL_BASE_URL="${AXON_INSTALL_BASE_URL%/}"
AXON_INSTALL_DIR="${AXON_INSTALL_DIR:-$HOME/.axon/bin}"
AXON_LINK_DIR="${AXON_LINK_DIR:-}"
AXON_VERSION="${AXON_VERSION:-latest}"
AXON_NO_PATH_UPDATE="${AXON_NO_PATH_UPDATE:-}"
AXON_SKILL_BASE_URL="${AXON_SKILL_BASE_URL:-https://axon-cli.monar.ai/skills}"
AXON_SKILL_BASE_URL="${AXON_SKILL_BASE_URL%/}"
AXON_NO_SKILL_INSTALL="${AXON_NO_SKILL_INSTALL:-}"
AXON_NO_CODEX_SKILL_INSTALL="${AXON_NO_CODEX_SKILL_INSTALL:-}"
AXON_NO_CLAUDE_SKILL_INSTALL="${AXON_NO_CLAUDE_SKILL_INSTALL:-}"
AXON_CODEX_HOME="${AXON_CODEX_HOME:-${CODEX_HOME:-$HOME/.codex}}"
AXON_CODEX_SKILLS_DIR="${AXON_CODEX_SKILLS_DIR:-$AXON_CODEX_HOME/skills}"
AXON_CLAUDE_HOME="${AXON_CLAUDE_HOME:-${CLAUDE_HOME:-$HOME/.claude}}"
AXON_CLAUDE_SKILLS_DIR="${AXON_CLAUDE_SKILLS_DIR:-$AXON_CLAUDE_HOME/skills}"
AXON_AGENT_SKILL_NAME="axon-cli"
REQUIRED_NODE_MAJOR=20

fail() {
  printf 'axon install: %s\n' "$*" >&2
  exit 1
}

has_command() {
  command -v "$1" >/dev/null 2>&1
}

download() {
  local url="$1"
  local output="$2"

  if has_command curl; then
    curl -fsSL "$url" -o "$output"
    return
  fi

  if has_command wget; then
    wget -qO "$output" "$url"
    return
  fi

  fail "curl or wget is required."
}

detect_target() {
  local os
  local arch

  os="$(uname -s)"
  arch="$(uname -m)"

  case "$os" in
    Darwin) os="darwin" ;;
    Linux) os="linux" ;;
    *) fail "unsupported operating system: $os" ;;
  esac

  case "$arch" in
    arm64|aarch64) arch="arm64" ;;
    x86_64|amd64) arch="x64" ;;
    *) fail "unsupported CPU architecture: $arch" ;;
  esac

  printf '%s-%s' "$os" "$arch"
}

ensure_node() {
  local major

  if ! has_command node; then
    fail "Node.js ${REQUIRED_NODE_MAJOR} or newer is required for this Axon CLI build."
  fi

  major="$(node -p 'Number(process.versions.node.split(".")[0])')"
  if [ "$major" -lt "$REQUIRED_NODE_MAJOR" ]; then
    fail "Node.js ${REQUIRED_NODE_MAJOR} or newer is required. Found $(node --version)."
  fi
}

read_latest_version() {
  local metadata_path="$1"
  sed -n 's/^[[:space:]]*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$metadata_path" | head -n 1
}

verify_checksum() {
  local artifact_path="$1"
  local checksums_path="$2"
  local artifact_name="$3"
  local expected
  local actual

  expected="$(awk -v file="$artifact_name" '$2 == file { print $1 }' "$checksums_path")"
  if [ -z "$expected" ]; then
    fail "checksum entry missing for $artifact_name."
  fi

  if has_command sha256sum; then
    actual="$(sha256sum "$artifact_path" | awk '{ print $1 }')"
  elif has_command shasum; then
    actual="$(shasum -a 256 "$artifact_path" | awk '{ print $1 }')"
  else
    fail "sha256sum or shasum is required to verify the download."
  fi

  if [ "$actual" != "$expected" ]; then
    fail "checksum mismatch for $artifact_name."
  fi
}

path_has_dir() {
  local dir="$1"

  case ":$PATH:" in
    *":$dir:"*) return 0 ;;
    *) return 1 ;;
  esac
}

install_path_link() {
  local installed_binary="$1"
  local candidate
  local link_path
  local candidates=()

  if [ -n "$AXON_NO_PATH_UPDATE" ]; then
    return 1
  fi

  if path_has_dir "$AXON_INSTALL_DIR"; then
    return 0
  fi

  if [ -n "$AXON_LINK_DIR" ]; then
    candidates+=("$AXON_LINK_DIR")
  fi
  candidates+=("/usr/local/bin" "/opt/homebrew/bin" "$HOME/.local/bin" "$HOME/bin")

  for candidate in "${candidates[@]}"; do
    if [ -z "$candidate" ] || ! path_has_dir "$candidate"; then
      continue
    fi

    if [ ! -d "$candidate" ] && ! mkdir -p "$candidate" 2>/dev/null; then
      continue
    fi

    if [ ! -w "$candidate" ]; then
      continue
    fi

    link_path="$candidate/axon"
    if [ -e "$link_path" ] || [ -L "$link_path" ]; then
      if [ ! -L "$link_path" ] || [ "$(readlink "$link_path")" != "$installed_binary" ]; then
        continue
      fi
    fi

    if ln -sf "$installed_binary" "$link_path" 2>/dev/null; then
      printf 'Linked axon at %s for immediate use.\n' "$link_path"
      return 0
    fi
  done

  return 1
}

install_path_update() {
  local shell_name
  local rc_file
  local path_line

  if [ -n "$AXON_NO_PATH_UPDATE" ]; then
    return
  fi

  if path_has_dir "$AXON_INSTALL_DIR"; then
    return
  fi

  shell_name="$(basename "${SHELL:-}")"
  case "$shell_name" in
    zsh) rc_file="$HOME/.zshrc" ;;
    bash) rc_file="$HOME/.bashrc" ;;
    *) rc_file="" ;;
  esac

  if [ -z "$rc_file" ]; then
    printf 'Add %s to PATH to run axon from a new shell.\n' "$AXON_INSTALL_DIR"
    return
  fi

  path_line="export PATH=\"$AXON_INSTALL_DIR:\$PATH\""
  if [ -f "$rc_file" ] && grep -F "$path_line" "$rc_file" >/dev/null 2>&1; then
    return
  fi

  {
    printf '\n# Axon CLI\n'
    printf '%s\n' "$path_line"
  } >>"$rc_file"

  printf 'Updated PATH in %s. Restart your shell or run: %s\n' "$rc_file" "$path_line"
}

install_agent_skill_copy() {
  local source_dir="$1"
  local skills_dir="$2"
  local label="$3"
  local target_dir="$skills_dir/$AXON_AGENT_SKILL_NAME"

  mkdir -p "$skills_dir"
  rm -rf "$target_dir"
  cp -R "$source_dir" "$target_dir"

  printf 'Axon %s skill installed at %s\n' "$label" "$target_dir"
}

install_agent_skills() {
  local tmp_dir="$1"
  local artifact_name="$AXON_AGENT_SKILL_NAME.tar.gz"
  local extract_dir="$tmp_dir/skill-extract"
  local source_dir="$extract_dir/$AXON_AGENT_SKILL_NAME"

  if [ -n "$AXON_NO_SKILL_INSTALL" ]; then
    return
  fi

  mkdir -p "$extract_dir"
  download "$AXON_SKILL_BASE_URL/$artifact_name" "$tmp_dir/$artifact_name"
  download "$AXON_SKILL_BASE_URL/SHASUMS256.txt" "$tmp_dir/skills-SHASUMS256.txt"
  verify_checksum "$tmp_dir/$artifact_name" "$tmp_dir/skills-SHASUMS256.txt" "$artifact_name"
  tar -xzf "$tmp_dir/$artifact_name" -C "$extract_dir"

  if [ ! -f "$source_dir/SKILL.md" ]; then
    fail "downloaded Axon agent skill is missing SKILL.md."
  fi

  if [ -z "$AXON_NO_CODEX_SKILL_INSTALL" ]; then
    install_agent_skill_copy "$source_dir" "$AXON_CODEX_SKILLS_DIR" "Codex"
  fi

  if [ -z "$AXON_NO_CLAUDE_SKILL_INSTALL" ]; then
    install_agent_skill_copy "$source_dir" "$AXON_CLAUDE_SKILLS_DIR" "Claude Code"
  fi
}

target="$(detect_target)"
ensure_node

tmp_dir="$(mktemp -d 2>/dev/null || mktemp -d -t axon-install)"
trap 'rm -rf "$tmp_dir"' EXIT

version="$AXON_VERSION"
if [ "$version" = "latest" ]; then
  download "$AXON_INSTALL_BASE_URL/latest.json" "$tmp_dir/latest.json"
  version="$(read_latest_version "$tmp_dir/latest.json")"
  if [ -z "$version" ]; then
    fail "could not read latest Axon CLI version."
  fi
fi

artifact_name="axon-${version}-${target}.tar.gz"
download "$AXON_INSTALL_BASE_URL/artifacts/$artifact_name" "$tmp_dir/$artifact_name"
download "$AXON_INSTALL_BASE_URL/SHASUMS256.txt" "$tmp_dir/SHASUMS256.txt"
verify_checksum "$tmp_dir/$artifact_name" "$tmp_dir/SHASUMS256.txt" "$artifact_name"

mkdir -p "$tmp_dir/extract" "$AXON_INSTALL_DIR"
tar -xzf "$tmp_dir/$artifact_name" -C "$tmp_dir/extract"

if has_command install; then
  install -m 755 "$tmp_dir/extract/axon" "$AXON_INSTALL_DIR/axon"
else
  cp "$tmp_dir/extract/axon" "$AXON_INSTALL_DIR/axon"
  chmod 755 "$AXON_INSTALL_DIR/axon"
fi

if ! install_path_link "$AXON_INSTALL_DIR/axon"; then
  install_path_update
fi

install_agent_skills "$tmp_dir"

printf 'Axon CLI %s installed at %s/axon\n' "$version" "$AXON_INSTALL_DIR"
printf 'Production defaults are built in. Override with AXON_MONAR_CONVEX_URL and AXON_WEB_BASE_URL, or ~/.axon/config.json.\n'
printf 'Next: run axon login, then axon whoami.\n'
printf 'Restart Codex or Claude Code to pick up the axon-cli skill.\n'
