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

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
PID_FILE="$PROJECT_DIR/.server.pid"
LOG_FILE="$PROJECT_DIR/logs/server.log"

# Optional project-root .env (gitignored; setup.sh writes credentials here,
# operators can add e.g. HOST=0.0.0.0 for reverse-proxy deployments so a bare
# `server.sh restart` keeps the bind address). Lines are `KEY=value` or
# `export KEY='value'`. Precedence: explicit environment > .env > defaults,
# so `HOST=127.0.0.1 scripts/server.sh restart` still overrides the file.
if [[ -f "$PROJECT_DIR/.env" ]]; then
  # Values are parsed LITERALLY (no eval/source): a value is either bare, or
  # single-quoted in setup.sh's format ('\'' encodes a literal quote), or
  # double-quoted (quotes stripped, contents kept literal — no $ expansion).
  # One KEY=value per line; full-line comments only.
  #
  # Keys this loader itself set from .env are tracked space-delimited (keys are
  # validated identifiers so this is unambiguous — no Bash-4 associative
  # arrays, macOS ships bash 3.2). Only EXTERNAL pre-set environment is
  # protected; duplicate keys within .env keep normal source semantics (last
  # line wins) because earlier lines are recorded here and may be overridden.
  _envfile_set=" "
  _q=\'
  _esc=$'\x01'
  while IFS= read -r _line || [[ -n "$_line" ]]; do
    _line="${_line#"${_line%%[![:space:]]*}"}"   # ltrim
    [[ -z "$_line" || "$_line" == \#* ]] && continue
    _kv="${_line#export }"
    _key="${_kv%%=*}"
    [[ "$_key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
    [[ "$_kv" == *=* ]] || continue
    _val="${_kv#*=}"
    case "$_val" in
      "$_q"*)
        _val="${_val//${_q}\\${_q}${_q}/${_esc}}"  # '\''  -> literal-quote marker
        _val="${_val//${_q}/}"                     # drop quoting quotes
        _val="${_val//${_esc}/${_q}}"              # restore literal quotes
        ;;
      \"*\")
        _val="${_val#\"}"
        _val="${_val%\"}"
        ;;
    esac
    if [[ "$_envfile_set" == *" $_key "* || -z "${!_key+x}" ]]; then
      export "$_key=$_val"
      _envfile_set="${_envfile_set}${_key} "
    fi
  done < "$PROJECT_DIR/.env"
  unset _line _kv _key _val _envfile_set _q _esc
fi

PORT="${PORT:-9876}"

cd "$PROJECT_DIR"

usage() {
  echo "Usage: $0 {start|stop|restart|status|logs}"
  exit 1
}

is_running() {
  if [[ -f "$PID_FILE" ]]; then
    local pid
    pid=$(cat "$PID_FILE")
    if kill -0 "$pid" 2>/dev/null; then
      return 0
    fi
    rm -f "$PID_FILE"
  fi
  return 1
}

do_start() {
  if is_running; then
    echo "Server already running (PID $(cat "$PID_FILE"))"
    return 0
  fi

  # Surface stray maestro processes the pidfile doesn't track (e.g. an orphaned
  # run on another port/mode that a previous restart never stopped). The
  # app-level worker lock (src/instance-lock.ts) will refuse to double-start
  # worker mode against the same DB, but warn here so the operator notices.
  local strays
  strays=$(pgrep -f "node dist/main.js" 2>/dev/null | grep -vx "$$" || true)
  if [[ -n "$strays" ]]; then
    echo "WARNING: other 'node dist/main.js' process(es) detected (PID: $(echo "$strays" | tr '\n' ' '))."
    echo "         If a previous run was not stopped cleanly, stop it first to avoid port/DB conflicts."
  fi

  mkdir -p "$(dirname "$LOG_FILE")"

  echo "Checking runtime dependencies..."
  "$SCRIPT_DIR/prepare.sh"

  echo "Building..."
  npm run build --silent 2>&1 | tail -1

  echo "Starting server on port $PORT..."
  # AAO is a single binary with two modes (dist/main.js dispatches):
  #   AAO_MODE=worker  (default) — full orchestrator (DB + bridge API + workers)
  #   AAO_MODE=gateway          — OpenAI-compatible LLM gateway only
  # To launch as a gateway: `AAO_MODE=gateway scripts/server.sh start` and
  # set `gateway.listen_port` in config.yaml (default 4000). dist/index.js
  # is preserved as a worker-mode shim for legacy paths.
  PORT="$PORT" AAO_MODE="${AAO_MODE:-worker}" nohup node dist/main.js >> "$LOG_FILE" 2>&1 &
  local pid=$!
  echo "$pid" > "$PID_FILE"

  # Wait briefly and verify it started
  sleep 2
  if kill -0 "$pid" 2>/dev/null; then
    echo "Server started (PID $pid, log: $LOG_FILE)"
  else
    rm -f "$PID_FILE"
    echo "Server failed to start. Check $LOG_FILE"
    tail -5 "$LOG_FILE"
    return 1
  fi
}

do_stop() {
  if ! is_running; then
    echo "Server not running"
    # Also kill any stray process on the port
    local stray
    stray=$(lsof -ti:"$PORT" 2>/dev/null || true)
    if [[ -n "$stray" ]]; then
      echo "Found stray process on port $PORT (PID $stray), killing..."
      kill "$stray" 2>/dev/null || true
    fi
    return 0
  fi

  local pid
  pid=$(cat "$PID_FILE")
  echo "Stopping server (PID $pid)..."
  kill "$pid" 2>/dev/null || true

  # Wait for graceful shutdown
  for i in {1..10}; do
    if ! kill -0 "$pid" 2>/dev/null; then
      rm -f "$PID_FILE"
      echo "Server stopped"
      return 0
    fi
    sleep 0.5
  done

  # Force kill
  echo "Force killing..."
  kill -9 "$pid" 2>/dev/null || true
  rm -f "$PID_FILE"
  echo "Server stopped (forced)"
}

do_status() {
  if is_running; then
    local pid
    pid=$(cat "$PID_FILE")
    echo "Server running (PID $pid, port $PORT)"
  else
    echo "Server not running"
  fi
}

do_logs() {
  if [[ -f "$LOG_FILE" ]]; then
    tail -f "$LOG_FILE"
  else
    echo "No log file found at $LOG_FILE"
  fi
}

case "${1:-}" in
  start)   do_start ;;
  stop)    do_stop ;;
  restart) do_stop; do_start ;;
  status)  do_status ;;
  logs)    do_logs ;;
  *)       usage ;;
esac
