#!/usr/bin/env bash
# auth-craft-stack — self-contained, one-command deploy for the Auth Craft
# Lambda + Cloudflare gateway stack.
#
#   auth-craft-stack <lambda|gateway|admin|all> [options]
#
# Inputs come from flags OR environment variables (flag wins, then env, then default).
# The caller (e.g. snapshot-commerce's local-deploy.sh) reads its own env file under any
# names it likes and maps them into the LAMBDA_*/CF_* contract when invoking. See
# .env.example for the full contract.

set -euo pipefail

PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
export PKG_ROOT
# shellcheck source=../lib/common.sh
source "$PKG_ROOT/lib/common.sh"
# shellcheck source=../lib/deploy-lambda.sh
source "$PKG_ROOT/lib/deploy-lambda.sh"
# shellcheck source=../lib/create-admin.sh
source "$PKG_ROOT/lib/create-admin.sh"
# shellcheck source=../lib/deploy-gateway.sh
source "$PKG_ROOT/lib/deploy-gateway.sh"

usage() {
  cat <<'EOF'
auth-craft-stack — deploy the Auth Craft Lambda + Cloudflare gateway stack

Usage:
  auth-craft-stack <command> [options]

Commands:
  lambda     Deploy DynamoDB + Lambda (CDK). Prints FunctionUrl, ApiBasePath, table.
             Use --outputs-file to capture CDK outputs as JSON for your own steps.
  gateway    Deploy the 3 gateway workers (system/tenant/customer) via wrangler,
             wiring BACKEND_URL/base-path/secrets from the live CDK outputs. Reads
             the Lambda outputs from CloudFormation if run as a separate invocation.
  admin      Create the super-admin (idempotent; skips if creds unset).
  all        lambda -> admin -> gateway, in one run.
  outputs    Print the live api-stack outputs as JSON (no deploy). Includes the
             derived backendUrl (FunctionUrl+ApiBasePath) — for self-driving callers.

Options (each also reads an env var; the flag wins):
  --stage <dev|staging|prod>   Stage          (env STAGE, default dev; accepts production/development)
  --project <name>             CDK project     (env PROJECT, default "default")
  --app-name <name>            Override app name (env AUTH_CRAFT_APP_NAME; default auth-craft[-project])
  --layer <name>               Layer namespace within the app (env LAYER, default "auth").
                               Resource names are {app}-{env}-{layer}-{component}, so a larger
                               app with other layers (e.g. billing) reuses this CLI per layer.
  --region <region>            AWS region      (env LAMBDA_AWS_REGION, default us-east-1)
  --gateway-secret <v>         env LAMBDA_GATEWAY_SECRET
  --jwt-public-key <v>         env LAMBDA_JWT_PUBLIC_KEY
  --jwt-private-key <v>        env LAMBDA_JWT_PRIVATE_KEY
  --edge-gate-secret <v>       env CF_EDGE_GATE_SECRET
  --cf-account-id <v>          env CLOUDFLARE_ACCOUNT_ID
  --cf-api-token <v>           env CLOUDFLARE_API_TOKEN
  --outputs-file <path>        (lambda) write CDK outputs JSON here for your own steps
  --env-file <path>            Optional: source a file first (flags/env still override)
  --skip-build                 No-op (artifacts are prebuilt in the package)
  -h, --help                   Show this help

All other inputs (token lifetimes, OAuth, SMTP/queue, super-admin creds, route prefixes,
custom domains, internal JWT, …) are read from their LAMBDA_*/CF_* env vars exactly as the
CI workflow names them. See the package's .env.example for the full list.

Examples:
  auth-craft-stack lambda --stage dev
  LAMBDA_JWT_PUBLIC_KEY=... LAMBDA_GATEWAY_SECRET=... CF_EDGE_GATE_SECRET=... \
    auth-craft-stack all --stage dev --region us-east-1
EOF
}

