#!/usr/bin/env bash
#
# bootstrap.sh — One-shot installer for the Zopu single-node execution plane.
#
# Run as root on a fresh Debian 12 host:
#
#   bash bootstrap.sh
#
# Environment overrides (set before running):
#   ZOPU_REPO_URL       — SSH clone URL (default: ssh://git@git.openputer.com:2222/puter/zopu-code.git)
#   ZOPU_REPO_BRANCH    — branch to deploy (default: dogfood/v0)
#   ZOPU_INSTALL_DIR    — install path (default: /opt/zopu)
#   ZOPU_SERVICE_USER   — system user (default: zopu)
#   TAILSCALE_AUTHKEY   — if set, configure Tailscale
#   TAILSCALE_HOSTNAME  — Tailscale hostname (default: zopu-runtime)
#
# Installs: Docker Engine, Node.js 22, Bun, clones the repo, runs bun install, builds the
# web app, daemon, and agent, creates a non-root service user, installs systemd
# units, and configures firewall/Tailscale defaults.

set -euo pipefail

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
REPO_URL="${ZOPU_REPO_URL:-ssh://git@git.openputer.com:2222/puter/zopu-code.git}"
REPO_BRANCH="${ZOPU_REPO_BRANCH:-dogfood/v0}"
INSTALL_DIR="${ZOPU_INSTALL_DIR:-/opt/zopu}"
SERVICE_USER="${ZOPU_SERVICE_USER:-zopu}"
DEPLOY_DIR="${INSTALL_DIR}/deploy/zopu-runtime"

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

log()  { echo -e "${GREEN}[bootstrap]${NC} $*"; }
warn() { echo -e "${YELLOW}[bootstrap]${NC} $*"; }
err()  { echo -e "${RED}[bootstrap]${NC} $*" >&2; }

# runuser is part of util-linux (essential on Debian) and always available.
# sudo is NOT assumed on minimal Debian installs.
run_as_service() {
  runuser -u "$SERVICE_USER" -- "$@"
}

# ---------------------------------------------------------------------------
# Pre-flight
# ---------------------------------------------------------------------------
if [[ "$EUID" -ne 0 ]]; then
  err "This script must be run as root."
  exit 1
fi

if [[ -f /etc/debian_version ]]; then
  log "Detected Debian $(cat /etc/debian_version)"
else
  warn "This script targets Debian 12. Other distributions may need manual adjustments."
fi

# ---------------------------------------------------------------------------
# 1. System packages
# ---------------------------------------------------------------------------
log "Updating apt and installing base packages..."
apt-get update -y
apt-get install -y \
  ca-certificates \
  curl \
  gnupg \
  ufw \
  git \
  jq \
  netcat-openbsd \
  openssh-client \
  unattended-upgrades \
  rsyslog

# ---------------------------------------------------------------------------
# 2. Docker Engine
# ---------------------------------------------------------------------------
if ! command -v docker &>/dev/null; then
  log "Installing Docker Engine..."
  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/debian/gpg \
    -o /etc/apt/keyrings/docker.asc
  chmod a+r /etc/apt/keyrings/docker.asc

  echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
    https://download.docker.com/linux/debian \
    $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
    > /etc/apt/sources.list.d/docker.list

  apt-get update -y
  apt-get install -y \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin
else
  log "Docker Engine already installed: $(docker --version)"
fi

systemctl enable --now docker

# ---------------------------------------------------------------------------
# 3. Node.js 22 (required by the Flue Node target)
# ---------------------------------------------------------------------------
NODE_MAJOR=$(node --version 2>/dev/null | sed -n 's/^v\([0-9][0-9]*\).*/\1/p')
if [[ -z "$NODE_MAJOR" || "$NODE_MAJOR" -lt 22 ]]; then
  log "Installing Node.js 22..."
  curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
  bash /tmp/nodesource_setup.sh
  apt-get install -y nodejs
else
  log "Node.js already installed: $(node --version)"
fi

# ---------------------------------------------------------------------------
# 4. Bun
# ---------------------------------------------------------------------------
if ! command -v bun &>/dev/null; then
  log "Installing Bun..."
  curl -fsSL https://bun.sh/install | bash
  install -m 0755 /root/.bun/bin/bun /usr/local/bin/bun
else
  log "Bun already installed: $(bun --version)"
fi

# ---------------------------------------------------------------------------
# 5. Service user
# ---------------------------------------------------------------------------
if ! id "$SERVICE_USER" &>/dev/null; then
  log "Creating service user: $SERVICE_USER"
  useradd -r -m -d "/home/$SERVICE_USER" -s /bin/bash "$SERVICE_USER"
fi

if ! id -nG "$SERVICE_USER" | grep -qw docker; then
  usermod -aG docker "$SERVICE_USER"
  log "Added $SERVICE_USER to docker group"
fi

# ---------------------------------------------------------------------------
# 6. Clone or update repository
# ---------------------------------------------------------------------------
if [[ -d "$INSTALL_DIR/.git" ]]; then
  log "Repository exists at $INSTALL_DIR, fetching latest..."
  cd "$INSTALL_DIR"
  git fetch origin
  git checkout "$REPO_BRANCH"
  git reset --hard "origin/$REPO_BRANCH"
else
  log "Cloning $REPO_URL (branch $REPO_BRANCH) into $INSTALL_DIR..."
  git clone --branch "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR"
  cd "$INSTALL_DIR"
fi

# ---------------------------------------------------------------------------
# 6b. Hand ownership of the checkout to the service user
# ---------------------------------------------------------------------------
log "Setting ownership of $INSTALL_DIR to $SERVICE_USER..."
chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR"

