#!/usr/bin/env bash
# ops-credentials — Audit which integration credentials are configured.
#
# For each integration the plugin knows about, scans every credential source
# (shell env, ops preferences.json, Doppler if configured, macOS Keychain,
# Dashlane if installed) and reports configured-vs-missing with a masked
# preview of any value found.
#
# Output modes:
#   ops-credentials              # human-readable table
#   ops-credentials --json       # JSON array for programmatic use
#   ops-credentials --service X  # only check service X
#
# Never prints raw credential values. Mask format: first 6 chars + "•••" +
# last 4 chars. Values shorter than 12 chars print as "•••".

set -euo pipefail

IS_MACOS=false
[[ "$(uname -s)" = "Darwin" ]] && IS_MACOS=true

OPS_DATA_DIR="${OPS_DATA_DIR:-${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}}"
PREFS="$OPS_DATA_DIR/preferences.json"

OUTPUT_MODE="text"
SERVICE_FILTER=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --json) OUTPUT_MODE="json"; shift ;;
    --service) SERVICE_FILTER="$2"; shift 2 ;;
    -h|--help)
      sed -n '2,15p' "$0" | sed 's/^# \?//'
      exit 0 ;;
    *) echo "unknown arg: $1" >&2; exit 2 ;;
  esac
done

# ─── credential catalog ───────────────────────────────────────────────────
# service|label|env_var|prefs_jq_path|keychain_service|dashlane_keyword
read -r -d '' CATALOG <<'CATEOF' || true
telegram|Telegram (api_hash)|TELEGRAM_API_HASH|.telegram.api_hash|telegram-api-hash|telegram
telegram|Telegram (session)|TELEGRAM_SESSION||telegram-session|telegram
slack|Slack (xoxc)|SLACK_MCP_XOXC_TOKEN||slack-xoxc|slack
slack|Slack (xoxd)|SLACK_MCP_XOXD_TOKEN||slack-xoxd|slack
discord|Discord bot token|DISCORD_BOT_TOKEN|.discord.bot_token||discord
discord|Discord webhook|DISCORD_WEBHOOK_URL|.discord.default_webhook_url||discord
pushover|Pushover user key|PUSHOVER_USER|.pushover_user_key|pushover-user|pushover
pushover|Pushover app token|PUSHOVER_TOKEN|.pushover_app_token|pushover-app|pushover
sentry|Sentry auth token|SENTRY_AUTH_TOKEN||sentry-auth-token|sentry
linear|Linear API key|LINEAR_API_KEY||linear-api-key|linear
datadog|Datadog API key|DATADOG_API_KEY|.datadog.api_key||datadog
datadog|Datadog app key|DATADOG_APP_KEY|.datadog.app_key||datadog
newrelic|New Relic API key|NEW_RELIC_API_KEY|.newrelic.api_key||newrelic
doppler|Doppler service token|DOPPLER_TOKEN|.doppler.service_token||doppler
klaviyo|Klaviyo private key|KLAVIYO_API_KEY|.marketing.klaviyo.api_key|klaviyo-api-key|klaviyo
meta_ads|Meta Ads access token|META_ACCESS_TOKEN|.marketing.meta.access_token||meta
shopify|Shopify admin token|SHOPIFY_ACCESS_TOKEN|.ecom.shopify.admin_token|shopify-admin-token|shopify
shipbob|ShipBob access token|SHIPBOB_ACCESS_TOKEN|.ecom.partners.shipbob.credentials.api_token||shipbob
bland_ai|Bland AI API key|BLAND_AI_API_KEY|.voice.bland.api_key|bland-ai-api-key|bland
elevenlabs|ElevenLabs API key|ELEVENLABS_API_KEY|.voice.elevenlabs.api_key|elevenlabs-api-key|elevenlabs
groq|Groq API key|GROQ_API_KEY|.voice.groq.api_key|groq-api-key|groq
stripe|Stripe secret key|STRIPE_SECRET_KEY|.revenue.stripe.secret_key|stripe-secret-key|stripe
revenuecat|RevenueCat API key|REVENUECAT_API_KEY|.revenue.revenuecat.api_key|revenuecat-api-key|revenuecat
postnl|PostNL customer code|POSTNL_CUSTOMER_CODE|.shipping.postnl.customer_code||postnl
dpd|DPD password|DPD_PASSWORD|.shipping.dpd.password||dpd
ups|UPS client secret|UPS_CLIENT_SECRET|.shipping.ups.client_secret||ups
fedex|FedEx client secret|FEDEX_CLIENT_SECRET|.shipping.fedex.client_secret||fedex
CATEOF

