#!/usr/bin/env bash
# Build server apps and restart PM2 processes.
#
# Usage:
#   cd /var/www/html/connect
#   chmod +x deploy.sh
#   ./deploy.sh

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"

if [[ ! -f "$ROOT/.env" ]]; then
  echo "ERROR: missing $ROOT/.env" >&2
  exit 1
fi

set -a
# shellcheck disable=SC1091
source "$ROOT/.env"
set +a

if [[ -z "${NEXT_PUBLIC_API_URL:-}" || -z "${NEXT_PUBLIC_GATEWAY_URL:-}" ]]; then
  echo "ERROR: NEXT_PUBLIC_API_URL and NEXT_PUBLIC_GATEWAY_URL must be set in .env" >&2
  exit 1
fi

if [[ "$NEXT_PUBLIC_API_URL" == *"127.0.0.1"* || "$NEXT_PUBLIC_API_URL" == *"localhost"* ]]; then
  echo "ERROR: NEXT_PUBLIC_API_URL is still local ($NEXT_PUBLIC_API_URL)." >&2
  echo "       Edit $ROOT/.env — use https://api-new.windshieldhub.com/api — then re-run." >&2
  exit 1
fi

write_runtime_config() {
  local target="$1"
  mkdir -p "$(dirname "$target")"
  # JSON.stringify so TURN passwords with quotes/$ don't break the file.
  node -e '
    const fs = require("fs");
    const env = process.env;
    const cfg = {
      NEXT_PUBLIC_API_URL: env.NEXT_PUBLIC_API_URL || "",
      NEXT_PUBLIC_GATEWAY_URL: env.NEXT_PUBLIC_GATEWAY_URL || "",
    };
    if (env.NEXT_PUBLIC_STUN_URLS) cfg.NEXT_PUBLIC_STUN_URLS = env.NEXT_PUBLIC_STUN_URLS;
    if (env.NEXT_PUBLIC_TURN_URLS) cfg.NEXT_PUBLIC_TURN_URLS = env.NEXT_PUBLIC_TURN_URLS;
    else if (env.NEXT_PUBLIC_TURN_URL) cfg.NEXT_PUBLIC_TURN_URL = env.NEXT_PUBLIC_TURN_URL;
    if (env.NEXT_PUBLIC_TURN_USERNAME) cfg.NEXT_PUBLIC_TURN_USERNAME = env.NEXT_PUBLIC_TURN_USERNAME;
    if (env.NEXT_PUBLIC_TURN_CREDENTIAL) cfg.NEXT_PUBLIC_TURN_CREDENTIAL = env.NEXT_PUBLIC_TURN_CREDENTIAL;
    const body = "/* Generated by deploy.sh — do not edit by hand */\n"
      + "window.__CONNECTHUB_ENV__ = " + JSON.stringify(cfg, null, 2) + ";\n";
    fs.writeFileSync(process.argv[1], body);
  ' "$target"
  echo "    wrote $target"
}

# Next build-time env (basePath) + runtime config (API/gateway URLs).
WEB_ENV="$ROOT/apps/web/.env.production"
cat > "$WEB_ENV" <<EOF
NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
NEXT_PUBLIC_GATEWAY_URL=${NEXT_PUBLIC_GATEWAY_URL}
NEXT_PUBLIC_BASE_PATH=${NEXT_PUBLIC_BASE_PATH:-}
EOF

write_runtime_config "$ROOT/apps/web/public/runtime-config.js"

echo "==> Repo: $ROOT"
echo "==> Public API URL:     $NEXT_PUBLIC_API_URL"
echo "==> Public Gateway URL: $NEXT_PUBLIC_GATEWAY_URL"
echo "==> Web basePath:       ${NEXT_PUBLIC_BASE_PATH:-"(none)"}"
if [[ -n "${NEXT_PUBLIC_TURN_URLS:-}${NEXT_PUBLIC_TURN_URL:-}" ]]; then
  echo "==> TURN relay:         configured"
else
  echo "==> TURN relay:         NOT set (cross-Wi‑Fi / cellular calls will stay on Connecting)"
fi

if [[ -z "${DATABASE_URL:-}" ]]; then
  echo "ERROR: DATABASE_URL must be set in .env" >&2
  exit 1
fi

if [[ "$DATABASE_URL" == *"127.0.0.1"* || "$DATABASE_URL" == *"localhost"* ]]; then
  echo "WARNING: DATABASE_URL points at localhost. On the server that is only OK if Postgres runs on this same machine." >&2
fi

echo "==> Installing dependencies…"
pnpm install

echo "==> Applying database migrations (calls, tasks, RLS, …)…"
# Needs a role that can run DDL. Prefer MIGRATE_DATABASE_URL (admin) when set;
# otherwise uses DATABASE_URL from .env.
if [[ -n "${MIGRATE_DATABASE_URL:-}" ]]; then
  DATABASE_URL="$MIGRATE_DATABASE_URL" pnpm --filter @slackwsh/data run db:migrate
else
  pnpm --filter @slackwsh/data run db:migrate