# ---------------------------------------------------------------------------
# 7. Install dependencies and build
# ---------------------------------------------------------------------------
log "Running bun install..."
run_as_service bun install

log "Building web app..."
run_as_service bun run --cwd apps/web build

log "Validating daemon production build..."
run_as_service bun run build:daemon

log "Building agent service..."
run_as_service bun run build:agents

# ---------------------------------------------------------------------------
# 8. Environment file
# ---------------------------------------------------------------------------
ENV_FILE="$INSTALL_DIR/.env"
if [[ ! -f "$ENV_FILE" ]]; then
  log "Copying .env.template to .env — EDIT BEFORE STARTING SERVICES"
  cp "$DEPLOY_DIR/.env.template" "$ENV_FILE"
  chown "$SERVICE_USER":"$SERVICE_USER" "$ENV_FILE"
  chmod 600 "$ENV_FILE"
  warn "Edit $ENV_FILE with real values before starting services."
else
  log ".env already exists at $ENV_FILE"
  # Ensure correct ownership and permissions on existing .env
  chown "$SERVICE_USER":"$SERVICE_USER" "$ENV_FILE"
  chmod 600 "$ENV_FILE"
fi

# ---------------------------------------------------------------------------
# 9. Persistent log directory
# ---------------------------------------------------------------------------
LOG_DIR="/var/log/zopu"
mkdir -p "$LOG_DIR"
chown "$SERVICE_USER":"$SERVICE_USER" "$LOG_DIR"

# ---------------------------------------------------------------------------
# 10. Install systemd units (substitute placeholders)
# ---------------------------------------------------------------------------
log "Installing systemd units..."
for unit in zopu-web.service zopu-daemon.service zopu-agent.service \
           zopu-health.timer zopu-health.service \
           zopu-docker-cleanup.timer zopu-docker-cleanup.service; do
  SRC="$DEPLOY_DIR/systemd/$unit"
  DST="/etc/systemd/system/$unit"
  if [[ -f "$SRC" ]]; then
    sed \
      -e "s|__INSTALL_DIR__|$INSTALL_DIR|g" \
      -e "s|__SERVICE_USER__|$SERVICE_USER|g" \
      "$SRC" > "$DST"
    log "  installed $unit"
  fi
done
systemctl daemon-reload

# ---------------------------------------------------------------------------
# 11. Firewall (deny-by-default, explicit allow for Tailscale)
# ---------------------------------------------------------------------------
log "Configuring firewall..."
if ! ufw status 2>/dev/null | grep -q "Status: active"; then
  ufw allow 22/tcp
  ufw default deny incoming
  ufw default allow outgoing

  # Allow all traffic on the Tailscale interface (if present)
  # This lets the agent and engine ports be reached over the private overlay.
  ufw allow in on tailscale0 || warn "tailscale0 not present yet; rule will activate when interface appears"

  ufw --force enable
  log "Firewall enabled: SSH (22) allowed, tailscale0 allowed."
  warn "Agent and RivetKit ports are NOT exposed on public interfaces."
  warn "Reachability is via Tailscale (tailscale0) only."
else
  log "Firewall already active. Ensuring tailscale0 rule..."
  ufw allow in on tailscale0 2>/dev/null || true
fi

# ---------------------------------------------------------------------------
# 12. Tailscale (optional)
# ---------------------------------------------------------------------------
if [[ -n "${TAILSCALE_AUTHKEY:-}" ]]; then
  log "Installing and configuring Tailscale..."
  if ! command -v tailscaled &>/dev/null; then
    curl -fsSL https://tailscale.com/install.sh | sh
  fi
  tailscale up --authkey "$TAILSCALE_AUTHKEY" \
    --hostname "${TAILSCALE_HOSTNAME:-zopu-runtime}" \
    --accept-routes
  log "Tailscale configured: $(tailscale ip -4 2>/dev/null || echo 'waiting for IP')"

  # Re-apply the tailscale0 firewall rule now that the interface exists
  ufw allow in on tailscale0 2>/dev/null || true
else
  warn "TAILSCALE_AUTHKEY not set — skipping Tailscale setup."
  warn "Without Tailscale, services are reachable only via localhost."
  warn "To use a private network interface, add an explicit UFW rule:"
  warn "  ufw allow in on <interface>"
fi

# ---------------------------------------------------------------------------
# 13. Disk-space monitoring cron
# /etc/cron.d format REQUIRES a username field.
# ---------------------------------------------------------------------------
log "Installing disk-space monitor (daily at 06:00)..."
CRON_LINE="0 6 * * * ${SERVICE_USER} ${DEPLOY_DIR}/scripts/disk-monitor.sh --warn-percent 80 >> /var/log/zopu/disk-monitor.log 2>&1"
echo "$CRON_LINE" > /etc/cron.d/zopu-disk-monitor
chmod 644 /etc/cron.d/zopu-disk-monitor

# ---------------------------------------------------------------------------
# Done
# ---------------------------------------------------------------------------
log "Bootstrap complete."
echo ""
echo "Next steps:"
echo "  1. Edit $ENV_FILE with real values"
echo "  2. Start services:"
echo "     systemctl start zopu-web zopu-daemon zopu-agent"
echo "  3. Enable health monitoring:"
echo "     systemctl enable --now zopu-health.timer zopu-docker-cleanup.timer"
echo "  4. Verify health:"
echo "     $DEPLOY_DIR/scripts/health-check.sh"
echo ""
warn "Services are NOT started automatically. Edit .env first."
