#!/usr/bin/env bash
# setup.sh — Prepare the local development environment.
# Safe to run multiple times: every step is a no-op if already complete.
#
# What it does:
#   1. Checks prerequisites (docker, pnpm); installs just if missing
#   2. Adds 127.0.0.1 keycloak to /etc/hosts (needed for GoTrue ↔ Keycloak)
#   3. Creates .env with dev defaults if it doesn't exist
#   4. Fixes supabase/migrations permissions if root-owned (common Docker artifact)
#   5. Installs pnpm dependencies
#   6. Starts the Docker stack
#   7. Waits for postgres, kong, and keycloak to be healthy
#   8. Applies SQL migrations (tracked — won't re-apply)
#   9. Seeds the database (no-op if already seeded)
#  10. Compiles Paraglide i18n files
set -euo pipefail

# ── Colours ───────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'

step() { echo; echo -e "${BOLD}${BLUE}==> $*${NC}"; }
ok()   { echo -e "    ${GREEN}✓${NC}  $*"; }
warn() { echo -e "    ${YELLOW}!${NC}  $*"; }
die()  { echo -e "\n${RED}ERROR:${NC} $*" >&2; exit 1; }

# ── Resolve project root ──────────────────────────────────────────────────────
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"

DC="docker compose --env-file .env -f infra/docker-compose.dev.yml"
DB_CONTAINER="colectivo-dev-db-1"

# ── 1. Prerequisites ──────────────────────────────────────────────────────────
step "Checking prerequisites"

command -v docker >/dev/null 2>&1 \
    || die "docker not found. Install from https://docs.docker.com/get-docker/"
docker compose version >/dev/null 2>&1 \
    || die "docker compose v2 not found. Update Docker or install the compose plugin."
ok "docker $(docker --version | grep -oP '[\d.]+' | head -1)"

command -v pnpm >/dev/null 2>&1 \
    || die "pnpm not found. Install: curl -fsSL https://get.pnpm.io/install.sh | sh"
ok "pnpm $(pnpm --version)"

command -v python3 >/dev/null 2>&1 \
    || die "python3 not found — required to generate JWT tokens."

# just may be installed in non-standard locations (Cargo, Homebrew, ~/.local/bin).
# Extend PATH before checking so we don't reinstall needlessly.
export PATH="$PATH:$HOME/.cargo/bin:$HOME/.local/bin:/home/linuxbrew/.linuxbrew/bin"

if ! command -v just >/dev/null 2>&1; then
    warn "just not found — installing to ~/.local/bin (no sudo needed)"
    mkdir -p "$HOME/.local/bin"
    curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh \
        | bash -s -- --to "$HOME/.local/bin"
    export PATH="$PATH:$HOME/.local/bin"
    ok "just $(just --version)"
else
    ok "just $(just --version)"
fi

# ── 2. /etc/hosts ─────────────────────────────────────────────────────────────
step "Checking /etc/hosts for 'keycloak' → 127.0.0.1"

if grep -qE '^\s*127\.0\.0\.1\s+.*\bkeycloak\b' /etc/hosts 2>/dev/null; then
    ok "Entry already present"
else
    warn "Adding '127.0.0.1 keycloak' to /etc/hosts (requires sudo)"
    if echo "127.0.0.1 keycloak" | sudo -n tee -a /etc/hosts >/dev/null 2>&1; then
        ok "Entry added"
    else
        # sudo requires a password — ask the user to do it once manually
        echo
        echo -e "  ${YELLOW}Run this command manually, then re-run setup.sh:${NC}"
        echo
        echo "    echo '127.0.0.1 keycloak' | sudo tee -a /etc/hosts"
        echo
        die "/etc/hosts entry missing and sudo requires a password. Add it manually (see above) and re-run."
    fi
fi

# ── 3. .env ────────────────────────────────────────────────────────────────────
step "Setting up .env"

if [[ -f .env ]]; then
    ok ".env already exists — skipping"
