#!/usr/bin/env bash
#
# FreeLLMAPI one-line installer (#250, idea by @StealthTensor).
#
#   curl -fsSL https://tashfeenahmed.github.io/freellmapi/install.sh | bash
#
# What it does:
#   1. Checks for Docker + Compose.
#   2. Creates an install dir (default ~/freellmapi, override FREELLMAPI_DIR).
#   3. Writes a docker-compose.yml pinned to ghcr.io/tashfeenahmed/freellmapi:latest
#      and a .env with a freshly generated ENCRYPTION_KEY (kept on re-runs).
#   4. Pulls the image, starts the container, waits for the health endpoint.
#
# Options (env vars):
#   FREELLMAPI_DIR   install directory            (default: ~/freellmapi)
#   PORT             host port for the dashboard  (default: 3001)
#   HOST_BIND        host interface to publish on (default: 127.0.0.1).
#                    Set HOST_BIND=0.0.0.0 to reach it from your LAN — only on
#                    a trusted network; the proxy is single-user.
#
# Re-running is safe: existing .env (and your encryption key) is preserved,
# the compose file is refreshed, and the container is updated to :latest.

set -euo pipefail

INSTALL_DIR="${FREELLMAPI_DIR:-$HOME/freellmapi}"
PORT="${PORT:-3001}"
HOST_BIND="${HOST_BIND:-127.0.0.1}"
IMAGE="ghcr.io/tashfeenahmed/freellmapi:latest"

say()  { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mWARN\033[0m %s\n' "$*" >&2; }
die()  { printf '\033[1;31mERROR\033[0m %s\n' "$*" >&2; exit 1; }

# ── 1. prerequisites ─────────────────────────────────────────────────────────
command -v docker >/dev/null 2>&1 || die "Docker is required. Install it from https://docs.docker.com/get-docker/ and re-run."

if docker compose version >/dev/null 2>&1; then
  COMPOSE=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
  COMPOSE=(docker-compose)
else
  die "Docker Compose is required (the 'docker compose' plugin or 'docker-compose')."
fi

docker info >/dev/null 2>&1 || die "Docker daemon isn't running (or you lack permission). Start Docker and re-run."

# ── 2. install dir ───────────────────────────────────────────────────────────
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
say "Installing into $INSTALL_DIR"

# ── 3. .env (generated once — your at-rest encryption key lives here) ────────
if [ -f .env ]; then
  say "Keeping existing .env (encryption key preserved)"
else
  if command -v openssl >/dev/null 2>&1; then
    KEY="$(openssl rand -hex 32)"
  else
    # /dev/urandom fallback when openssl isn't installed
    KEY="$(od -vN 32 -An -tx1 /dev/urandom | tr -d ' \n')"
  fi
  umask 077
  cat > .env <<EOF
# Generated by install.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ)
# At-rest encryption key for your stored provider keys. Losing it means
# re-entering your provider keys, so back this file up.
ENCRYPTION_KEY=$KEY
PORT=$PORT
HOST_BIND=$HOST_BIND
EOF
  umask 022
  say "Wrote .env with a fresh ENCRYPTION_KEY"
fi

# ── 4. compose file (image-only; refreshed on every run) ─────────────────────
cat > docker-compose.yml <<EOF
# Written by install.sh — safe to edit; re-running the installer rewrites it.
services:
  freellmapi:
    image: $IMAGE
    env_file:
      - .env
    environment:
      NODE_ENV: production
      PORT: 3001
    # Bound to localhost by default — FreeLLMAPI is single-user and must not
    # be exposed to the internet. Set HOST_BIND=0.0.0.0 in .env for LAN access.
    ports:
      - "\${HOST_BIND:-127.0.0.1}:\${PORT:-3001}:3001"
    volumes:
      - freellmapi-data:/app/server/data
    restart: unless-stopped
    healthcheck:
      test:
        [
          "CMD",
          "node",
          "-e",
          "fetch('http://127.0.0.1:3001/api/ping').then((res) => { if (!res.ok) process.exit(1); }).catch(() => process.exit(1));"
        ]
      interval: 30s
      timeout: 5s
      start_period: 15s
      retries: 3

volumes:
  freellmapi-data:
EOF

# ── 5. pull + start ──────────────────────────────────────────────────────────
say "Pulling $IMAGE"
"${COMPOSE[@]}" pull --quiet 2>/dev/null || "${COMPOSE[@]}" pull
say "Starting FreeLLMAPI"
"${COMPOSE[@]}" up -d

# ── 6. wait for health ───────────────────────────────────────────────────────
say "Waiting for the server to come up"
HEALTH_HOST="127.0.0.1"
[ "$HOST_BIND" != "0.0.0.0" ] && [ "$HOST_BIND" != "127.0.0.1" ] && HEALTH_HOST="$HOST_BIND"
for i in $(seq 1 30); do
  if curl -fsS "http://$HEALTH_HOST:$PORT/api/ping" >/dev/null 2>&1; then
    HEALTHY=1
    break
  fi
  sleep 2
done

if [ "${HEALTHY:-0}" = "1" ]; then
  cat <<EOF

  FreeLLMAPI is running.

    Dashboard      http://localhost:$PORT
    Next steps     add provider keys on the Keys page, then grab your
                   unified API key from the page header — that's the
                   Bearer token your OpenAI-compatible client uses.
    API endpoint   http://localhost:$PORT/v1

    Update         cd $INSTALL_DIR && docker compose pull && docker compose up -d
    Logs           cd $INSTALL_DIR && docker compose logs -f
    Uninstall      cd $INSTALL_DIR && docker compose down -v && cd .. && rm -rf $INSTALL_DIR

EOF
else
  warn "Container started but the health endpoint didn't answer within 60s."
  warn "Check logs with: cd $INSTALL_DIR && ${COMPOSE[*]} logs -f"
  exit 1
fi
