#!/usr/bin/env bash
# ops-secret-sync — Detect Doppler→GitHub secrets drift and report stale secrets.
#
# Usage:
#   ops-secret-sync --repo <owner/repo> --project <doppler-proj> [--config <env>] [--json] [--dry-run]
#
# Output:
#   Human table by default; JSON when --json is passed.
#   Exit codes: 0=ok/no-drift, 1=drift-found, 2=error
#
# This script is READ-ONLY — it never writes secrets. Syncing is done by the
# ops-secret-sync SKILL.md which calls `gh secret set` after user confirmation.

set -euo pipefail

REPO=""
PROJECT=""
CONFIG="prd"
JSON_OUT=false
DRY_RUN=false   # kept for compatibility; script is always dry-run (read-only)
THRESHOLD_SECONDS=86400  # 24h

# ── arg parse ────────────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
  case "$1" in
    --repo)      REPO="$2";    shift 2 ;;
    --project)   PROJECT="$2"; shift 2 ;;
    --config)    CONFIG="$2";  shift 2 ;;
    --json)      JSON_OUT=true; shift ;;
    --dry-run)   DRY_RUN=true;  shift ;;
    *) shift ;;
  esac
done

# ── prereq checks ────────────────────────────────────────────────────────────
missing_tools=()
command -v gh      >/dev/null 2>&1 || missing_tools+=("gh")
command -v doppler >/dev/null 2>&1 || missing_tools+=("doppler")
command -v jq      >/dev/null 2>&1 || missing_tools+=("jq")