else
    warn ".env not found — generating with dev defaults"

    # Well-known dev-only JWT secret. Never use in production.
    JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long"

    # Generate anon and service_role JWTs signed with the dev secret.
    # Python 3 is available on virtually every Linux/macOS dev machine.
    IFS=' ' read -r ANON_KEY SERVICE_KEY < <(python3 - <<PYEOF
import hmac, hashlib, base64, json

secret = "${JWT_SECRET}"

def b64url(data):
    if isinstance(data, str):
        data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

header = b64url(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(',', ':')))

def make_jwt(payload):
    p = b64url(json.dumps(payload, separators=(',', ':')))
    msg = f"{header}.{p}".encode()
    sig = b64url(hmac.new(secret.encode(), msg, hashlib.sha256).digest())
    return f"{header}.{p}.{sig}"

anon  = make_jwt({"role": "anon",         "iat": 1634740000, "exp": 4790413600})
srole = make_jwt({"role": "service_role", "iat": 1634740000, "exp": 4790413600})
print(anon, srole)
PYEOF
)

    cat > .env <<ENVEOF
# Local development environment — generated by setup.sh
# Never commit this file. Override values here; do NOT edit .env.example.

# ── PostgreSQL ────────────────────────────────────────────────────────────────
POSTGRES_PASSWORD=postgres

# ── Supabase JWT — dev only, never use in production ─────────────────────────
SUPABASE_JWT_SECRET=${JWT_SECRET}
PUBLIC_SUPABASE_ANON_KEY=${ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY=${SERVICE_KEY}

# ── Keycloak ──────────────────────────────────────────────────────────────────
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
# Client secret for colectivo-web (confidential client, used by GoTrue).
# Dev-only value — matches keycloak/realm-export.json. Override for production.
KEYCLOAK_CLIENT_SECRET=gotrue-dev-secret

# ── Public endpoints ──────────────────────────────────────────────────────────
PUBLIC_SUPABASE_URL=http://localhost:8001
PUBLIC_KEYCLOAK_URL=http://keycloak:8080
PUBLIC_KEYCLOAK_REALM=colectivo
PUBLIC_KEYCLOAK_CLIENT_ID=colectivo-web
PUBLIC_APP_URL=http://localhost:5173
ENVEOF

    ok ".env created (JWT tokens generated from dev secret)"
fi

# ── 4. supabase/migrations permissions ────────────────────────────────────────
step "Checking supabase/migrations permissions"

mkdir -p supabase/migrations

if [[ ! -w supabase/migrations ]]; then
    warn "supabase/migrations not writable — fixing with sudo chown"
    if sudo -n chown "$USER:$USER" supabase/migrations 2>/dev/null; then
        ok "Permissions fixed"
    else
        echo
        echo -e "  ${YELLOW}Run this command manually, then re-run setup.sh:${NC}"
        echo
        echo "    sudo chown \$USER:\$USER supabase/migrations"
        echo
        die "supabase/migrations is not writable. Fix it manually (see above) and re-run."
    fi
else
    ok "supabase/migrations is writable"
fi

# ── 5. Install dependencies ────────────────────────────────────────────────────
step "Installing pnpm dependencies"
pnpm install
ok "Dependencies ready"

# ── 6. Start Docker stack ──────────────────────────────────────────────────────
step "Starting Docker stack"
$DC up -d
ok "Containers started"

# ── 7. Wait for services ───────────────────────────────────────────────────────
step "Waiting for services"

# Usage: wait_for <label> <check_command> [timeout_seconds]
wait_for() {
    local label="$1"
    local cmd="$2"
    local max="${3:-90}"
    local n=0
    printf "    %-18s" "$label"
    until eval "$cmd" >/dev/null 2>&1; do
        n=$((n + 2))
        if (( n >= max )); then
            echo -e " ${RED}timed out${NC}"
            die "$label did not become healthy after ${max}s. Check: docker compose -f infra/docker-compose.dev.yml logs"
        fi
        sleep 2
        printf "."
    done
    echo -e " ${GREEN}ready${NC}"
}

# PostgreSQL must be first — everything else depends on it.
wait_for "postgres"  "docker exec ${DB_CONTAINER} pg_isready -U postgres -q"
# GoTrue (auth) is reached through Kong.
wait_for "kong/auth" "curl -sf http://localhost:8001/auth/v1/health"
# Keycloak realm endpoint — ready only after realm import completes.
wait_for "keycloak"  "curl -sf http://localhost:8080/realms/colectivo" 120

# ── 8. Apply database migrations ──────────────────────────────────────────────
step "Applying database migrations"

# Create migration tracking table in the public schema (idempotent).
docker exec "$DB_CONTAINER" psql -U postgres -d postgres -q \
    -c "CREATE TABLE IF NOT EXISTS public._applied_migrations (
            filename   TEXT        PRIMARY KEY,
            applied_at TIMESTAMPTZ DEFAULT now()
        );" \
    2>/dev/null || die "Could not create migration tracking table"

