#!/usr/bin/env bash
# Tripwire one-line installer — prebuilt images, no source, no build.
#
#   curl -fsSL https://get.upliftr.io | bash
#
# Local installs come up trusted-HTTPS at https://tripwire.test (Caddy internal
# CA, installed into your machine's trust store — green padlock, no warnings).
#
# Optional knobs (env vars before the pipe):
#   ANTHROPIC_API_KEY=sk-...    seed the engine key (or set it later in Settings → AI)
#   TRIPWIRE_DOMAIN=tw.acme.com  install on a server with a real domain (plain http;
#                                front it with your own TLS / see the self-hosting docs)
#   TRIPWIRE_DIR=/opt/tripwire   install location (default ~/tripwire, /opt/tripwire as root)
set -euo pipefail

GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
ok()   { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
fatal(){ echo -e "\033[0;31m[ERROR]${NC} $1" >&2; exit 1; }

BASE_GET="${TRIPWIRE_GET_BASE:-https://get.upliftr.io}"
LOCAL_HOST="tripwire.test"
port_busy() { command -v lsof >/dev/null 2>&1 && lsof -nP -iTCP:"$1" -sTCP:LISTEN >/dev/null 2>&1; }

echo ""
echo -e "${BOLD}  Tripwire — one-line install${NC}"
echo ""

# ─── Preflight ───────────────────────────────────────────────────────
command -v docker >/dev/null 2>&1 || fatal "Docker isn't installed → https://docs.docker.com/get-docker/"
docker compose version >/dev/null 2>&1 || fatal "Docker Compose v2 is required (the 'docker compose' plugin)."
docker info >/dev/null 2>&1 || fatal "The Docker daemon isn't running — start Docker and retry."
command -v openssl >/dev/null 2>&1 || fatal "openssl is required to generate secrets."
command -v curl >/dev/null 2>&1 || fatal "curl is required."

# ─── Install location ────────────────────────────────────────────────
if [ -n "${TRIPWIRE_DIR:-}" ]; then DIR="${TRIPWIRE_DIR}";
elif [ "$(id -u)" = 0 ]; then DIR="/opt/tripwire";
else DIR="${HOME}/tripwire"; fi
mkdir -p "${DIR}"; cd "${DIR}"
ok "Installing in ${DIR}"

# ─── Mode: local (https://tripwire.test) vs server (real domain) ─────
LOCAL_HTTPS=0
DC=(docker compose)
if [ -n "${TRIPWIRE_DOMAIN:-}" ]; then
  HOST="${TRIPWIRE_DOMAIN}"; WEB_PORT="${TRIPWIRE_WEB_PORT:-80}"
  DASH_URL="http://${HOST}"
else
  HOST="${LOCAL_HOST}"
  # Local default: trusted HTTPS via Caddy. Needs 80 + 443; else fall back to http.
  if port_busy 80 || port_busy 443; then
    warn "Port 80 or 443 is busy — falling back to plain http on :3400."
    WEB_PORT=3400; DASH_URL="http://${HOST}:3400"
  else
    LOCAL_HTTPS=1; WEB_PORT=3400; DASH_URL="https://${HOST}"
    DC=(docker compose -f docker-compose.yml -f docker-compose.https.yml)
  fi
  # Map tripwire.test → localhost so the browser can open it (idempotent, sudo).
  if ! grep -qE "[[:space:]]${LOCAL_HOST//./\\.}([[:space:]]|\$)" /etc/hosts 2>/dev/null; then
    info "Mapping ${LOCAL_HOST} → 127.0.0.1 (needs sudo)…"
    if printf '\n127.0.0.1\t%s\n' "${LOCAL_HOST}" | sudo tee -a /etc/hosts >/dev/null 2>&1; then
      ok "Added ${LOCAL_HOST} to /etc/hosts"
    else
      warn "Couldn't edit /etc/hosts — add '127.0.0.1 ${LOCAL_HOST}' yourself."
    fi
  fi
fi

# ─── Fetch compose (+ HTTPS overlay locally) ─────────────────────────
info "Downloading compose file…"
curl -fsSL "${BASE_GET}/docker-compose.yml" -o docker-compose.yml || fatal "Couldn't download from ${BASE_GET}"
if [ "${LOCAL_HTTPS}" = 1 ]; then
  curl -fsSL "${BASE_GET}/docker-compose.https.yml" -o docker-compose.https.yml || fatal "Couldn't download the HTTPS overlay"
  curl -fsSL "${BASE_GET}/Caddyfile.local" -o Caddyfile.local || fatal "Couldn't download Caddyfile.local"
fi

# ─── Generate .env once ──────────────────────────────────────────────
if [ -f .env ] && grep -q '^TRIPWIRE_SECRET_KEY=' .env; then
  ok "Reusing existing .env"; REUSE=1
else
  REUSE=0
  info "Generating secrets + admin…"
  ADMIN_EMAIL="${TRIPWIRE_ADMIN_EMAIL:-admin@${HOST}}"
  ADMIN_PASS="$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 18)"
  umask 077
  cat > .env <<ENVEOF
# Tripwire self-hosted — generated by get.sh
TRIPWIRE_VERSION=latest
TRIPWIRE_DEPLOYMENT_MODE=selfhosted
TRIPWIRE_BASE_URL=${DASH_URL}
TRIPWIRE_WEB_PORT=${WEB_PORT}
TRIPWIRE_LOCAL_HOST=${HOST}
POSTGRES_USER=tripwire
POSTGRES_DB=tripwire
POSTGRES_PASSWORD=$(openssl rand -hex 24)
TRIPWIRE_SECRET_KEY=$(openssl rand -hex 64)
TRIPWIRE_JWT_SECRET=$(openssl rand -hex 64)
TRIPWIRE_ADMIN_EMAIL=${ADMIN_EMAIL}
TRIPWIRE_ADMIN_PASSWORD=${ADMIN_PASS}
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
TRIPWIRE_RUN_EXECUTOR=thread
TRIPWIRE_RUN_TIMEOUT_S=1800
TRIPWIRE_RUN_EGRESS=open
ENVEOF
  umask 022
  chmod 600 .env
  ok ".env written (chmod 600)"
fi
set -a; . ./.env; set +a

# ─── Pull + launch ───────────────────────────────────────────────────
info "Pulling images (no build — a few seconds to a minute)…"
"${DC[@]}" pull
info "Starting Tripwire…"
"${DC[@]}" up -d

info "Waiting for Tripwire to become ready…"
HEALTHY=false
READY='import httpx,sys; sys.exit(0 if httpx.get("http://localhost:8400/api/v1/health/ready",timeout=5).status_code==200 else 1)'
for _ in $(seq 1 40); do
  # </dev/null is critical: when this script is run via `curl … | bash`, the
  # script itself is on stdin, and `docker compose exec` would otherwise consume
  # the rest of it — so bash would hit EOF and skip the CA-trust + success steps.
  if "${DC[@]}" exec -T api python -c "${READY}" </dev/null >/dev/null 2>&1; then HEALTHY=true; break; fi
  sleep 3
done

# ─── Trust the local CA so https://tripwire.test is green ────────────
trust_local_ca() {
  local crt="${DIR}/tripwire-local-ca.crt"
  # Caddy writes its internal root CA here once it starts serving the site.
  for _ in $(seq 1 10); do
    "${DC[@]}" cp caddy:/data/caddy/pki/authorities/local/root.crt "${crt}" </dev/null >/dev/null 2>&1 && break
    sleep 2
  done
  [ -s "${crt}" ] || { warn "Couldn't read the local CA — the browser will warn until it's trusted."; return; }
  info "Trusting the local CA (needs sudo)…"
  case "$(uname -s)" in
    Darwin)
      sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${crt}" >/dev/null 2>&1 \
        && ok "CA trusted (macOS keychain)." || warn "CA trust failed — trust ${crt} manually (Keychain Access)."
      ;;
    Linux)
      if [ -d /usr/local/share/ca-certificates ]; then
        sudo cp "${crt}" /usr/local/share/ca-certificates/tripwire-local-ca.crt && sudo update-ca-certificates >/dev/null 2>&1 \
          && ok "CA trusted (ca-certificates)." || warn "CA trust failed — trust ${crt} manually."
      elif [ -d /etc/pki/ca-trust/source/anchors ]; then
        sudo cp "${crt}" /etc/pki/ca-trust/source/anchors/tripwire-local-ca.crt && sudo update-ca-trust >/dev/null 2>&1 \
          && ok "CA trusted (ca-trust)." || warn "CA trust failed — trust ${crt} manually."
      else
        warn "Unknown trust store — trust ${crt} manually."
      fi
      command -v certutil >/dev/null 2>&1 && for db in "${HOME}"/.mozilla/firefox/*/; do
        [ -f "${db}/cert9.db" ] && certutil -A -n "Tripwire Local CA" -t "C,," -i "${crt}" -d "sql:${db}" >/dev/null 2>&1 || true
      done
      ;;
    *) warn "Trust ${crt} in your OS trust store to avoid a browser warning." ;;
  esac
}
[ "${HEALTHY}" = true ] && [ "${LOCAL_HTTPS}" = 1 ] && trust_local_ca

echo ""
if [ "${HEALTHY}" = true ]; then
  echo -e "${GREEN}${BOLD}  ✓ Tripwire is live — open  ${DASH_URL}${NC}"
  echo ""
  if [ "${REUSE}" = 0 ]; then
    echo -e "  ${BOLD}Log in (saved in ${DIR}/.env):${NC}"
    echo -e "    Email:    ${CYAN}${TRIPWIRE_ADMIN_EMAIL}${NC}"
    echo -e "    Password: ${CYAN}${TRIPWIRE_ADMIN_PASSWORD}${NC}"
    echo ""
  fi
  echo -e "  ${BOLD}Manage (from ${DIR}):${NC}  docker compose {logs -f api | down | pull && up -d}"
  echo ""
  [ -z "${ANTHROPIC_API_KEY:-}" ] && warn "Add your Anthropic key in Settings → AI to run tests."
  echo -e "  Testing an app on your own machine? See ${CYAN}docs.upliftr.io/deployment/test-local-apps${NC}"
else
  warn "Health check didn't pass within ~120s. Check: (cd ${DIR} && ${DC[*]} logs api)"
  exit 1
fi
