#!/usr/bin/env bash
# minimax-check-upgrade — detect new/removed/changed models in /v1/models
#
# Polls https://api.minimax.io/v1/models, diffs against a locked snapshot,
# and reports any changes.
#
# Exit codes:
#   0 — no change (live matches locked snapshot)
#   1 — change detected (new/removed/modified models)
#   2 — fetch or parse error
#
# Flags:
#   --json     emit structured JSON diff to stdout instead of human-readable
#   --update   if changes detected, OVERWRITE the locked snapshot with live
#              (use after reviewing the diff and validating new behavior)
#
# Environment variables (override defaults):
#   MINIMAX_LOCKED_SNAPSHOT  — path to the locked snapshot JSON file
#                              (default: <plugin-root>/references/fixtures/models-list-locked.json)
#   MINIMAX_API_KEY          — MiniMax API key (skips 1Password lookup if set)
#   MINIMAX_API_KEY_OP_PATH  — 1Password op-path for API key
#                              (default: op://ggk4orq7rmcm7jinsb4ahygv7e/e54cb3ujopexslaq7loywpuycm/password)
#   MINIMAX_OP_ACCOUNT       — 1Password account UUID
#                              (default: K5BH72Z7O5BYXOGKBYT5FWTP2E)
#
# Provenance: ported from ~/own/amonic/bin/minimax-check-upgrade (amonic
# campaign iter-41) with portability adjustments for cc-skills marketplace.
# See ../references/api-patterns/model-upgrade-detection.md for architecture.
#
# SSoT: this script + the locked snapshot. New models from MiniMax are detected
# automatically; the operator decides whether to bump the locked snapshot via
# --update after auditing the diff.

set -euo pipefail

# Resolve plugin root from script location: this script lives at
# <plugin-root>/scripts/minimax-check-upgrade
PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

# Default locked snapshot location; override via MINIMAX_LOCKED_SNAPSHOT
LOCKED_SNAPSHOT="${MINIMAX_LOCKED_SNAPSHOT:-${PLUGIN_ROOT}/references/fixtures/models-list-locked.json}"
ENDPOINT="https://api.minimax.io/v1/models"

EMIT_JSON=0
DO_UPDATE=0
for arg in "$@"; do
    case "$arg" in
        --json) EMIT_JSON=1 ;;
        --update) DO_UPDATE=1 ;;
        --help|-h)
            sed -n '2,/^$/p' "${BASH_SOURCE[0]}" | sed 's/^# \?//'
            exit 0
            ;;
        *) echo "Unknown arg: $arg" >&2; exit 2 ;;
    esac
done

# Acquire API key. Order of resolution:
#   1. MINIMAX_API_KEY env var (if set, skip 1Password)
#   2. op read with MINIMAX_API_KEY_OP_PATH + MINIMAX_OP_ACCOUNT (if op CLI available)
#   3. Error out
if [[ -n "${MINIMAX_API_KEY:-}" ]]; then
    API_KEY="$MINIMAX_API_KEY"
elif command -v op >/dev/null 2>&1; then
    OP_PATH="${MINIMAX_API_KEY_OP_PATH:-op://ggk4orq7rmcm7jinsb4ahygv7e/e54cb3ujopexslaq7loywpuycm/password}"
    OP_ACCOUNT="${MINIMAX_OP_ACCOUNT:-K5BH72Z7O5BYXOGKBYT5FWTP2E}"
    API_KEY=$(op read "$OP_PATH" --account "$OP_ACCOUNT" 2>/dev/null) || {
        echo "ERROR: Failed to read MiniMax API key from 1Password (path=$OP_PATH, account=$OP_ACCOUNT)" >&2
        echo "  Set MINIMAX_API_KEY env var, or override MINIMAX_API_KEY_OP_PATH / MINIMAX_OP_ACCOUNT" >&2
        exit 2
    }
else
    echo "ERROR: MINIMAX_API_KEY not set and 'op' CLI not available" >&2
    echo "  Either: export MINIMAX_API_KEY=<your-key>" >&2
    echo "  Or: install 1Password CLI and configure MINIMAX_API_KEY_OP_PATH" >&2
    exit 2
fi