fi

echo "==> Building shared libs…"
pnpm --filter @slackwsh/contracts --filter @slackwsh/core --filter @slackwsh/data \
     --filter @slackwsh/messaging --filter @slackwsh/sync \
     run build

echo "==> Building api / gateway / worker…"
pnpm --filter @slackwsh/api --filter @slackwsh/gateway --filter @slackwsh/worker run build

require_file() {
  local path="$1"
  if [[ ! -f "$path" ]]; then
    echo "ERROR: expected build output missing: $path" >&2
    echo "       Re-run the package build and inspect the error above." >&2
    exit 1
  fi
  echo "    ok $path"
}

echo "==> Verifying Nest build outputs…"
require_file "$ROOT/apps/api/dist/main.js"
require_file "$ROOT/apps/gateway/dist/main.js"
require_file "$ROOT/apps/worker/dist/main.js"

echo "==> Building web + marketing…"
pnpm --filter @slackwsh/web --filter @slackwsh/marketing run build

# Overwrite again after build (public/ is copied into out/ during next build).
write_runtime_config "$ROOT/apps/web/out/runtime-config.js"
write_runtime_config "$ROOT/apps/web/public/runtime-config.js"

echo "==> Checking runtime-config.js on disk:"
cat "$ROOT/apps/web/out/runtime-config.js"

if ! grep -q "api-new.windshieldhub.com" "$ROOT/apps/web/out/runtime-config.js"; then
  echo "ERROR: out/runtime-config.js does not contain api-new.windshieldhub.com" >&2
  exit 1
fi

# Catch the classic "Unexpected token '<'" failure: HTML served for a .js chunk.
# That happens when basePath wasn't baked in (chunks requested under /_next instead
# of /app/_next) or when Apache falls back to index.html for missing assets.
if [[ -n "${NEXT_PUBLIC_BASE_PATH:-}" ]]; then
  local_index="$ROOT/apps/web/out/index.html"
  if [[ ! -f "$local_index" ]]; then
    echo "ERROR: missing $local_index — web build did not produce a static export" >&2
    exit 1
  fi
  if ! grep -q "${NEXT_PUBLIC_BASE_PATH}/_next/" "$local_index"; then
    echo "ERROR: $local_index does not reference ${NEXT_PUBLIC_BASE_PATH}/_next/" >&2
    echo "       NEXT_PUBLIC_BASE_PATH was probably empty during next build." >&2
    echo "       Set NEXT_PUBLIC_BASE_PATH=/app in .env and re-run ./deploy.sh" >&2
    exit 1
  fi
  echo "==> basePath ok in index.html (${NEXT_PUBLIC_BASE_PATH}/_next/...)"
fi

if [[ ! -d "$ROOT/apps/web/out/_next" ]]; then
  echo "ERROR: missing apps/web/out/_next — static chunks were not emitted" >&2
  exit 1
fi

if ! command -v pm2 >/dev/null 2>&1; then
  echo "ERROR: pm2 is not installed. Install with: sudo npm install -g pm2" >&2
  exit 1
fi

# Kill anything still bound to our ports (old nohup/manual node, crashed PM2 children).
free_port() {
  local port="$1"
  local pids
  pids="$(ss -tlnp "sport = :$port" 2>/dev/null | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | sort -u || true)"
  if [[ -z "$pids" ]]; then
    pids="$(lsof -t -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)"
  fi
  if [[ -n "$pids" ]]; then
    echo "==> Freeing port $port (pids: $pids)"
    # shellcheck disable=SC2086
    kill $pids 2>/dev/null || true
    sleep 1
    # shellcheck disable=SC2086
    kill -9 $pids 2>/dev/null || true
  fi
}

echo "==> Stopping PM2 apps before freeing ports…"
pm2 stop api gateway worker 2>/dev/null || true
free_port 3001
free_port 3002

echo "==> Restarting PM2 processes (api / gateway / worker)…"
# Recreate rather than blind-restart so a stale PM2 script/cwd cannot keep
# pointing at a missing entry file after a failed or partial build.
# Nest mains load ../../.env from apps/<pkg> cwd (repo root .env).
start_app() {
  local name="$1"
  local pkg="$2"
  pm2 delete "$name" 2>/dev/null || true
  pm2 start "$ROOT/apps/$pkg/dist/main.js" \
    --name "$name" \
    --cwd "$ROOT/apps/$pkg"
}

start_app api api
start_app gateway gateway
start_app worker worker

if pm2 describe web >/dev/null 2>&1; then
  pm2 delete web || true
fi

sleep 2
if ! ss -tln | grep -q ':3001'; then
  echo "ERROR: nothing is listening on :3001 after restart — check: pm2 logs api" >&2
  pm2 logs api --lines 30 --nostream || true
  exit 1
fi

pm2 save
pm2 status

echo "==> Deploy finished."
echo "    Open https://api-new.windshieldhub.com/app/ and hard-refresh (Ctrl+Shift+R)."
echo "    Confirm in DevTools → Network that runtime-config.js shows api-new.windshieldhub.com"