# ── Parse args ────────────────────────────────────────────────────────────────
COMMAND=""
ENV_FILE=""
# Flag overrides captured here, applied AFTER the optional env-file load so flags win.
declare -A FLAG
while [[ $# -gt 0 ]]; do
  case "$1" in
    lambda|gateway|admin|all|outputs) COMMAND="$1"; shift ;;
    --stage)            FLAG[STAGE]="${2:?}"; shift 2 ;;
    --project)          FLAG[PROJECT]="${2:?}"; shift 2 ;;
    --app-name)         FLAG[AUTH_CRAFT_APP_NAME]="${2:?}"; shift 2 ;;
    --layer)            FLAG[LAYER]="${2:?}"; shift 2 ;;
    --region)           FLAG[LAMBDA_AWS_REGION]="${2:?}"; shift 2 ;;
    --gateway-secret)   FLAG[LAMBDA_GATEWAY_SECRET]="${2:?}"; shift 2 ;;
    --jwt-public-key)   FLAG[LAMBDA_JWT_PUBLIC_KEY]="${2:?}"; shift 2 ;;
    --jwt-private-key)  FLAG[LAMBDA_JWT_PRIVATE_KEY]="${2:?}"; shift 2 ;;
    --edge-gate-secret) FLAG[CF_EDGE_GATE_SECRET]="${2:?}"; shift 2 ;;
    --cf-account-id)    FLAG[CLOUDFLARE_ACCOUNT_ID]="${2:?}"; shift 2 ;;
    --cf-api-token)     FLAG[CLOUDFLARE_API_TOKEN]="${2:?}"; shift 2 ;;
    --outputs-file)     OUTPUTS_FILE="${2:?}"; export OUTPUTS_FILE; shift 2 ;;
    --env-file)         ENV_FILE="${2:?}"; shift 2 ;;
    --skip-build)       shift ;;
    -h|--help)          usage; exit 0 ;;
    *) error "Unknown argument: $1"; echo; usage; exit 1 ;;
  esac
done

[[ -n "$COMMAND" ]] || { usage; exit 1; }

# ── Resolve env: flags > exported env > env-file > defaults ───────────────────
# 1) Optional env-file fills only vars not already in the environment.
load_env_file "$ENV_FILE"
# 2) Flags override everything.
for k in "${!FLAG[@]}"; do export "$k=${FLAG[$k]}"; done
# 3) Defaults.
export AWS_REGION="${LAMBDA_AWS_REGION:-${AWS_REGION:-us-east-1}}"
export LAMBDA_AWS_REGION="$AWS_REGION"

derive_naming
# #5 — derive the Lambda's gateway-JWT public hex from the worker's private JWK when
# only the private key is provided, so the verify/sign pair can never drift.
derive_gateway_jwt_public_hex

# ── Dispatch ──────────────────────────────────────────────────────────────────
print_summary() {
  header "Deployment Summary"
  echo "  Stage / Project : $STAGE / $PROJECT"
  echo "  Region          : $AWS_REGION"
  [[ -n "${LAMBDA_FUNCTION_URL:-}" ]] && echo "  Lambda URL      : $LAMBDA_FUNCTION_URL"
  [[ -n "${API_BASE_PATH:-}" ]]       && echo "  API base path   : $API_BASE_PATH"
  [[ -n "${DYNAMODB_TABLE:-}" ]]      && echo "  DynamoDB table  : $DYNAMODB_TABLE"
  [[ -n "${LAMBDA_HEALTH:-}" ]]       && echo "  Lambda health   : $LAMBDA_HEALTH"
  if [[ -n "${GATEWAY_URLS:-}" ]]; then
    echo "  Gateway URLs    :"
    local entry
    for entry in "${GATEWAY_URLS[@]}"; do echo "    - ${entry%%=*}: ${entry#*=}"; done
  fi
  [[ -n "${SUPER_ADMIN_STATUS:-}" ]]  && echo "  Super admin     : $SUPER_ADMIN_STATUS"
  echo
  success "Done."
}

# `outputs` prints machine-readable JSON only (for an orchestrator); no summary.
if [[ "$COMMAND" == "outputs" ]]; then
  print_outputs_json
  exit 0
fi

case "$COMMAND" in
  lambda)  deploy_lambda ;;
  admin)   require_aws_identity; create_admin ;;
  gateway)
    # Standalone gateway deploy needs Lambda outputs — read them from the live stack
    # if not already exported in this process (e.g. snapshot drove `lambda` separately).
    if [[ -z "${LAMBDA_FUNCTION_URL:-}" || -z "${API_BASE_PATH:-}" ]]; then
      require_aws_identity
      api_stack="${RESOURCE_NAME}-api"
      LAMBDA_FUNCTION_URL="$(cfn_output "$api_stack" LambdaFunctionUrl)"
      API_BASE_PATH="$(cfn_output "$api_stack" ApiBasePath)"
      AUTH_SYSTEM_NAME="$(cfn_output "$api_stack" AuthSystemName)"
      export LAMBDA_FUNCTION_URL API_BASE_PATH AUTH_SYSTEM_NAME
      [[ -n "$LAMBDA_FUNCTION_URL" && "$LAMBDA_FUNCTION_URL" != "None" ]] \
        || die "Could not read Lambda outputs from stack $api_stack — deploy the lambda stage first."
    fi
    deploy_gateway ;;
  all)
    deploy_lambda
    require_aws_identity
    create_admin
    deploy_gateway ;;
esac

print_summary
