#!/usr/bin/env bash
# scripts/tg — wrapper: decrypt state before, encrypt+commit after mutating ops
# Usage: scripts/tg apply --non-interactive
#        scripts/tg plan
# Auth: `vault login -method=oidc` (token at ~/.vault-token)
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SYNC="$REPO_ROOT/scripts/state-sync"

# Enable provider cache (shared across stacks)
export TF_PLUGIN_CACHE_DIR="${TF_PLUGIN_CACHE_DIR:-$HOME/.terraform.d/plugin-cache}"
export TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE=1
mkdir -p "$TF_PLUGIN_CACHE_DIR"

# Determine stack name from cwd (relative to stacks/)
STACK_NAME=""
cwd="$(pwd)"
stacks_dir="$REPO_ROOT/stacks"
if [[ "$cwd" == "$stacks_dir"/* ]]; then
  rel="${cwd#$stacks_dir/}"
  STACK_NAME="${rel%%/*}"
fi

# ── Advisory lock via Vault KV ──
LOCK_MAX_AGE=1800  # 30 minutes — stale lock threshold
acquire_lock() {
  local stack="$1"
  local vault_addr="${VAULT_ADDR:-https://vault.viktorbarzin.me}"
  local lock_path="secret/data/locks/$stack"
  local holder="pid=$$,host=$(hostname -s),user=$(whoami)"

  # Check if lock exists and is not stale
  local existing
  existing=$(vault kv get -format=json "secret/locks/$stack" 2>/dev/null || echo '{}')
  local locked=$(echo "$existing" | jq -r '.data.data.locked // "false"')
  local acquired=$(echo "$existing" | jq -r '.data.data.acquired // "0"')
  local existing_holder=$(echo "$existing" | jq -r '.data.data.holder // ""')

  if [ "$locked" = "true" ]; then
    local now=$(date +%s)
    local age=$((now - acquired))
    if [ "$age" -lt "$LOCK_MAX_AGE" ]; then
      echo "ERROR: Stack '$stack' is locked by: $existing_holder (${age}s ago)"
      echo "       Wait for it to finish or run: vault kv delete secret/locks/$stack"
      return 1
    fi
    echo "WARNING: Breaking stale lock on '$stack' (held ${age}s by $existing_holder)"
  fi

  vault kv put "secret/locks/$stack" locked=true holder="$holder" acquired="$(date +%s)" >/dev/null
}

release_lock() {
  local stack="$1"
  vault kv delete "secret/locks/$stack" >/dev/null 2>&1 || true
}

# Decrypt state before any operation
if [ -n "$STACK_NAME" ] && [ -f "$REPO_ROOT/state/stacks/$STACK_NAME/terraform.tfstate.enc" ]; then
  "$SYNC" decrypt "$STACK_NAME"
fi

# Detect if this is a mutating operation
is_mutating=false
for arg in "$@"; do
  case "$arg" in
    apply|destroy|import|state) is_mutating=true ;;
  esac
done

# Acquire lock for mutating operations
if $is_mutating && [ -n "$STACK_NAME" ]; then
  if command -v vault &>/dev/null && [ -n "${VAULT_TOKEN:-}" ]; then
    acquire_lock "$STACK_NAME"
    trap 'release_lock "$STACK_NAME"' EXIT
  fi
fi

# If running apply with --non-interactive, add -auto-approve for Terraform
args=("$@")
has_apply=false
has_non_interactive=false
for arg in "${args[@]}"; do
  case "$arg" in
    apply) has_apply=true ;;
    --non-interactive) has_non_interactive=true ;;
  esac
done

if $has_apply && $has_non_interactive; then
  new_args=()
  for arg in "${args[@]}"; do
    new_args+=("$arg")
    if [ "$arg" = "apply" ]; then
      new_args+=("-auto-approve")
    fi
  done
  terragrunt "${new_args[@]}"
else
  terragrunt "$@"
fi

# After mutating operations, encrypt and commit
if $is_mutating && [ -n "$STACK_NAME" ]; then
  "$SYNC" encrypt "$STACK_NAME"
  cd "$REPO_ROOT"
  git add "state/stacks/$STACK_NAME/terraform.tfstate.enc"
  if ! git diff --cached --quiet; then
    git commit -m "state($STACK_NAME): update encrypted state"
  fi
fi
