#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"

usage() {
  printf 'usage: sync-convex-env <dev|staging|production|prod>\n' >&2
  exit 2
}

ENVIRONMENT="${1:-}"
[[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging || "$ENVIRONMENT" == production || "$ENVIRONMENT" == prod ]] || usage
INFISICAL_ENV="$ENVIRONMENT"
case "$INFISICAL_ENV" in
  production) INFISICAL_ENV=prod ;;
esac

if [[ "${2:-}" != "--from-current-env" ]]; then
  ENV_FILE="$(mktemp "${TMPDIR:-/tmp}/spoon-convex-env.XXXXXX.env")"
  trap 'rm -f "$ENV_FILE"' EXIT INT TERM HUP
  sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"
  exec bun dotenv -e "$ENV_FILE" -- "$0" "$ENVIRONMENT" --from-current-env
fi

info() { printf '▶ %s\n' "$*"; }
warn() { printf 'Warning: %s\n' "$*" >&2; }
STATE_FILE="$ROOT_DIR/.local/$INFISICAL_ENV.generated.env"
MISSING_REQUIRED=0
MISSING_REQUIRED_NAMES=()

convex_env_names() {
  (cd "$ROOT_DIR/packages/backend" && bun convex env list) 2>/dev/null \
    | sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=.*/\1/p'
}

convex_env_has() {
  local name="$1"
  printf '%s\n' "$CURRENT_CONVEX_ENV_NAMES" | grep -qx "$name"
}

set_convex_env() {
  local name="$1"
  local value="${!name-}"
  local tmp

  if [[ -z "$value" ]]; then
    warn "Skipping $name; it is not present in exported $ENVIRONMENT environment."
    return 0
  fi

  tmp="$(mktemp "${TMPDIR:-/tmp}/spoon-convex-value.XXXXXX")"
  printf '%s' "$value" > "$tmp"
  (cd "$ROOT_DIR/packages/backend" && bun convex env set "$name" --from-file "$tmp" >/dev/null)
  rm -f "$tmp"
  printf '  synced %s\n' "$name"
}

require_exported_env() {
  local name="$1"
  if [[ -z "${!name-}" ]]; then
    printf 'Error: required %s is missing from exported %s environment.\n' "$name" "$ENVIRONMENT" >&2
    MISSING_REQUIRED=1
    MISSING_REQUIRED_NAMES+=("$name")
  fi
}

require_available_env() {
  local name="$1"
  if [[ -n "${!name-}" ]] || convex_env_has "$name"; then
    return 0
  fi
  printf 'Error: required %s is missing from exported %s environment and Convex env.\n' "$name" "$ENVIRONMENT" >&2
  MISSING_REQUIRED=1
  MISSING_REQUIRED_NAMES+=("$name")
}

require_non_dev_env() {
  [[ "$ENVIRONMENT" != dev ]] || return 0
  for name in \
    JWT_PRIVATE_KEY \
    JWKS \
    AUTH_AUTHENTIK_ID \
    AUTH_AUTHENTIK_SECRET \
    AUTH_AUTHENTIK_ISSUER \
    AUTH_GITHUB_ID \
    AUTH_GITHUB_SECRET \
    SPOON_ENCRYPTION_KEY
  do
    require_available_env "$name"
  done
  if [[ -z "${SITE_URL:-}" && -z "${NEXT_PUBLIC_SITE_URL:-}" ]]; then
    if convex_env_has SITE_URL; then
      return 0
    fi
    printf 'Error: required SITE_URL or NEXT_PUBLIC_SITE_URL is missing from exported %s environment and SITE_URL is missing from Convex env.\n' "$ENVIRONMENT" >&2
    MISSING_REQUIRED=1
    MISSING_REQUIRED_NAMES+=("SITE_URL")
  fi
}

finish_required_env_check() {
  [[ "$MISSING_REQUIRED" -eq 0 ]] && return 0
  printf '\nConvex env sync completed for available values, but required values are still missing: %s\n' "${MISSING_REQUIRED_NAMES[*]}" >&2
  printf '\nGenerate missing Convex Auth keys with:\n  bun auth:keys\n\nStore missing values in Infisical/Gitea, then rerun sync.\n' >&2
  exit 1
}

set_literal_convex_env() {
  local name="$1"
  local value="$2"
  local tmp

  tmp="$(mktemp "${TMPDIR:-/tmp}/spoon-convex-value.XXXXXX")"
  printf '%s' "$value" > "$tmp"
  (cd "$ROOT_DIR/packages/backend" && bun convex env set "$name" --from-file "$tmp" >/dev/null)
  rm -f "$tmp"
  printf '  synced %s\n' "$name"
}

upsert_state() {
  local key="$1" value="$2" tmp escaped
  mkdir -p "$ROOT_DIR/.local"
  tmp="$(mktemp "${TMPDIR:-/tmp}/spoon-state.XXXXXX.env")"
  [ ! -f "$STATE_FILE" ] || grep -v "^${key}=" "$STATE_FILE" > "$tmp" || true
  escaped="$(printf '%s' "$value" | sed "s/'/'\\\\''/g")"
  printf "%s='%s'\n" "$key" "$escaped" >> "$tmp"
  mv "$tmp" "$STATE_FILE"
}

