#!/usr/bin/env bash
# Teletraan Framework — single smart installer.
# Run: ./install.sh
#
# Flow:
#   1. Install prerequisites (Homebrew, Node 22+, Python 3.12+, PM2)
#   2. Install Claude Code CLI if missing; resolve its absolute path
#   3. Verify Claude Code is authenticated (loop until confirmed)
#   4. Launch the conversational agent installer (agent-installer/start.sh)
#
# Flags:
#   --prereqs-only   Install prerequisites only, then exit (useful for testing)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NODE_MIN_MAJOR=22
PYTHON_MIN_MINOR=12  # 3.12+

PREREQS_ONLY=0

for arg in "$@"; do
  case "$arg" in
    --prereqs-only) PREREQS_ONLY=1 ;;
  esac
done

# ─── Helpers ──────────────────────────────────────────────────────────────────

info() { echo "  [info]  $*"; }
ok()   { echo "  [ ok ]  $*"; }
step() { echo; echo "==> $*"; }
die()  { echo; echo "  [fail]  $*" >&2; exit 1; }

# ─── Step 0: Verify sudo / admin access ───────────────────────────────────────

step "Checking administrator (sudo) access..."

_require_sudo() {
  if sudo -n true 2>/dev/null; then
    ok "sudo available (no password required)."
    sudo -v
    return
  fi

  # Non-interactive sudo failed — check whether the user is in the macOS admin
  # group (admin group membership = can sudo with a password on macOS).
  if ! groups "$USER" | tr ' ' '\n' | grep -qx 'admin'; then
    echo
    echo "  [fail]  Your account does not have Administrator privileges." >&2
    echo "          To fix this, ask an admin to run:" >&2
    echo "            sudo dseditgroup -o edit -a ${USER} -t user admin" >&2
    echo "          Or go to System Settings > Users & Groups and enable" >&2
    echo "          Administrator for your account." >&2
    echo "          Then re-run this installer." >&2
    exit 1
  fi

  echo 'This installer needs administrator access to set up system packages.'
  echo 'You may be prompted for your password.'
  sudo -v || die "sudo authentication failed. Please try again."
  ok "Administrator access confirmed."
}

_require_sudo

# ─── Step 1: Homebrew ─────────────────────────────────────────────────────────

step "Checking Homebrew..."

_setup_brew_path() {
  if [ -f /opt/homebrew/bin/brew ]; then
    eval "$(/opt/homebrew/bin/brew shellenv)"
  elif [ -f /usr/local/bin/brew ]; then
    eval "$(/usr/local/bin/brew shellenv)"
  fi
}

if command -v brew &>/dev/null; then
  ok "Homebrew already installed ($(brew --version | head -1))"
else
  info "Homebrew not found — installing..."
  NONINTERACTIVE=1 /bin/bash -c \
    "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \
    || die "Homebrew installation failed. Visit https://brew.sh to install manually."
  _setup_brew_path
  command -v brew &>/dev/null \
    || die "Homebrew installed but 'brew' is still not in PATH. Restart your shell and re-run."
  ok "Homebrew installed ($(brew --version | head -1))"
fi

# Ensure brew env is loaded even if brew was already present but not in PATH.
_setup_brew_path

# ─── Step 2: Node.js 22+ ──────────────────────────────────────────────────────

step "Checking Node.js ${NODE_MIN_MAJOR}+..."

_node_major() {
  node --version 2>/dev/null | sed 's/^v//' | cut -d. -f1
}

_check_node() {
  command -v node &>/dev/null || return 1
  local major
  major=$(_node_major)
  [ "${major:-0}" -ge "$NODE_MIN_MAJOR" ]
}

if _check_node; then
  ok "Node.js $(_node_major) already installed"
else
  info "Node.js ${NODE_MIN_MAJOR}+ not found — installing via Homebrew..."
  brew install "node@${NODE_MIN_MAJOR}" \
    || die "Failed to install node@${NODE_MIN_MAJOR} via Homebrew."
  brew link --overwrite "node@${NODE_MIN_MAJOR}" 2>/dev/null || true
  # Some Homebrew setups need shellenv refreshed after a new keg-only formula.
  _setup_brew_path
  # If still not found, try the keg-only path directly.
  if ! command -v node &>/dev/null; then
    KEG_BIN="$(brew --prefix "node@${NODE_MIN_MAJOR}")/bin"
    export PATH="$KEG_BIN:$PATH"
  fi
  _check_node \
    || die "Node.js installation appeared to succeed but 'node --version' is still < ${NODE_MIN_MAJOR}. Run: brew link --overwrite node@${NODE_MIN_MAJOR}"
  ok "Node.js $(_node_major) installed"
fi

# ─── Step 3: Python 3.12+ ─────────────────────────────────────────────────────

step "Checking Python 3.12+..."

_python_minor() {
  # brew installs as python3.12, not python3, on macOS — prefer the versioned binary
  if command -v python3.12 &>/dev/null; then
    python3.12 --version 2>/dev/null | awk '{print $2}' | cut -d. -f2
  else
    python3 --version 2>/dev/null | awk '{print $2}' | cut -d. -f2
  fi
}

_check_python() {
  local minor
  # Check python3.12 directly first (brew keg-only install)
  if command -v python3.12 &>/dev/null; then
    minor=$(python3.12 --version 2>/dev/null | awk '{print $2}' | cut -d. -f2)
    [ "${minor:-0}" -ge "$PYTHON_MIN_MINOR" ] && return 0
  fi
  # Fall back to python3
  command -v python3 &>/dev/null || return 1
  minor=$(python3 --version 2>/dev/null | awk '{print $2}' | cut -d. -f2)
  [ "${minor:-0}" -ge "$PYTHON_MIN_MINOR" ]
}

