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

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
AUTHENTIK_COMPOSE="$SCRIPT_DIR/authentik/compose.yml"
VAULT_COMPOSE="$SCRIPT_DIR/ai-gateway/compose.yml"

usage() {
  cat <<'EOF'
Usage: ./homelab-emergency <command> [arguments]

Bootstrap and configuration:
  init-secrets
  identity-bootstrap

Recovery:
  status
  identity-recovery [username]                 default: liooil
  identity-set-password [username]             default: liooil
  identity-reset-2fa [username]                 delete all TOTP devices; default: liooil
  identity-reset-passkeys [username]            delete all Passkeys; default: liooil
  vault-list [owner-sub]
  vault-audit [limit]
  vault-verify <credential-id>
  vault-delete <credential-id>
  vault-reassign <credential-id> <issuer> <sub> <username>
  vault-backup <destination.db>
  vault-rotate-key <new-key-file>

The script never prints stored provider credentials.
EOF
}

require_command() {
  command -v "$1" >/dev/null 2>&1 || {
    echo "Required command not found: $1" >&2
    exit 1
  }
}

ensure_parent() {
  mkdir -p -- "$(dirname -- "$1")"
}

generate_base64_key() {
  local destination=$1
  if [[ -e "$destination" ]]; then
    echo "Keeping existing secret: $destination"
    return
  fi
  ensure_parent "$destination"
  chmod 700 -- "$(dirname -- "$destination")"
  openssl rand 32 | base64 -w 0 >"$destination"
  chmod 600 -- "$destination"
  echo "Created: $destination"
}


vault_exec() {
  if docker ps --format '{{.Names}}' | grep -Fxq ai-gateway; then
    docker compose -f "$VAULT_COMPOSE" exec -T ai-gateway node /app/cli.mjs "$@"
  else
    docker compose -f "$VAULT_COMPOSE" run --rm --no-deps ai-gateway node /app/cli.mjs "$@"
  fi
}

identity_reset_authenticators() {
  local username=$1
  local model_module=$2
  local model_name=$3
  docker compose -f "$AUTHENTIK_COMPOSE" exec -T -e TARGET_USERNAME="$username" authentik \
    ak shell -c "import os; from authentik.core.models import User; from $model_module import $model_name; user=User.objects.get(username=os.environ['TARGET_USERNAME']); print($model_name.objects.filter(user=user).delete()[0])"
}

identity_audit() {
  local action=$1
  local username=$2
  local encoded
  encoded=$(printf '%s' "$username" | base64 -w 0)
  mkdir -p -- "$SCRIPT_DIR/site/data"
  printf '{"createdAt":"%s","actor":"homelab-emergency","action":"%s","target":"base64:%s","detail":"{}"}\n' \
    "$(date --iso-8601=seconds)" "$action" "$encoded" >>"$SCRIPT_DIR/site/data/identity-audit.jsonl"
  chmod 600 -- "$SCRIPT_DIR/site/data/identity-audit.jsonl"
}


command_name=${1:-}
shift || true

