#!/usr/bin/env bash
# Idempotently (re)provision the OpenBao objects mcpd's secret backend needs.
#
# WHY THIS EXISTS: the KV mount + `app-mcpd` ACL policy + `app-mcpd-role` token
# role were hand-created and NOT captured anywhere, so an OpenBao rebuild/re-init
# silently dropped the policy — leaving mcpd with valid-looking tokens that
# granted nothing (403 on every secret write). This script makes that
# provisioning reproducible: run it after any OpenBao (re)init to restore mcpd's
# access. It is safe to run repeatedly.
#
# It mirrors mcpd's own wizard (src/shared/src/vault/policy.ts) — keep them in sync.
#
# Usage:
#   scripts/provision-openbao.sh [--seed] [--dry-run]
#     --seed     also mint a fresh role token and write it into the `bao-creds`
#                secret (mcpctl --direct ... --force), then restart mcpd.
#     --dry-run  print what would change; make no writes.
#
# Env (with sensible homelab defaults):
#   BAO_ADDR=https://bao.ad.itaz.eu  MOUNT=secret  PREFIX=mcpctl  ROLE=app-mcpd-role
#   POLICY=app-mcpd  PERIOD=2592000  KUBE_CONTEXT=worker0-k8s0
#   BAO_TOKEN (admin/root) — else read from the openbao-init-credentials secret.
set -euo pipefail

BAO_ADDR="${BAO_ADDR:-https://bao.ad.itaz.eu}"
MOUNT="${MOUNT:-secret}"
PREFIX="${PREFIX:-mcpctl}"
ROLE="${ROLE:-app-mcpd-role}"
POLICY="${POLICY:-app-mcpd}"
PERIOD="${PERIOD:-2592000}"            # 30d periodic token → renews forever
KUBE_CONTEXT="${KUBE_CONTEXT:-worker0-k8s0}"
MCPCTL_NS="${MCPCTL_NS:-mcpctl}"

SEED=false; DRY=false
for a in "$@"; do case "$a" in --seed) SEED=true;; --dry-run) DRY=true;; esac; done

say()  { printf '\n\033[1;36m>>> %s\033[0m\n' "$*"; }
ok()   { printf '\033[1;32m  ✓ %s\033[0m\n' "$*"; }
die()  { printf '\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; }

# ── Admin token ──
if [ -z "${BAO_TOKEN:-}" ]; then
  BAO_TOKEN="$(kubectl --context "$KUBE_CONTEXT" -n openbao get secret openbao-init-credentials -o jsonpath='{.data.root-token}' 2>/dev/null | base64 -d || true)"
fi
[ -n "${BAO_TOKEN:-}" ] || die "no BAO_TOKEN and could not read openbao-init-credentials root-token"
bao() { curl -fsS -m 15 -H "X-Vault-Token: $BAO_TOKEN" "$@"; }
bao -o /dev/null "$BAO_ADDR/v1/auth/token/lookup-self" || die "admin token rejected by $BAO_ADDR (OpenBao may have been re-initialized — update the init-credentials secret)"
ok "admin token valid"

# Canonical policy HCL — must match src/shared/src/vault/policy.ts
read -r -d '' POLICY_HCL <<EOF || true
path "$MOUNT/data/$PREFIX/*"      { capabilities = ["create", "read", "update"] }
path "$MOUNT/metadata/$PREFIX/*"  { capabilities = ["list", "delete"] }
path "$MOUNT/metadata/$PREFIX/"   { capabilities = ["list"] }
path "auth/token/create/$ROLE" { capabilities = ["create", "update"] }
path "auth/token/revoke-accessor"     { capabilities = ["update"] }
path "auth/token/lookup-self"         { capabilities = ["read"] }
EOF

say "Plan: mount=$MOUNT (kv2)  prefix=$PREFIX  policy=$POLICY  role=$ROLE (periodic ${PERIOD}s)  seed=$SEED  dry-run=$DRY"
if [ "$DRY" = true ]; then printf '%s\n' "$POLICY_HCL"; say "dry-run: no writes."; exit 0; fi

# ── 1. KV v2 mount ──
say "1. Ensure KV v2 mount '$MOUNT/'"
if bao -o /dev/null "$BAO_ADDR/v1/sys/mounts/$MOUNT" 2>/dev/null; then
  ok "mount '$MOUNT/' already exists"
else
  bao -X POST "$BAO_ADDR/v1/sys/mounts/$MOUNT" -d '{"type":"kv","options":{"version":"2"}}' >/dev/null
  ok "created KV v2 mount '$MOUNT/'"
fi

# ── 2. Policy ──
say "2. Write ACL policy '$POLICY'"
bao -X PUT "$BAO_ADDR/v1/sys/policies/acl/$POLICY" \
  -d "$(python3 -c 'import json,sys; print(json.dumps({"policy": sys.stdin.read()}))' <<<"$POLICY_HCL")" >/dev/null
ok "policy '$POLICY' written ($(printf '%s' "$POLICY_HCL" | wc -l) rules)"

# ── 3. Periodic token role ──
say "3. Write token role '$ROLE' (periodic, renewable)"
bao -X POST "$BAO_ADDR/v1/auth/token/roles/$ROLE" \
  -d "$(python3 -c 'import json,sys; print(json.dumps({"allowed_policies":[sys.argv[1]],"period":int(sys.argv[2]),"renewable":True,"token_type":"service","orphan":False}))' "$POLICY" "$PERIOD")" >/dev/null
ok "role '$ROLE' written (period=${PERIOD}s, allowed_policies=[$POLICY])"

# ── 4. Optional: seed bao-creds + restart mcpd ──
if [ "$SEED" = true ]; then
  say "4. Mint token + seed bao-creds + restart mcpd"
  NEW_TOK="$(bao -X POST "$BAO_ADDR/v1/auth/token/create/$ROLE" | python3 -c 'import sys,json; print(json.load(sys.stdin)["auth"]["client_token"])')"
  [ -n "$NEW_TOK" ] || die "failed to mint role token"
  mcpctl --direct create secret bao-creds --data "token=$NEW_TOK" --force >/dev/null
  ok "bao-creds updated"
  kubectl --context "$KUBE_CONTEXT" -n "$MCPCTL_NS" rollout restart deployment/mcpd >/dev/null
  kubectl --context "$KUBE_CONTEXT" -n "$MCPCTL_NS" rollout status deployment/mcpd --timeout=3m
  ok "mcpd restarted — verify with: mcpctl status  (Secrets line should be ✓)"
fi

say "OpenBao provisioning complete."