if _check_python; then
  ok "Python 3.$(_python_minor) already installed"
else
  info "Python 3.${PYTHON_MIN_MINOR}+ not found — installing via Homebrew..."
  brew install "python@3.${PYTHON_MIN_MINOR}" \
    || die "Failed to install python@3.${PYTHON_MIN_MINOR} via Homebrew."
  _setup_brew_path
  # brew installs python3.12 as a keg-only formula; add libexec/bin so python3 resolves to it
  PYTHON_LIBEXEC="$(brew --prefix "python@3.${PYTHON_MIN_MINOR}" 2>/dev/null)/libexec/bin"
  if [ -d "$PYTHON_LIBEXEC" ]; then
    export PATH="$PYTHON_LIBEXEC:$PATH"
  fi
  _check_python \
    || die "Python installation appeared to succeed but version is still < 3.${PYTHON_MIN_MINOR}."
  ok "Python 3.$(_python_minor) installed"
fi

# ─── Step 4: PM2 ──────────────────────────────────────────────────────────────

step "Checking PM2..."

if command -v pm2 &>/dev/null; then
  ok "PM2 already installed ($(pm2 --version 2>/dev/null || echo 'unknown version'))"
else
  info "PM2 not found — installing via npm..."
  npm install -g pm2 \
    || die "Failed to install PM2 globally. Check npm permissions."
  command -v pm2 &>/dev/null \
    || die "PM2 installed but not found in PATH. You may need to restart your shell."
  ok "PM2 installed ($(pm2 --version 2>/dev/null || echo 'installed'))"
fi

# ─── Step 5: Claude Code CLI ──────────────────────────────────────────────────

step "Checking Claude Code CLI..."

if command -v claude &>/dev/null; then
  ok "Claude Code CLI already installed ($(claude --version 2>/dev/null || echo 'unknown version'))"
else
  info "Claude Code CLI not found — installing..."
  curl -fsSL https://claude.ai/install.sh | bash \
    || die "Failed to install Claude Code CLI. Visit https://claude.ai to install manually."
  # Reload shell config so the new binary is discoverable in PATH.
  [ -f "$HOME/.bashrc" ] && source "$HOME/.bashrc" 2>/dev/null || true
  [ -f "$HOME/.zshrc" ]  && source "$HOME/.zshrc"  2>/dev/null || true
  # If still not in PATH, check the default install location and add it.
  if ! command -v claude &>/dev/null; then
    if [ -x "$HOME/.local/bin/claude" ]; then
      export PATH="$HOME/.local/bin:$PATH"
      info "Added ~/.local/bin to PATH for this session."
    fi
  fi
  command -v claude &>/dev/null \
    || die "Claude Code CLI installed but 'claude' is still not in PATH. Restart your shell and re-run."
  ok "Claude Code CLI installed ($(claude --version 2>/dev/null || echo 'installed'))"
fi

# Resolve and export the absolute path to the claude binary.
CLAUDE_BINARY_PATH=$(command -v claude 2>/dev/null || true)
if [ -z "$CLAUDE_BINARY_PATH" ]; then
  for p in "$HOME/.local/bin/claude" /usr/local/bin/claude /opt/homebrew/bin/claude; do
    if [ -x "$p" ]; then CLAUDE_BINARY_PATH="$p"; break; fi
  done
fi
[ -n "$CLAUDE_BINARY_PATH" ] \
  || die "Could not resolve the claude binary path. Ensure Claude Code is installed and in PATH."
export CLAUDE_BINARY_PATH
info "Claude binary: ${CLAUDE_BINARY_PATH}"

# ─── Step 6: Verify Claude Code authentication ────────────────────────────────

step "Checking Claude Code authentication..."

_check_claude_auth() {
  # Run a minimal non-interactive prompt. Exits non-zero when not authenticated.
  "$CLAUDE_BINARY_PATH" -p 'respond with ok' --max-turns 1 &>/dev/null
}

if _check_claude_auth; then
  ok "Claude Code is authenticated."
else
  echo
  echo "  Claude Code is not yet authenticated."
  echo
  echo "  Opening a new Terminal window to authenticate..."
  echo

  # Open a new Terminal window running 'claude' for the user to authenticate.
  osascript -e "
    tell application \"Terminal\"
      activate
      do script \"export PATH=\\\"$HOME/.local/bin:\$PATH\\\"; claude\"
    end tell
  " 2>/dev/null || {
    echo "  Could not open a new Terminal window automatically."
    echo "  Please open a new terminal and run:  claude"
    echo
  }

  echo "  Complete the login in the new Terminal window,"
  echo "  then come back here and press Enter."
  echo

  while true; do
    read -rp "  Press Enter once you have authenticated Claude Code... " _ignored </dev/tty
    echo
    if _check_claude_auth; then
      ok "Claude Code is authenticated."
      break
    else
      info "Still not authenticated — please complete the login in the other terminal, then try again."
      echo
    fi
  done
fi

# ─── Prerequisites complete ───────────────────────────────────────────────────

echo
echo "All prerequisites satisfied."

if [ "$PREREQS_ONLY" -eq 1 ]; then
  echo "  (--prereqs-only) Exiting without running the installer."
  exit 0
fi

# ─── Step 7: Launch Teletraan installer ──────────────────────────────────────
# installer/index.js is the canonical Node-based installer. Earlier versions of
# the framework launched a Claude-Code-driven agent installer here, but that
# was more fragile (required Claude Code auth mid-install + many interactive
# steps that could fail). The Node installer below is testable, supports a
# --config flag for non-interactive runs, and has the same coverage.

step "Launching Teletraan installer..."
exec node "${SCRIPT_DIR}/installer/index.js" "$@"