generate_secret() {
  node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"
}

sync_generated_dev_auth_keys() {
  [[ "$ENVIRONMENT" == dev ]] || return 0
  if convex_env_has JWT_PRIVATE_KEY && convex_env_has JWKS; then
    return 0
  fi

  info "Generating local Convex Auth signing keys"
  local auth_keys jwt jwks
  auth_keys="$(node "$ROOT_DIR/scripts/generate-convex-auth-keys.mjs")"
  jwt="$(printf '%s\n' "$auth_keys" | sed -n 's/^JWT_PRIVATE_KEY="\(.*\)"$/\1/p')"
  jwks="$(printf '%s\n' "$auth_keys" | sed -n 's/^JWKS=//p')"
  [[ -n "$jwt" && -n "$jwks" ]] || {
    printf 'sync-convex-env: failed to generate Convex Auth keys.\n' >&2
    exit 1
  }

  set_literal_convex_env JWT_PRIVATE_KEY "$jwt"
  set_literal_convex_env JWKS "$jwks"
}

sync_encryption_key() {
  if [[ -n "${SPOON_ENCRYPTION_KEY:-}" ]]; then
    set_convex_env SPOON_ENCRYPTION_KEY
    return 0
  fi
  if convex_env_has SPOON_ENCRYPTION_KEY; then
    return 0
  fi

  info "Generating $ENVIRONMENT Spoon encryption key"
  local encryption_key
  encryption_key="$(generate_secret)"
  [[ -n "$encryption_key" ]] || {
    printf 'sync-convex-env: failed to generate Spoon encryption key.\n' >&2
    exit 1
  }
  upsert_state SPOON_ENCRYPTION_KEY "$encryption_key"
  export SPOON_ENCRYPTION_KEY="$encryption_key"
  set_literal_convex_env SPOON_ENCRYPTION_KEY "$encryption_key"
}

sync_generated_dev_worker_token() {
  [[ "$ENVIRONMENT" == dev ]] || return 0
  if [[ -n "${SPOON_WORKER_TOKEN:-}" ]]; then
    set_convex_env SPOON_WORKER_TOKEN
    return 0
  fi
  if convex_env_has SPOON_WORKER_TOKEN; then
    return 0
  fi

  info "Generating local Spoon worker token"
  local worker_token
  worker_token="$(generate_secret)"
  [[ -n "$worker_token" ]] || {
    printf 'sync-convex-env: failed to generate Spoon worker token.\n' >&2
    exit 1
  }
  upsert_state SPOON_WORKER_TOKEN "$worker_token"
  export SPOON_WORKER_TOKEN="$worker_token"
  set_literal_convex_env SPOON_WORKER_TOKEN "$worker_token"
}

sync_site_url() {
  if [[ -n "${SITE_URL:-}" ]]; then
    set_convex_env SITE_URL
    return 0
  fi

  if [[ "$ENVIRONMENT" == dev ]]; then
    set_literal_convex_env SITE_URL "http://localhost:3000"
    return 0
  fi

  if [[ -n "${NEXT_PUBLIC_SITE_URL:-}" ]]; then
    set_literal_convex_env SITE_URL "$NEXT_PUBLIC_SITE_URL"
  else
    warn "Skipping SITE_URL; neither SITE_URL nor NEXT_PUBLIC_SITE_URL is present."
  fi
}

CURRENT_CONVEX_ENV_NAMES="$(convex_env_names || true)"

info "Syncing $ENVIRONMENT environment variables into Convex"
sync_generated_dev_auth_keys
sync_encryption_key
sync_generated_dev_worker_token
require_non_dev_env

for name in \
  JWT_PRIVATE_KEY \
  JWKS \
  AUTH_AUTHENTIK_ID \
  AUTH_AUTHENTIK_SECRET \
  AUTH_AUTHENTIK_ISSUER \
  AUTH_GITHUB_ID \
  AUTH_GITHUB_SECRET \
  GITHUB_APP_ID \
  GITHUB_APP_CLIENT_ID \
  GITHUB_APP_CLIENT_SECRET \
  GITHUB_APP_PRIVATE_KEY \
  GITHUB_APP_WEBHOOK_SECRET \
  GITHUB_APP_SLUG \
  GITHUB_APP_INSTALLATION_ID \
  GITHUB_APP_OWNER \
  SPOON_WORKER_TOKEN \
  USESEND_API_KEY \
  USESEND_URL \
  USESEND_FROM_EMAIL
do
  if [[ "$ENVIRONMENT" == dev && ( "$name" == JWT_PRIVATE_KEY || "$name" == JWKS ) && -z "${!name-}" ]]; then
    continue
  fi
  set_convex_env "$name"
done

sync_site_url

finish_required_env_check

info "Convex environment sync complete"