case "$command_name" in
  init-secrets)
    require_command openssl
    require_command base64
    umask 077
    generate_base64_key "$SCRIPT_DIR/ai-gateway/secrets/vault_master_key"
    generate_base64_key "$SCRIPT_DIR/ai-gateway/secrets/portal_gateway_hmac"
    ;;
  identity-bootstrap)
    docker compose -f "$AUTHENTIK_COMPOSE" exec -T authentik \
      ak shell -c "exec(open('/bootstrap/portal_identity.py').read())"
    docker compose -f "$SCRIPT_DIR/site/compose.yml" up -d --force-recreate xiteng-site
    ;;
  identity-recovery)
    username=${1:-liooil}
    docker compose -f "$AUTHENTIK_COMPOSE" exec -T authentik ak create_recovery_key 60 "$username" -v 0
    ;;
  identity-set-password)
    username=${1:-liooil}
    docker compose -f "$AUTHENTIK_COMPOSE" exec authentik ak changepassword "$username"
    ;;
  identity-reset-2fa)
    username=${1:-liooil}
    read -r -p "Delete every TOTP device for $username? [y/N] " confirm
    [[ "$confirm" == "y" || "$confirm" == "Y" ]] || exit 1
    identity_reset_authenticators "$username" "authentik.stages.authenticator_totp.models" "TOTPDevice"
    identity_audit "emergency.totp.reset" "$username"
    ;;
  identity-reset-passkeys)
    username=${1:-liooil}
    read -r -p "Delete every Passkey for $username? [y/N] " confirm
    [[ "$confirm" == "y" || "$confirm" == "Y" ]] || exit 1
    identity_reset_authenticators "$username" "authentik.stages.authenticator_webauthn.models" "WebAuthnDevice"
    identity_audit "emergency.passkeys.reset" "$username"
    ;;
  status)
    docker compose -f "$AUTHENTIK_COMPOSE" ps authentik worker postgres redis
    docker compose -f "$VAULT_COMPOSE" ps
    ;;
  vault-list)
    vault_exec list "$@"
    ;;
  vault-audit)
    vault_exec audit "$@"
    ;;
  vault-verify)
    vault_exec verify "$@"
    ;;
  vault-delete)
    credential_id=${1:-}
    if [[ -z "$credential_id" ]]; then
      echo "Credential ID is required" >&2
      exit 1
    fi
    read -r -p "Permanently delete credential $credential_id? [y/N] " confirm
    [[ "$confirm" == "y" || "$confirm" == "Y" ]] || exit 1
    vault_exec delete "$credential_id"
    ;;
  vault-reassign)
    if [[ $# -lt 4 ]]; then
      echo "credential-id, issuer, sub and username are required" >&2
      exit 1
    fi
    vault_exec reassign "$@"
    ;;
  vault-backup)
    destination=${1:-}
    if [[ -z "$destination" ]]; then
      echo "Destination DB path is required" >&2
      exit 1
    fi
    absolute_destination=$(realpath -m -- "$destination")
    case "$absolute_destination" in
      "$SCRIPT_DIR/ai-gateway/data/"*) ;;
      *)
        echo "Vault CLI backup target must be under ai-gateway/data so the container can write it." >&2
        echo "Suggested: ai-gateway/data/backups/vault-$(date +%Y%m%d-%H%M%S).db" >&2
        exit 1
        ;;
    esac
    relative_destination=/data/${absolute_destination#"$SCRIPT_DIR/ai-gateway/data/"}
    ensure_parent "$absolute_destination"
    vault_exec backup "$relative_destination"
    chmod 600 -- "$absolute_destination"
    echo "Vault backup written to $absolute_destination"
    ;;
  vault-rotate-key)
    new_key_file=${1:-}
    if [[ -z "$new_key_file" || ! -f "$new_key_file" ]]; then
      echo "A readable new key file is required" >&2
      exit 1
    fi
    read -r -p "Rewrap every Vault data key and then replace vault_master_key? [y/N] " confirm
    [[ "$confirm" == "y" || "$confirm" == "Y" ]] || exit 1
    temporary_key="$SCRIPT_DIR/ai-gateway/secrets/vault_master_key.next"
    cp -- "$new_key_file" "$temporary_key"
    chmod 600 -- "$temporary_key"
    vault_exec rotate-master "/run/secrets/vault_master_key.next"
    mv -- "$temporary_key" "$SCRIPT_DIR/ai-gateway/secrets/vault_master_key"
    chmod 600 -- "$SCRIPT_DIR/ai-gateway/secrets/vault_master_key"
    docker compose -f "$VAULT_COMPOSE" restart ai-gateway
    echo "Vault master key rotated and service restarted. Back up the new key now."
    ;;
  ""|-h|--help|help)
    usage
    ;;
  *)
    echo "Unknown command: $command_name" >&2
    usage >&2
    exit 1
    ;;
esac
