#!/bin/sh
# dev-start.sh — one-command local development environment.
#
# What it does (in order):
#   1. Generates ADMIN_TOKEN into .env if missing (closes #684 fail-open)
#   2. Runs infra/scripts/setup.sh (postgres + redis + langfuse + clickhouse
#      + temporal + populates template/plugin registry from manifest.json)
#   3. Starts the platform (Go :8080), waits for /health
#   4. Starts the canvas (Next.js :3000), waits for HTTP 200
#   5. Prints a readiness banner with API-key add instructions
#   6. On Ctrl-C, kills both background processes and tears down infra
#
# Prerequisites:
#   - Docker + Docker Compose v2  (for postgres/redis/langfuse/etc)
#   - Go 1.25+                     (for the platform binary)
#   - Node.js 20+                  (for the canvas)
#   - jq                           (for setup.sh's manifest clone — optional;
#                                   without it, template palette will be
#                                   empty until you run clone-manifest.sh
#                                   manually)
#
# Usage:
#   ./scripts/dev-start.sh
#   # Open http://localhost:3000, add your model API key in
#   # Config → Secrets & API Keys, then create your first workspace.
#
# Idempotent: re-running picks up where the last run left off (existing
# .env is preserved, npm install skipped if node_modules present, etc).

set -e

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
ENV_FILE="$ROOT/.env"

cleanup() {
    echo ""
    echo "==> Shutting down..."
    kill $PLATFORM_PID $CANVAS_PID 2>/dev/null || true
    # Use setup.sh's compose file (full infra) since that's what we
    # brought up. `down` keeps named volumes by default — call with
    # --volumes here only if you want a clean slate (we don't, since
    # idempotent re-runs are the usual case).
    docker compose -f "$ROOT/docker-compose.infra.yml" down 2>/dev/null || true
    echo "    Done."
}
trap cleanup EXIT INT TERM

# ─────────────────────────────────────────────── 1. dev-mode auth posture

# The AdminAuth middleware closes its fail-open the moment the first
# workspace token lands in the DB — at which point /workspaces and
# other admin routes 401 unless the caller has either ADMIN_TOKEN or
# the dev-mode escape hatch. The canvas at localhost:3000 has no
# bearer token to send, so without one of those two paths it can't
# call admin endpoints after a workspace exists.
#
# For local dev the right posture is the dev-mode escape hatch:
#
#   MOLECULE_ENV=development AND ADMIN_TOKEN unset
#
# That makes middleware.isDevModeFailOpen() return true and lets the
# canvas keep working without a bearer. Setting ADMIN_TOKEN here
# would BREAK the canvas (it has no way to read that token in dev).
#
# For SaaS the platform is provisioned with ADMIN_TOKEN set AND
# MOLECULE_ENV=production — either one closes the hatch. So the dev
# mode signal here is safe (it's only active when both other knobs
# are absent).
if [ -f "$ENV_FILE" ] && grep -q '^MOLECULE_ENV=' "$ENV_FILE"; then
    echo "==> Reusing MOLECULE_ENV from existing .env"
else
    echo "==> Setting MOLECULE_ENV=development in .env (dev-mode auth hatch)"
    {
        if [ -f "$ENV_FILE" ]; then
            cat "$ENV_FILE"
            echo ""
        fi
        echo "# Generated by scripts/dev-start.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ)"
        echo "# Local-dev auth posture: dev-mode fail-open lets the canvas at"
        echo "# localhost:3000 call admin endpoints without a bearer token."
        echo "# DO NOT set ADMIN_TOKEN here in dev — it would close the hatch"
        echo "# and the canvas would 401 on every admin call."
        echo "MOLECULE_ENV=development"
    } > "$ENV_FILE.tmp"
    mv "$ENV_FILE.tmp" "$ENV_FILE"
    echo "    Saved to $ENV_FILE"
fi

# Source .env so the platform inherits ADMIN_TOKEN (and anything else
# the user has added — e.g. ANTHROPIC_API_KEY for skipping the canvas
# Secrets UI). `set -a` exports every assignment in the sourced file
# without us having to know the var names.
set -a
# shellcheck disable=SC1090
. "$ENV_FILE"
set +a

# ─────────────────────────────────────────────── 2. infra + templates