if [[ ${#missing_tools[@]} -gt 0 ]]; then
  echo '{"error":"missing_tools","tools":'"$(printf '%s\n' "${missing_tools[@]}" | jq -R . | jq -s .)"'}' >&2
  exit 2
fi

if [[ -z "$REPO" || -z "$PROJECT" ]]; then
  echo '{"error":"missing_args","message":"--repo and --project are required"}' >&2
  exit 2
fi

# ── fetch GH secrets ─────────────────────────────────────────────────────────
GH_JSON=$(gh secret list --repo "$REPO" --json name,updatedAt 2>/dev/null || echo "[]")

# ── fetch Doppler secrets ─────────────────────────────────────────────────────
DOPPLER_JSON=$(doppler secrets --project "$PROJECT" --config "$CONFIG" --json 2>/dev/null || echo "{}")

# ── drift analysis ────────────────────────────────────────────────────────────
# Build analysis via jq: cross-reference by name, compute delta in seconds.
ANALYSIS=$(jq -n \
  --argjson gh "$GH_JSON" \
  --argjson dop "$DOPPLER_JSON" \
  --argjson threshold "$THRESHOLD_SECONDS" \
  '
  # Build GH lookup: name → updatedAt (epoch)
  def gh_map: ($gh | map({ (.name): .updatedAt }) | add // {});
  def to_epoch(s): s | if . == null then 0
    else gsub("\\.[0-9]+Z$"; "Z") | strptime("%Y-%m-%dT%H:%M:%SZ") | mktime end;

  def gh_epoch(name): (gh_map)[name] | to_epoch(.);

  # Doppler secret names (exclude meta-keys with no updatedAt)
  ($dop | to_entries | map(select(.value | type == "object"))) as $dop_entries |

  ($dop_entries | map(
    .key as $name |
    (.value.updatedAt // null) as $dop_ts |
    (gh_map | has($name)) as $in_gh |
    {
      name: $name,
      doppler_ts: $dop_ts,
      gh_ts: (if $in_gh then (gh_map)[$name] else null end),
      in_gh: $in_gh,
      delta_seconds: (
        if $in_gh and $dop_ts != null then
          (to_epoch($dop_ts) - gh_epoch($name))
        else null end
      )
    }
  )) as $compared |

  {
    repo: "'"$REPO"'",
    project: "'"$PROJECT"'",
    config: "'"$CONFIG"'",
    threshold_seconds: $threshold,
    drifted: ($compared | map(select(.delta_seconds != null and .delta_seconds > $threshold))),
    missing_from_gh: ($compared | map(select(.in_gh == false))),
    in_sync: ($compared | map(select(.in_gh == true and (.delta_seconds == null or .delta_seconds <= $threshold)))),
    doppler_total: ($compared | length),
    gh_total: ($gh | length)
  }
' 2>/dev/null)

if [[ -z "$ANALYSIS" ]]; then
  echo '{"error":"analysis_failed","message":"jq analysis returned empty — check doppler/gh output"}' >&2
  exit 2
fi

DRIFT_COUNT=$(echo "$ANALYSIS" | jq '.drifted | length')
MISSING_COUNT=$(echo "$ANALYSIS" | jq '.missing_from_gh | length')

# ── output ───────────────────────────────────────────────────────────────────
if $JSON_OUT; then
  echo "$ANALYSIS"
  # Exit 1 if drift found so callers can branch
  if [[ "$DRIFT_COUNT" -gt 0 || "$MISSING_COUNT" -gt 0 ]]; then exit 1; fi
  exit 0
fi

# ── mobile / compact mode ─────────────────────────────────────────────────────
COMPACT=false
if [[ -n "${SSH_CONNECTION:-}${SSH_CLIENT:-}${SSH_TTY:-}" || "${OPS_MOBILE:-}" == "1" ]]; then
  COMPACT=true
fi
if [[ -n "${COLUMNS:-}" ]] && (( COLUMNS < 80 )); then COMPACT=true; fi

if $COMPACT; then
  echo "repo: $REPO  project: $PROJECT/$CONFIG"
  echo "drifted: $DRIFT_COUNT  missing: $MISSING_COUNT"
  echo "$ANALYSIS" | jq -r '
    (.drifted[] | "\(.name)  doppler: \(.doppler_ts // "?")  gh: \(.gh_ts // "?")  DRIFTED"),
    (.missing_from_gh[] | "\(.name)  gh: MISSING")
  ' 2>/dev/null || true
  if [[ "$DRIFT_COUNT" -gt 0 || "$MISSING_COUNT" -gt 0 ]]; then exit 1; fi
  exit 0
fi

# ── full table output ─────────────────────────────────────────────────────────
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " OPS ► SECRET-SYNC — $REPO / $PROJECT/$CONFIG"
echo " Scanned: $NOW"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "$ANALYSIS" | jq -r '"  Doppler secrets : \(.doppler_total)\n  GH repo secrets : \(.gh_total)\n  Drifted (>24h)  : \(.drifted|length)\n  GH missing      : \(.missing_from_gh|length)\n  In sync         : \(.in_sync|length)"'
echo ""

if [[ "$DRIFT_COUNT" -gt 0 ]]; then
  echo " DRIFTED SECRETS (GH is stale vs Doppler)"
  echo "$ANALYSIS" | jq -r '
    .drifted[] |
    "   \(.name)\n     Doppler : \(.doppler_ts // "unknown")\n     GH      : \(.gh_ts // "unknown")\n     delta   : \(if .delta_seconds != null then "\(.delta_seconds / 86400 | floor)d \(.delta_seconds % 86400 / 3600 | floor)h" else "unknown" end)"
  ' 2>/dev/null
  echo ""
fi

if [[ "$MISSING_COUNT" -gt 0 ]]; then
  echo " GH MISSING (present in Doppler, absent from GH repo)"
  echo "$ANALYSIS" | jq -r '.missing_from_gh[] | "   \(.name)"' 2>/dev/null
  echo ""
fi

IN_SYNC_COUNT=$(echo "$ANALYSIS" | jq '.in_sync | length')
if [[ "$IN_SYNC_COUNT" -gt 0 ]]; then
  echo " IN SYNC"
  echo "$ANALYSIS" | jq -r '.in_sync[] | "   \(.name)  last-gh: \(.gh_ts // "unknown")"' 2>/dev/null
  echo ""
fi

echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

if [[ "$DRIFT_COUNT" -gt 0 || "$MISSING_COUNT" -gt 0 ]]; then
  echo " Action: run /ops:secret-sync to review and sync."
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  exit 1
fi

echo " All secrets in sync."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
