#!/usr/bin/env bash
#
# First-install secrets bootstrap for the Nquiry stack. Reads required secret
# keys from the live ECS task definition, populates the AWS Secrets Manager
# secret with values from three sources (tfvars / auto-generated / customer
# input), and writes a new AWSCURRENT version.
#
# Companion to apply.sh, bootstrap-db.sh, and deploy-image.sh. Run order for
# a fresh install:
#
#   ../../apply.sh                # provisions infrastructure
#   ../../bootstrap-secrets.sh    # this script — populates secrets
#   ../../bootstrap-db.sh         # schema shim + app migrations
#   ../../deploy-image.sh         # deploys the application image
#
# Usage:
#   cd infrastructure/terraform/environments/<your-env>
#   ../../bootstrap-secrets.sh                     # interactive prompts
#   ../../bootstrap-secrets.sh --values-file s.json  # non-interactive (CI / re-runs)
#   ../../bootstrap-secrets.sh --from-env          # read values from caller's env vars
#   ../../bootstrap-secrets.sh --rotate KEY        # overwrite KEY even if already populated
#
# Idempotent. Keys already present in the secret are skipped unless --rotate is set.

set -euo pipefail

bold=$'\033[1m'
red=$'\033[31m'
dim=$'\033[2m'
reset=$'\033[0m'

target_dir="$PWD"
values_file=""
from_env=false
rotate_keys=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    --values-file) values_file="$2"; shift 2 ;;
    --from-env)    from_env=true; shift ;;
    --rotate)      rotate_keys+=("$2"); shift 2 ;;
    -h|--help)
      sed -n '3,22p' "$0" | sed 's/^# \{0,1\}//'
      exit 0
      ;;
    -*)
      echo "${red}Unknown flag:${reset} $1" >&2
      exit 1
      ;;
    *)
      target_dir="$1"; shift ;;
  esac
done

abort() {
  echo
  echo "${red}Aborted.${reset} No secret values written."
  exit 130
}
trap abort INT

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "${red}$1 not found on PATH.${reset} $2" >&2
    exit 1
  fi
}

require_cmd terraform "Install Terraform >= 1.0 and retry."
require_cmd aws       "Install the AWS CLI v2 and retry."
require_cmd jq        "Install jq and retry."
require_cmd openssl   "Install openssl (used to generate auto-secret values)."

if [[ ! -d "$target_dir" ]]; then
  echo "${red}Not a directory:${reset} $target_dir" >&2
  exit 1
fi
if [[ ! -f "$target_dir/main.tf" ]]; then
  echo "${red}No main.tf in $target_dir.${reset} Run this from a Terraform environment directory." >&2
  exit 1
fi
if [[ ! -f "$target_dir/terraform.tfvars" ]]; then
  echo "${red}No terraform.tfvars in $target_dir.${reset} Required for db_password / redis_auth_token lookup." >&2
  exit 1
fi
if [[ -n "$values_file" && ! -f "$values_file" ]]; then
  echo "${red}--values-file not found:${reset} $values_file" >&2
  exit 1
fi

cd "$target_dir"

# Key classification. Maintained as the customer-deployable surface evolves.
# Discovery is dynamic against the live task def (see `required_keys` below),
# so dropping STRIPE_* from the customer-shared module (NQU-808) just narrows
# the surface from 8 keys to 6 — no script change needed. The classification
# arrays still list STRIPE_* so that internal envs (JE Vectors dev / SaaS) that
# keep enable_stripe=true can still prompt for them via the optional branch.
tfvars_source_keys=(DB_PASSWORD REDIS_AUTH_TOKEN)
auto_gen_keys=(CRON_SECRET)
optional_customer_keys=(NEXT_PUBLIC_SENTRY_DSN STRIPE_SECRET_KEY STRIPE_WEBHOOK_SECRET)
required_customer_keys=(ANTHROPIC_API_KEY RESEND_API_KEY)

# Map task-def secret KEY -> terraform.tfvars variable name (lowercased here).
tfvars_var_for_key() {
  case "$1" in
    DB_PASSWORD)      echo "db_password" ;;
    REDIS_AUTH_TOKEN) echo "redis_auth_token" ;;
    *)                echo "" ;;
  esac
}

in_array() {
  local needle="$1"; shift
  local item
  for item in "$@"; do
    [[ "$item" == "$needle" ]] && return 0
  done
  return 1
}

read_tfvar() {
  # Extract a string value from terraform.tfvars. Naive but sufficient for the
  # simple `key = "value"` lines in customer tfvars files.
  local key="$1"
  grep -E "^${key}[[:space:]]*=" terraform.tfvars \
    | head -1 \
    | sed -E "s/^${key}[[:space:]]*=[[:space:]]*\"(.+)\".*/\1/"
}

# Discover the secret ARN and required keys from the live task def. The task
# def family is name_prefix-app — derive name_prefix from terraform output.
echo "${bold}==> Discovering secret schema${reset}"
cluster=$(terraform output -raw ecs_cluster_name 2>/dev/null || true)
if [[ -z "$cluster" ]]; then
  echo "${red}terraform output ecs_cluster_name not available.${reset} Run apply.sh first." >&2
  exit 1
fi
# task def family = name_prefix-app; derive name_prefix as cluster minus -cluster.
task_def_family="${cluster%-cluster}-app"
region="${AWS_REGION:-$(terraform output -raw aws_region 2>/dev/null || echo us-east-1)}"