# ─── helpers ──────────────────────────────────────────────────────────────
mask() {
  local v="${1:-}"
  local len=${#v}
  if [[ $len -eq 0 ]]; then printf ''
  elif [[ $len -lt 12 ]]; then printf '•••'
  else printf '%s•••%s' "${v:0:6}" "${v: -4}"
  fi
}

resolve() {
  local env_var="$1" prefs_path="$2" keychain_svc="$3" dashlane_kw="$4"
  local value="" source=""

  # 1. shell env
  if [[ -n "$env_var" ]]; then
    local v="${!env_var:-}"
    if [[ -n "$v" ]]; then echo "${v}|env:${env_var}"; return 0; fi
  fi

  # 2. preferences.json
  if [[ -n "$prefs_path" && -f "$PREFS" ]] && command -v jq >/dev/null 2>&1; then
    local v
    v=$(jq -r "${prefs_path} // empty" "$PREFS" 2>/dev/null || true)
    if [[ -n "$v" && "$v" != "null" ]]; then
      # Doppler reference is "configured" but resolves elsewhere
      if [[ "$v" == doppler:* ]]; then
        local key="${v#doppler:}"
        local resolved=""
        if command -v doppler >/dev/null 2>&1; then
          resolved=$(doppler secrets get "$key" --plain 2>/dev/null || true)
        fi
        if [[ -n "$resolved" ]]; then
          echo "${resolved}|doppler:${key}"; return 0
        fi
        echo "doppler-ref|prefs:${prefs_path}=${v}"; return 0
      fi
      echo "${v}|prefs:${prefs_path}"; return 0
    fi
  fi

  # 3. macOS keychain
  if $IS_MACOS && [[ -n "$keychain_svc" ]] && command -v security >/dev/null 2>&1; then
    local v
    v=$(security find-generic-password -s "$keychain_svc" -w 2>/dev/null || true)
    if [[ -n "$v" ]]; then echo "${v}|keychain:${keychain_svc}"; return 0; fi
  fi

  # 4. Dashlane
  if [[ -n "$dashlane_kw" ]] && command -v dcli >/dev/null 2>&1; then
    local v
    v=$(dcli password "$dashlane_kw" --output json 2>/dev/null | jq -r '.[0].password // empty' 2>/dev/null || true)
    if [[ -n "$v" && "$v" != "null" ]]; then echo "${v}|dashlane:${dashlane_kw}"; return 0; fi
  fi

  echo ""
}

# ─── scan ─────────────────────────────────────────────────────────────────
results_json="[]"
configured=0
missing=0
declare -a configured_lines=()
declare -a missing_lines=()

while IFS='|' read -r service label env_var prefs_path keychain_svc dashlane_kw; do
  [[ -z "$service" || "$service" == \#* ]] && continue
  if [[ -n "$SERVICE_FILTER" && "$service" != "$SERVICE_FILTER" ]]; then continue; fi

  found=$(resolve "$env_var" "$prefs_path" "$keychain_svc" "$dashlane_kw")
  if [[ -n "$found" ]]; then
    value="${found%|*}"
    src="${found##*|}"
    masked=$(mask "$value")
    configured=$((configured+1))
    configured_lines+=("$(printf '  ✓ %-32s %-22s %s' "$label" "$masked" "($src)")")
    if [[ "$OUTPUT_MODE" == "json" ]]; then
      results_json=$(jq -c --arg s "$service" --arg l "$label" --arg m "$masked" --arg src "$src" \
        '. + [{service:$s, label:$l, configured:true, masked:$m, source:$src}]' <<<"$results_json")
    fi
  else
    missing=$((missing+1))
    missing_lines+=("$(printf '  ✗ %-32s — run /ops:setup %s' "$label" "$service")")
    if [[ "$OUTPUT_MODE" == "json" ]]; then
      results_json=$(jq -c --arg s "$service" --arg l "$label" \
        '. + [{service:$s, label:$l, configured:false}]' <<<"$results_json")
    fi
  fi
done <<<"$CATALOG"

# ─── output ───────────────────────────────────────────────────────────────
if [[ "$OUTPUT_MODE" == "json" ]]; then
  echo "$results_json" | jq '.'
  exit 0
fi

# Mobile / SSH compact mode
COMPACT=0
if [[ -n "${SSH_CONNECTION:-}${SSH_CLIENT:-}${SSH_TTY:-}" || "${OPS_MOBILE:-}" == "1" ]]; then COMPACT=1; fi

if [[ $COMPACT -eq 1 ]]; then
  echo "credentials: ${configured} configured, ${missing} missing"
  if [[ $configured -gt 0 ]]; then
    printf '%s\n' "${configured_lines[@]}" | sed 's/^  ✓ /✓ /'
  fi
  if [[ $missing -gt 0 ]]; then
    printf '%s\n' "${missing_lines[@]}" | sed 's/^  ✗ /✗ /'
  fi
  exit 0
fi

cat <<EOF
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► CREDENTIALS AUDIT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EOF

if [[ $configured -gt 0 ]]; then
  echo
  echo "Configured ($configured):"
  printf '%s\n' "${configured_lines[@]}"
fi

if [[ $missing -gt 0 ]]; then
  echo
  echo "Missing ($missing):"
  printf '%s\n' "${missing_lines[@]}"
fi

cat <<EOF

──────────────────────────────────────────────────────
 Total: $((configured + missing)) tracked services · $configured set · $missing pending
 Configure: /ops:setup <service-name>
 Re-audit:  /ops:credentials  (or  bin/ops-credentials --json)
──────────────────────────────────────────────────────
EOF