# Use setup.sh (not raw docker-compose) so the template registry gets
# populated from manifest.json. Without that, the canvas template
# palette is empty and the user has to manually clone repos — exactly
# the friction this script exists to eliminate.
echo "==> Running infra/scripts/setup.sh (infra + template registry)"
"$ROOT/infra/scripts/setup.sh"

# ─────────────────────────────────────────────── 3. platform
#
# Two paths:
#   (a) `go` is on PATH → run the platform directly via `go run`.
#       Fast iteration, attaches to /tmp/molecule-platform.log.
#   (b) `go` is NOT on PATH → fall back to the published platform
#       container image. Slower first run (image pull) but the script
#       still works on a fresh dev box without forcing the dev to
#       install Go just to read logs.
#
# The earlier version of this script silently called `go run` and died
# with `go: not found` on dev boxes where Go wasn't installed; the
# script's own prerequisite list (line 13-21) said "Go 1.25+" but the
# user had no signpost between "open the doc" and "command not found
# at line 111." This branch makes the failure path either succeed
# (fallback) or fail loud with explicit install guidance.

if command -v go >/dev/null 2>&1; then
    echo "==> Starting Platform (Go :8080)"
    cd "$ROOT/workspace-server"
    go run ./cmd/server > /tmp/molecule-platform.log 2>&1 &
    PLATFORM_PID=$!
else
    echo "==> Go not found on PATH — falling back to docker-compose platform service"
    echo "    (Install Go 1.25+ for faster iteration: https://go.dev/dl/)"
    cd "$ROOT"
    # Bring up just the platform service from docker-compose.yml. infra/setup.sh
    # already brought up postgres+redis+etc on docker-compose.infra.yml; this
    # adds the platform container on top, mapped to :8080 so the rest of this
    # script's wait-for-/health loop works unchanged.
    docker compose up -d --build platform > /tmp/molecule-platform.log 2>&1 || {
        echo "    ✗ docker compose up platform failed — see /tmp/molecule-platform.log"
        echo "    Either install Go 1.25+ (https://go.dev/dl/) and rerun, or fix the docker fallback."
        exit 1
    }
    # PLATFORM_PID is unset on this path; cleanup() handles that with `kill ... 2>/dev/null || true`.
    PLATFORM_PID=
fi

echo "    Waiting for Platform /health..."
PLATFORM_READY=0
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
         21 22 23 24 25 26 27 28 29 30; do
    if curl -sf http://localhost:8080/health >/dev/null 2>&1; then
        echo "    Platform ready (t+${i}s)"
        PLATFORM_READY=1
        break
    fi
    sleep 1
done
if [ "$PLATFORM_READY" -ne 1 ]; then
    echo "    ✗ Platform did not respond in 30s — check /tmp/molecule-platform.log"
    exit 1
fi

# ─────────────────────────────────────────────── 4. canvas

echo "==> Starting Canvas (Next.js :3000)"
cd "$ROOT/canvas"
if [ ! -d node_modules ]; then
    echo "    First-run: npm install (~30-60s)"
    npm install
fi
npm run dev > /tmp/molecule-canvas.log 2>&1 &
CANVAS_PID=$!

echo "    Waiting for Canvas HTTP 200..."
CANVAS_READY=0
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
         21 22 23 24 25 26 27 28 29 30; do
    code=$(curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000/ 2>/dev/null || echo "0")
    if [ "$code" = "200" ]; then
        echo "    Canvas ready (t+${i}s)"
        CANVAS_READY=1
        break
    fi
    sleep 1
done
if [ "$CANVAS_READY" -ne 1 ]; then
    echo "    ✗ Canvas did not respond in 30s — check /tmp/molecule-canvas.log"
    exit 1
fi

# ─────────────────────────────────────────────── 5. readiness banner

cat <<EOF

═══════════════════════════════════════════════════════════
  Molecule AI dev environment ready

  Canvas:   http://localhost:3000
  Platform: http://localhost:8080
  Logs:     /tmp/molecule-platform.log
            /tmp/molecule-canvas.log

  Next steps:
    1. Open http://localhost:3000 in a browser.
    2. Add your model API key in
         Config → Secrets & API Keys → Global
       (skip if ANTHROPIC_API_KEY / OPENAI_API_KEY is already
        set in .env — the platform inherits it.)
    3. Click a template card or "+ Create blank workspace".

  Press Ctrl-C to stop all services.
═══════════════════════════════════════════════════════════
EOF

wait
