#!/usr/bin/env bash
#MISE description="Токен Terraform для Vault, проверка политик, отзыв root-токена (нужен ROOT_TOKEN)"
set -euo pipefail

MOUNT=$VAULT_MOUNT
ENV_FILE=.env
KEYS_FILE=vault_keys.txt
REVOKE_ROOT=false
[[ "${1:-}" == "--revoke-root" ]] && REVOKE_ROOT=true

bao_as() {
  local token=$1
  shift
  docker compose exec -T -e BAO_TOKEN="$token" vault bao "$@"
}

step() { printf '\n== %s\n' "$*"; }
ok() { printf '   ok: %s\n' "$*"; }
fail() {
  printf '   ОШИБКА: %s\n' "$*" >&2
  exit 1
}

ROOT_TOKEN=${ROOT_TOKEN:-$(sed -n 's/^Initial Root Token: *//p' "$KEYS_FILE" 2>/dev/null || true)}
[[ -n "$ROOT_TOKEN" ]] || fail "нет root-токена: задайте ROOT_TOKEN или положите $KEYS_FILE"
bao_as "$ROOT_TOKEN" token lookup >/dev/null 2>&1 || fail "root-токен не принят (отозван или Vault запечатан)"

step "Шаг 1. Токен для Terraform"
TF_TOKEN=${VAULT_TOKEN:-}
if [[ -n "$TF_TOKEN" ]] && bao_as "$TF_TOKEN" token lookup -format=json 2>/dev/null |
  jq -e '.data.policies | index("terraform")' >/dev/null; then
  ok "в $ENV_FILE уже есть рабочий токен, новый не создаю"
else
  TF_TOKEN=$(bao_as "$ROOT_TOKEN" token create -orphan -policy=terraform -period=720h \
    -display-name=terraform -format=json | jq -r .auth.client_token)
  [[ -n "$TF_TOKEN" && "$TF_TOKEN" != null ]] || fail "токен не создан"
  sed -i '/^VAULT_TOKEN=/d' "$ENV_FILE"
  printf 'VAULT_TOKEN=%s\n' "$TF_TOKEN" >>"$ENV_FILE"
  ok "токен создан и записан в $ENV_FILE как VAULT_TOKEN"
fi
bao_as "$TF_TOKEN" token lookup -format=json |
  jq -r '.data | "   политики: \(.policies | join(", ")); orphan: \(.orphan); period: \(.period)s"'

step "Шаг 2. terraform plan в vault/ с новым токеном"
set +e
TF_VAR_vault_token=$TF_TOKEN terraform -chdir=vault plan -input=false -no-color -detailed-exitcode >/tmp/vault-plan.$$ 2>&1
PLAN_EXIT=$?
set -e
case $PLAN_EXIT in
0) ok "No changes: политики terraform хватает" ;;
2)
  grep -E '^  # |^Plan:' /tmp/vault-plan.$$ || true
  rm -f /tmp/vault-plan.$$
  fail "plan хочет что-то изменить, прочитайте его: terraform -chdir=vault plan"
  ;;
*)
  grep -vE 'Refreshing state' /tmp/vault-plan.$$ | tail -30
  rm -f /tmp/vault-plan.$$
  fail "plan упал, скорее всего в политике terraform не хватает пути из ошибки выше"
  ;;
esac
rm -f /tmp/vault-plan.$$

step "Шаг 3. Границы сервисов через AppRole"
ROLES_JSON=$(terraform -chdir=vault output -json approle_role_ids)
mapfile -t SERVICES < <(jq -r 'keys[]' <<<"$ROLES_JSON")

cleanup() {
  for svc in "${SERVICES[@]}"; do
    bao_as "$ROOT_TOKEN" kv metadata delete -mount="$MOUNT" "$svc/bootstrap-check" >/dev/null 2>&1 || true
  done
}
trap cleanup EXIT

for svc in "${SERVICES[@]}"; do
  bao_as "$ROOT_TOKEN" kv put -mount="$MOUNT" "$svc/bootstrap-check" ok=1 >/dev/null
done

for svc in "${SERVICES[@]}"; do
  role_id=$(jq -r --arg s "$svc" '.[$s]' <<<"$ROLES_JSON")
  secret_id=$(bao_as "$ROOT_TOKEN" write -f -field=secret_id "auth/approle/role/$svc/secret-id")
  svc_token=$(bao_as "$ROOT_TOKEN" write -field=token auth/approle/login role_id="$role_id" secret_id="$secret_id")

  for target in "${SERVICES[@]}"; do
    set +e
    out=$(bao_as "$svc_token" kv get -mount="$MOUNT" "$target/bootstrap-check" 2>&1)
    rc=$?
    set -e
    if [[ "$target" == "$svc" ]]; then
      [[ $rc -eq 0 ]] || fail "$svc не читает свой путь $MOUNT/$target: $out"
      ok "$svc читает $MOUNT/$target/*"
    else
      [[ $rc -ne 0 && "$out" == *"permission denied"* ]] || fail "$svc прочитал чужой путь $MOUNT/$target"
      ok "$svc получает permission denied на $MOUNT/$target/*"
    fi
  done
  bao_as "$svc_token" token revoke -self >/dev/null
done

step "Шаг 4. Root-токен"
if $REVOKE_ROOT; then
  cleanup
  trap - EXIT
  bao_as "$ROOT_TOKEN" token revoke -self >/dev/null
  ok "root-токен отозван; новый выпускается через bao operator generate-root с unseal-ключом"
else
  ok "не отозван (запустите с --revoke-root, когда будете готовы)"
fi
