#!/usr/bin/env bash
# dev-reseed-auth.sh — rebuild the local Zitadel instance from scratch and rewire
# every env file that references it.
#
# A fresh Zitadel means new OIDC client IDs, new IdP IDs, and a new seeder PAT.
# Doing that by hand means editing three env files from the seeder's output and
# remembering that `docker compose restart api` does NOT reload .env. This does
# all of it.
#
#   ./dev-reseed-auth.sh             keep app data (workspaces, meetings)
#   ./dev-reseed-auth.sh --all       also wipe app_pg — everything goes
#   ./dev-reseed-auth.sh --teardown  wipe and stop; do NOT reseed
#   ./dev-reseed-auth.sh --yes       skip the confirmation prompt
#
# Social IdPs are seeded only when their credentials are present in src/.env
# (ZITADEL_MICROSOFT_CLIENT_ID/SECRET, ZITADEL_GOOGLE_CLIENT_ID/SECRET).

set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")" && pwd)"
SRC="$REPO_ROOT/src"
PROJECT=continuum

WIPE_APP=0
ASSUME_YES=0
TEARDOWN_ONLY=0
for arg in "$@"; do
  case "$arg" in
    --all) WIPE_APP=1 ;;
    --yes|-y) ASSUME_YES=1 ;;
    --teardown) TEARDOWN_ONLY=1 ;;
    *) echo "unknown flag: $arg" >&2; exit 1 ;;
  esac
done

say() { printf '\n\033[1m→ %s\033[0m\n' "$1"; }

# Portable in-place env edit. GNU and BSD sed disagree on -i, so rewrite via a
# temp file instead. Replaces the key if present, appends it otherwise.
set_var() {
  local file="$1" key="$2" val="$3" tmp
  tmp="$(mktemp)"
  if [ -f "$file" ] && grep -q "^${key}=" "$file"; then
    awk -v k="$key" -v v="$val" '$0 ~ "^" k "=" { print k "=" v; next } { print }' "$file" > "$tmp"
  else
    [ -f "$file" ] && cat "$file" > "$tmp"
    printf '%s=%s\n' "$key" "$val" >> "$tmp"
  fi
  mv "$tmp" "$file"
}

[ -f "$SRC/.env" ] || { echo "src/.env not found — copy it from .env.example first." >&2; exit 1; }
[ -f "$SRC/ui/.env" ]     || cp "$SRC/ui/.env.example"     "$SRC/ui/.env"
[ -f "$SRC/mobile/.env" ] || cp "$SRC/mobile/.env.example" "$SRC/mobile/.env"

echo
echo "  This DESTROYS the local Zitadel instance:"
echo "    - all Zitadel users and their external identity links"
echo "    - the admin password resets to the value in src/.env"
if [ "$WIPE_APP" = "1" ]; then
  echo "    - AND all app data: workspaces, meetings, commitments"
else
  echo "    - app data is preserved (users re-match by email on next sign-in)"
fi
if [ "$TEARDOWN_ONLY" = "1" ]; then
  echo "    - teardown only: NOT reseeded, auth left broken until you re-run"
fi
echo
if [ "$ASSUME_YES" != "1" ]; then
  printf "  Continue? [y/N] "
  read -r reply
  case "$reply" in [yY]*) ;; *) echo "aborted."; exit 0 ;; esac
fi

cd "$SRC"

say "tearing down Zitadel"
docker compose stop zitadel zitadel-login auth-gateway zitadel-db >/dev/null 2>&1 || true
docker compose rm -f zitadel zitadel-login auth-gateway zitadel-db >/dev/null 2>&1 || true
docker volume rm "${PROJECT}_zitadel_pg" "${PROJECT}_zitadel_bootstrap" >/dev/null 2>&1 || true

if [ "$WIPE_APP" = "1" ]; then
  say "tearing down app data"
  docker compose stop api worker db >/dev/null 2>&1 || true
  docker compose rm -f api worker db >/dev/null 2>&1 || true
  docker volume rm "${PROJECT}_app_pg" >/dev/null 2>&1 || true
fi

if [ "$TEARDOWN_ONLY" = "1" ]; then
  echo
  echo "  Torn down. Auth is now BROKEN until you reseed:"
  echo "    ./dev-reseed-auth.sh"
  echo
  echo "  src/.env, src/ui/.env and src/mobile/.env still hold client ids and IdP"
  echo "  ids for the instance that was just destroyed. Starting the stack without"
  echo "  reseeding renders the provider buttons (the check is only for a non-empty"
  echo "  value) but every sign-in fails."
  echo
  exit 0
fi

say "starting Zitadel"
docker compose up -d zitadel-db zitadel zitadel-login auth-gateway