task_def_json=$(aws ecs describe-task-definition \
                  --task-definition "$task_def_family" \
                  --region "$region" \
                  --query 'taskDefinition' \
                  --output json)

required_keys=($(echo "$task_def_json" | jq -r '.containerDefinitions[0].secrets[].name' | sort))
secret_arn=$(echo "$task_def_json" \
              | jq -r '.containerDefinitions[0].secrets[0].valueFrom' \
              | sed 's/:[^:]*::$//')  # strip the :KEY:: suffix to get the secret ARN

echo "  Secret ARN: $secret_arn"
echo "  Required keys (${#required_keys[@]}): ${required_keys[*]}"

# Read existing AWSCURRENT, if any, so we can merge instead of clobber.
existing_json="{}"
if aws secretsmanager describe-secret --secret-id "$secret_arn" --region "$region" \
     --query 'VersionIdsToStages' --output json 2>/dev/null \
     | grep -q AWSCURRENT; then
  existing_json=$(aws secretsmanager get-secret-value --secret-id "$secret_arn" --region "$region" \
                    --query SecretString --output text 2>/dev/null \
                    || echo "{}")
fi

# Build new values map.
new_values=$(echo "$existing_json" | jq '.')

# Source lookup for customer-provided keys.
get_customer_value() {
  local key="$1"
  local optional="${2:-false}"

  # --values-file precedence
  if [[ -n "$values_file" ]]; then
    local v
    v=$(jq -r --arg k "$key" '.[$k] // empty' "$values_file")
    if [[ -n "$v" ]]; then echo "$v"; return; fi
  fi

  # --from-env precedence
  if [[ "$from_env" == "true" ]]; then
    local v="${!key:-}"
    if [[ -n "$v" ]]; then echo "$v"; return; fi
  fi

  # Interactive prompt
  local prompt="$key"
  if [[ "$optional" == "true" ]]; then
    prompt="$prompt (optional, leave blank to skip)"
  fi
  read -r -s -p "  $prompt: " v
  echo >&2
  echo "$v"
}

key_has_value() {
  local key="$1"
  local v
  v=$(echo "$new_values" | jq -r --arg k "$key" '.[$k] // empty')
  [[ -n "$v" ]]
}

echo
echo "${bold}==> Populating values${reset}"

for key in "${required_keys[@]}"; do
  if key_has_value "$key" && ! in_array "$key" "${rotate_keys[@]+"${rotate_keys[@]}"}"; then
    echo "  $key: ${dim}already set, skipping${reset}"
    continue
  fi

  value=""
  if in_array "$key" "${tfvars_source_keys[@]}"; then
    var_name=$(tfvars_var_for_key "$key")
    value=$(read_tfvar "$var_name")
    if [[ -z "$value" ]]; then
      echo "${red}  $key: tfvars var '$var_name' not found or empty.${reset}" >&2
      exit 1
    fi
    echo "  $key: ${dim}read from terraform.tfvars ($var_name)${reset}"
  elif in_array "$key" "${auto_gen_keys[@]}"; then
    value=$(openssl rand -hex 32)
    echo "  $key: ${dim}auto-generated${reset}"
  elif in_array "$key" "${required_customer_keys[@]}"; then
    value=$(get_customer_value "$key" false)
    if [[ -z "$value" ]]; then
      echo "${red}  $key: required but no value provided.${reset}" >&2
      exit 1
    fi
  elif in_array "$key" "${optional_customer_keys[@]}"; then
    value=$(get_customer_value "$key" true)
    if [[ -z "$value" ]]; then
      value="DISABLED-by-bootstrap"
      echo "  $key: ${dim}empty, writing placeholder 'DISABLED-by-bootstrap'${reset}"
    fi
  else
    # Unknown key — treat as required customer-provided. Future task-def
    # additions will hit this branch and prompt by default.
    echo "  $key: ${dim}unknown classification, prompting${reset}"
    value=$(get_customer_value "$key" false)
    if [[ -z "$value" ]]; then
      echo "${red}  $key: required but no value provided.${reset}" >&2
      exit 1
    fi
  fi

  new_values=$(echo "$new_values" | jq --arg k "$key" --arg v "$value" '. + {($k): $v}')
done

# Validate every required key has a non-empty value in the final map.
missing=()
for key in "${required_keys[@]}"; do
  v=$(echo "$new_values" | jq -r --arg k "$key" '.[$k] // empty')
  [[ -z "$v" ]] && missing+=("$key")
done
if [[ ${#missing[@]} -gt 0 ]]; then
  echo "${red}Missing values for required keys:${reset} ${missing[*]}" >&2
  exit 1
fi

# Write the new version. Secrets Manager keeps prior versions for rollback.
echo
echo "${bold}==> Writing AWSCURRENT to Secrets Manager${reset}"
echo "$new_values" | aws secretsmanager put-secret-value \
  --secret-id "$secret_arn" \
  --secret-string file:///dev/stdin \
  --region "$region" \
  --query 'VersionStages' \
  --output text

echo
echo "${bold}Bootstrap complete.${reset} ${#required_keys[@]} key(s) written to $secret_arn."
echo "Next: ${dim}../../bootstrap-db.sh${reset} to apply schema + run migrations, then ${dim}../../deploy-image.sh${reset}."
