#!/usr/bin/env bash

set -euo pipefail

usage() {
  cat <<'USAGE'
Usage: key-rotation.sh --authority-url URL --api-key TOKEN --key-id ID --key-path PATH [options]

Required flags:
  -u, --authority-url   Base Authority URL (e.g. https://authority.example.com)
  -k, --api-key         Bootstrap API key (x-stellaops-bootstrap-key header)
  -i, --key-id          Identifier (kid) for the new signing key
  -p, --key-path        Path (relative to Authority content root or absolute) where the PEM key lives

Optional flags:
  -s, --source          Key source loader identifier (default: file)
  -a, --algorithm       Signing algorithm (default: ES256)
      --provider        Preferred crypto provider name
  -m, --meta key=value  Additional metadata entries for the rotation record (repeatable)
      --dry-run         Print the JSON payload instead of invoking the API
  -h, --help            Show this help

Environment fallbacks:
  AUTHORITY_URL, AUTHORITY_BOOTSTRAP_KEY, AUTHORITY_KEY_SOURCE, AUTHORITY_KEY_PROVIDER

Example:
  AUTHORITY_BOOTSTRAP_KEY=$(cat key.txt) \\
    ./key-rotation.sh -u https://authority.local \\
      -i authority-signing-2025 \\
      -p ../certificates/authority-signing-2025.pem \\
      -m rotatedBy=pipeline -m ticket=OPS-1234
USAGE
}

require_python() {
  if command -v python3 >/dev/null 2>&1; then
    PYTHON_BIN=python3
  elif command -v python >/dev/null 2>&1; then
    PYTHON_BIN=python
  else
    echo "error: python3 (or python) is required for JSON encoding" >&2
    exit 1
  fi
}

json_quote() {
  "$PYTHON_BIN" - "$1" <<'PY'
import json, sys
print(json.dumps(sys.argv[1]))
PY
}

AUTHORITY_URL="${AUTHORITY_URL:-}"
API_KEY="${AUTHORITY_BOOTSTRAP_KEY:-}"
KEY_ID=""
KEY_PATH=""
SOURCE="${AUTHORITY_KEY_SOURCE:-file}"
ALGORITHM="ES256"
PROVIDER="${AUTHORITY_KEY_PROVIDER:-}"
DRY_RUN=false
declare -a METADATA=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    -u|--authority-url)
      AUTHORITY_URL="$2"
      shift 2
      ;;
    -k|--api-key)
      API_KEY="$2"
      shift 2
      ;;
    -i|--key-id)
      KEY_ID="$2"
      shift 2
      ;;
    -p|--key-path)
      KEY_PATH="$2"
      shift 2
      ;;
    -s|--source)
      SOURCE="$2"
      shift 2
      ;;
    -a|--algorithm)
      ALGORITHM="$2"
      shift 2
      ;;
    --provider)
      PROVIDER="$2"
      shift 2
      ;;
    -m|--meta)
      METADATA+=("$2")
      shift 2
      ;;
    --dry-run)
      DRY_RUN=true
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown option: $1" >&2
      usage
      exit 1
      ;;
  esac
done

if [[ -z "$AUTHORITY_URL" || -z "$API_KEY" || -z "$KEY_ID" || -z "$KEY_PATH" ]]; then
  echo "error: missing required arguments" >&2
  usage
  exit 1
fi

case "$AUTHORITY_URL" in
  http://*|https://*) ;;
  *)
    echo "error: --authority-url must include scheme (http/https)" >&2
    exit 1
    ;;
esac

require_python

payload="{"
payload+="\"keyId\":$(json_quote "$KEY_ID"),"
payload+="\"location\":$(json_quote "$KEY_PATH"),"
payload+="\"source\":$(json_quote "$SOURCE"),"
payload+="\"algorithm\":$(json_quote "$ALGORITHM"),"
if [[ -n "$PROVIDER" ]]; then
  payload+="\"provider\":$(json_quote "$PROVIDER"),"
fi

if [[ ${#METADATA[@]} -gt 0 ]]; then
  payload+="\"metadata\":{"
  for entry in "${METADATA[@]}"; do
    if [[ "$entry" != *=* ]]; then
      echo "warning: ignoring metadata entry '$entry' (expected key=value)" >&2
      continue
    fi
    key="${entry%%=*}"
    value="${entry#*=}"
    payload+="$(json_quote "$key"):$(json_quote "$value"),"
  done
  if [[ "${payload: -1}" == "," ]]; then
    payload="${payload::-1}"
  fi
  payload+="},"
fi

if [[ "${payload: -1}" == "," ]]; then
  payload="${payload::-1}"
fi
payload+="}"

if [[ "$DRY_RUN" == true ]]; then
  echo "# Dry run payload:"
  echo "$payload"
  exit 0
fi

tmp_response="$(mktemp)"
cleanup() { rm -f "$tmp_response"; }
trap cleanup EXIT

http_code=$(curl -sS -o "$tmp_response" -w "%{http_code}" \
  -X POST "${AUTHORITY_URL%/}/internal/signing/rotate" \
  -H "Content-Type: application/json" \
  -H "x-stellaops-bootstrap-key: $API_KEY" \
  --data "$payload")

if [[ "$http_code" != "200" && "$http_code" != "201" ]]; then
  echo "error: rotation API returned HTTP $http_code" >&2
  cat "$tmp_response" >&2 || true
  exit 1
fi

echo "Rotation request accepted (HTTP $http_code). Response:"
cat "$tmp_response"

echo
echo "Fetching JWKS to confirm active key..."
curl -sS "${AUTHORITY_URL%/}/jwks" || true
echo
echo "Done. Remember to update authority.yaml with the new key metadata to keep restarts consistent."