# The healthcheck gates `up -d`, but the bootstrap PAT is written slightly after
# the instance reports healthy.
say "waiting for the bootstrap PAT"
PAT=""
for _ in $(seq 1 30); do
  PAT="$(docker run --rm -v "${PROJECT}_zitadel_bootstrap":/b alpine cat /b/seeder.pat 2>/dev/null || true)"
  [ -n "$PAT" ] && break
  sleep 2
done
[ -n "$PAT" ] || { echo "seeder PAT never appeared — check: docker compose logs zitadel" >&2; exit 1; }
set_var "$SRC/.env" ZITADEL_SEEDER_PAT "$PAT"

say "seeding branding, OIDC apps, and identity providers"
SEED_OUT="$(docker compose --profile seed run --rm zitadel-branding 2>&1)"
echo "$SEED_OUT"

pluck() { printf '%s\n' "$SEED_OUT" | sed -n "s/^ *$1=//p" | tr -d '\r' | head -1; }
WEB_ID="$(pluck ZITADEL_CLIENT_ID)"
MOBILE_ID="$(pluck ZITADEL_MOBILE_CLIENT_ID)"
MS_IDP="$(pluck VITE_ZITADEL_MICROSOFT_IDP_ID)"
GOOGLE_IDP="$(pluck VITE_ZITADEL_GOOGLE_IDP_ID)"

[ -n "$WEB_ID" ] && [ -n "$MOBILE_ID" ] || {
  echo "could not parse client ids from the seeder output — env files left untouched." >&2
  exit 1
}

say "rewiring env files"
set_var "$SRC/.env"        ZITADEL_CLIENT_ID           "$WEB_ID"
set_var "$SRC/.env"        ZITADEL_MOBILE_CLIENT_ID    "$MOBILE_ID"
set_var "$SRC/ui/.env"     VITE_ZITADEL_CLIENT_ID      "$WEB_ID"
set_var "$SRC/mobile/.env" EXPO_PUBLIC_ZITADEL_CLIENT_ID "$MOBILE_ID"
printf '  src/.env, src/ui/.env, src/mobile/.env — client ids\n'

if [ -n "$MS_IDP" ]; then
  set_var "$SRC/ui/.env"     VITE_ZITADEL_MICROSOFT_IDP_ID        "$MS_IDP"
  set_var "$SRC/mobile/.env" EXPO_PUBLIC_ZITADEL_MICROSOFT_IDP_ID "$MS_IDP"
  printf '  Microsoft IdP %s\n' "$MS_IDP"
fi
if [ -n "$GOOGLE_IDP" ]; then
  set_var "$SRC/ui/.env"     VITE_ZITADEL_GOOGLE_IDP_ID        "$GOOGLE_IDP"
  set_var "$SRC/mobile/.env" EXPO_PUBLIC_ZITADEL_GOOGLE_IDP_ID "$GOOGLE_IDP"
  printf '  Google IdP %s\n' "$GOOGLE_IDP"
fi
if [ -z "$MS_IDP" ] && [ -z "$GOOGLE_IDP" ]; then
  printf '  no social IdPs seeded — set ZITADEL_{MICROSOFT,GOOGLE}_CLIENT_ID/SECRET in src/.env\n'
fi

# `restart` reuses the existing container config and silently keeps stale env;
# api must be recreated. ui reads .env off the mounted volume, so restart is fine.
say "recreating api, restarting ui"
docker compose up -d api worker db >/dev/null
# `up -d` first: restart alone cannot CREATE the container, so a reseed from a
# fully-down stack silently left the UI missing. The restart makes the .env
# reload deterministic for an already-live ui rather than relying on watchers.
docker compose up -d ui >/dev/null
docker compose restart ui >/dev/null

say "verifying"
sleep 12
ok=0
code="$(curl -s -o /dev/null -w '%{http_code}' \
  "http://localhost:8080/oauth/v2/authorize?client_id=${WEB_ID}&redirect_uri=http%3A%2F%2Flocalhost%3A5173%2Fauth%2Fcallback&response_type=code&scope=openid&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&state=x" || true)"
[ "$code" = "302" ] && printf '  authorize      302 ok\n' || { printf '  authorize      %s FAILED\n' "$code"; ok=1; }
code="$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8000/health || true)"
[ "$code" = "200" ] && printf '  api health     200 ok\n' || { printf '  api health     %s FAILED\n' "$code"; ok=1; }
code="$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8000/api/meetings || true)"
[ "$code" = "401" ] && printf '  api authz      401 ok (auth enforced)\n' || { printf '  api authz      %s FAILED\n' "$code"; ok=1; }

echo
if [ "$ok" = "0" ]; then
  echo "  Ready — http://localhost:5173"
  echo "  Admin: admin@continuum.dev / ${ZITADEL_ADMIN_PASSWORD:-see src/.env}"
else
  echo "  Some checks failed — inspect: docker compose logs api" >&2
  exit 1
fi