# Fetch live snapshot (capture body and HTTP status separately)
LIVE_BODY=$(mktemp)
trap 'rm -f "$LIVE_BODY"' EXIT
HTTP_STATUS=$(curl -sS -o "$LIVE_BODY" -w "%{http_code}" \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    "$ENDPOINT") || {
    echo "ERROR: curl to $ENDPOINT failed" >&2
    exit 2
}

if [[ "$HTTP_STATUS" != "200" ]]; then
    echo "ERROR: HTTP $HTTP_STATUS from $ENDPOINT" >&2
    cat "$LIVE_BODY" >&2
    exit 2
fi

if [[ ! -f "$LOCKED_SNAPSHOT" ]]; then
    echo "ERROR: locked snapshot not found at $LOCKED_SNAPSHOT" >&2
    echo "Initialize with: cp \"$LIVE_BODY\" \"$LOCKED_SNAPSHOT\"" >&2
    exit 2
fi

# Diff in Python (inline) — single source of truth for diff logic.
# Run Python directly (not via command substitution) so its non-zero exit
# (1 = changes detected) doesn't trip `set -e` before we read PYTHON_EXIT.
# This bash-trap was discovered in amonic iter-41 — see model-upgrade-detection.md
# "Bug history (educational)" section.
set +e
python3 - "$LOCKED_SNAPSHOT" "$LIVE_BODY" "$EMIT_JSON" <<'PYEOF'
import json
import sys
from datetime import datetime, timezone

locked_path, live_path, emit_json = sys.argv[1], sys.argv[2], sys.argv[3] == "1"

with open(locked_path) as f:
    locked = json.load(f)
with open(live_path) as f:
    live = json.load(f)

locked_models = {m["id"]: m for m in locked.get("data", [])}
live_models = {m["id"]: m for m in live.get("data", [])}

added = sorted(set(live_models) - set(locked_models))
removed = sorted(set(locked_models) - set(live_models))
modified = []
for mid in sorted(set(live_models) & set(locked_models)):
    if locked_models[mid] != live_models[mid]:
        modified.append({
            "id": mid,
            "locked": locked_models[mid],
            "live": live_models[mid],
        })

has_changes = bool(added or removed or modified)

result = {
    "checked_at": datetime.now(timezone.utc).isoformat(),
    "endpoint": "https://api.minimax.io/v1/models",
    "locked_count": len(locked_models),
    "live_count": len(live_models),
    "added": [{"id": mid, **live_models[mid]} for mid in added],
    "removed": [{"id": mid, **locked_models[mid]} for mid in removed],
    "modified": modified,
    "has_changes": has_changes,
}

if emit_json:
    print(json.dumps(result, indent=2))
else:
    print(f"Checked {result['endpoint']} at {result['checked_at']}")
    print(f"Locked: {result['locked_count']} models | Live: {result['live_count']} models")
    if not has_changes:
        print("✅ No changes — live matches locked snapshot")
    else:
        print(f"🚨 CHANGES DETECTED")
        if added:
            print(f"\n  ➕ Added ({len(added)}):")
            for m in result["added"]:
                created_iso = datetime.fromtimestamp(m.get("created", 0), tz=timezone.utc).isoformat()
                print(f"     {m['id']} (created {created_iso})")
        if removed:
            print(f"\n  ➖ Removed ({len(removed)}):")
            for m in result["removed"]:
                print(f"     {m['id']}")
        if modified:
            print(f"\n  📝 Modified ({len(modified)}):")
            for m in result["modified"]:
                print(f"     {m['id']}:")
                for k in set(m["locked"]) | set(m["live"]):
                    if m["locked"].get(k) != m["live"].get(k):
                        print(f"       {k}: {m['locked'].get(k)} → {m['live'].get(k)}")

sys.exit(1 if has_changes else 0)
PYEOF
PYTHON_EXIT=$?
set -e

# If --update and changes were detected, overwrite the lock
if [[ "$DO_UPDATE" == "1" && "$PYTHON_EXIT" == "1" ]]; then
    cp "$LIVE_BODY" "$LOCKED_SNAPSHOT"
    echo ""
    echo "✓ Updated $LOCKED_SNAPSHOT to match live"
    exit 0
fi

exit "$PYTHON_EXIT"