shopt -s nullglob
migration_files=(supabase/migrations/*.sql)
shopt -u nullglob

if (( ${#migration_files[@]} == 0 )); then
    warn "No migration files found in supabase/migrations/"
else
    for sql_file in "${migration_files[@]}"; do
        filename="$(basename "$sql_file")"

        already_applied=$(docker exec "$DB_CONTAINER" psql -U postgres -d postgres -tAq \
            -c "SELECT count(*) FROM public._applied_migrations WHERE filename = '${filename}'" \
            2>/dev/null)

        if [[ "${already_applied:-0}" -gt 0 ]]; then
            ok "$filename (already applied)"
            continue
        fi

        printf "    Applying %-40s" "$filename ..."
        if docker exec -i "$DB_CONTAINER" \
               psql -U postgres -d postgres -q -v ON_ERROR_STOP=1 \
               < "$sql_file" 2>/tmp/migration_err; then
            docker exec "$DB_CONTAINER" psql -U postgres -d postgres -q \
                -c "INSERT INTO public._applied_migrations (filename)
                    VALUES ('${filename}') ON CONFLICT DO NOTHING;"
            echo -e " ${GREEN}done${NC}"
        else
            echo -e " ${RED}FAILED${NC}"
            echo "--- error output ---"
            cat /tmp/migration_err
            die "Migration ${filename} failed. Fix the error and re-run setup.sh."
        fi
    done
fi

# ── 9. Seed database ───────────────────────────────────────────────────────────
step "Seeding database"

user_count=$(docker exec "$DB_CONTAINER" psql -U postgres -d postgres -tAq \
    -c "SELECT count(*) FROM public.users" 2>/dev/null || echo "0")

if (( user_count > 0 )); then
    ok "Already seeded ($user_count users in public.users)"
else
    printf "    Applying seed.sql ..."
    if docker exec -i "$DB_CONTAINER" \
           psql -U postgres -d postgres -q -v ON_ERROR_STOP=1 \
           < supabase/seed.sql 2>/tmp/seed_err; then
        echo -e " ${GREEN}done${NC}"
    else
        echo -e " ${RED}FAILED${NC}"
        cat /tmp/seed_err
        die "seed.sql failed. Fix the error and re-run setup.sh."
    fi
fi

# ── 10. Compile Paraglide i18n ──────────────────────────────────────────────
step "Compiling Paraglide i18n"

PARAGLIDE_OUTDIR="apps/web/src/lib/paraglide"

if [[ -d "$PARAGLIDE_OUTDIR" && -f "$PARAGLIDE_OUTDIR/runtime.js" ]]; then
    ok "Already compiled"
else
    PARAGLIDE_BIN="$(find node_modules/.pnpm -name 'paraglide-js' -path '*/.bin/paraglide-js' \
        2>/dev/null | head -1)"

    if [[ -z "$PARAGLIDE_BIN" ]]; then
        warn "paraglide-js binary not found — skipping (run 'pnpm install' and re-run setup.sh)"
    else
        "$PARAGLIDE_BIN" compile \
            --project apps/web/project.inlang \
            --outdir "$PARAGLIDE_OUTDIR"
        ok "Compiled to $PARAGLIDE_OUTDIR"
    fi
fi

# ── Done ───────────────────────────────────────────────────────────────────────
echo
echo -e "${BOLD}${GREEN}✓ Local environment is ready.${NC}"
echo
echo "  Start the app:         just dev"
echo "  Supabase Studio:       http://localhost:54323"
echo "  Keycloak Admin:        http://localhost:8080/admin  (admin / admin)"
echo "  Test users (pw: test1234): ana, borja, carmen, david, eva"
echo
