#!/usr/bin/env python3
"""
CLI Submit Command - Posts benchmark results to FastAPI backend.

SECURITY: CLI does NOT have Supabase credentials. It POSTs to the
FastAPI backend which handles validation, rate limiting, and DB insert.

Usage:
    python -m cli submit result.json
    python -m cli submit result.json --dry-run
    python -m cli submit result.json --github myhandle
"""

import hashlib
import hmac
import json
import os
import sys
from datetime import datetime, timezone
from typing import Optional

import httpx

# Backend URL (no Supabase credentials needed!)
API_URL = os.environ.get("JANUS_LABS_API", "https://fulfilling-courtesy-production-9c2c.up.railway.app")
HMAC_SECRET = os.environ.get("JANUS_HMAC_SECRET", "default-dev-secret")
_USING_DEFAULT_SECRET = HMAC_SECRET == "default-dev-secret"


def generate_signature(payload: dict) -> str:
    """Generate HMAC-SHA256 signature for payload."""
    # Canonical JSON (sorted keys, no spaces)
    canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
    sig = hmac.new(
        HMAC_SECRET.encode(), canonical.encode(), hashlib.sha256
    ).hexdigest()[:64]
    return sig


def _score_to_grade(score: float) -> str:
    """Convert numeric score to letter grade.

    Backend expects single letters: S, A, B, C, D, F
    S = 95+, A = 85+, B = 70+, C = 55+, D = 40+, F = below
    """
    if score >= 95:
        return "S"
    elif score >= 85:
        return "A"
    elif score >= 70:
        return "B"
    elif score >= 55:
        return "C"
    elif score >= 40:
        return "D"
    else:
        return "F"


def _behavior_id_to_code(behavior_id: str) -> str:
    """Convert behavior ID to backend format.

    Backend expects: ^[A-Z]-\\d+\\.\\d+$ (e.g., "B-1.0")
    Input: "BHV-001-test-cheating" -> "B-1.0"
    """
    import re
    # Extract number from behavior ID
    match = re.search(r"(\d+)", behavior_id)
    if match:
        num = int(match.group(1))
        return f"B-{num}.0"
    return "B-1.0"


def submit_result(
    result_file: str, github_handle: Optional[str] = None, dry_run: bool = False
) -> dict:
    """Submit benchmark result to FastAPI backend.

    Handles both suite-level results (from janus run) and single-behavior
    results (from janus score).
    """

    with open(result_file) as f:
        result = json.load(f)

    # Detect result type and normalize to suite format
    if "headline_score" in result:
        # Suite-level result from janus run
        score = result["headline_score"]
        grade = result["grade"]
        suite_id = result["suite_id"]
        behaviors = [
            {
                "code": _behavior_id_to_code(b["behavior_id"]),
                "score": b["score"],
                "grade": b["grade"],
            }
            for b in result.get("behavior_scores", [])
        ]
    elif "behavior_id" in result:
        # Single behavior result from janus score
        # Convert 1-10 score to 0-100 for consistency
        raw_score = result.get("outcome_score") or result.get("score")
        score = raw_score * 10  # 9.0 -> 90
        grade = _score_to_grade(score)
        # Extract suite from behavior ID (e.g., BHV-001-test-cheating -> derive from context)
        suite_id = result.get("suite_id", "refactor-storm")
        behavior_code = _behavior_id_to_code(result["behavior_id"])
        behaviors = [
            {"code": behavior_code, "score": score, "grade": grade}
        ]
    else:
        raise RuntimeError("Unrecognized result format - missing headline_score or behavior_id")

    # Generate config hash (8-12 chars required by backend)
    config_fp = result.get("config_fingerprint", "")
    if not config_fp or config_fp == "unknown" or len(config_fp) < 8:
        # Generate hash from result content
        config_fp = hashlib.sha256(
            json.dumps(result, sort_keys=True).encode()
        ).hexdigest()[:12]
    elif len(config_fp) > 12:
        # Truncate if too long (backend max is 12)
        config_fp = config_fp[:12]

    # Build submission payload
    payload = {
        "score": score,
        "grade": grade,
        "agent": result.get("agent", "claude-code"),
        "model": result.get("model", "opus-4.5"),
        "suite": suite_id,
        "suite_version": result.get("suite_version", "1.0"),
        "cli_version": result.get("cli_version", "0.2.0"),
        "config_hash": config_fp,
        "config_sources": result.get("config_sources", ["CLAUDE.md"]),
        "config_badge": result.get("config_badge", "default"),
        "behaviors": behaviors,
        "client_timestamp": datetime.now(timezone.utc).isoformat(),
    }

    if github_handle:
        payload["github_handle"] = github_handle

    # Generate signature (backend will verify)
    payload["signature"] = generate_signature(payload)

    if dry_run:
        print("DRY RUN - Would submit:")
        print(json.dumps(payload, indent=2))
        if _USING_DEFAULT_SECRET:
            print("\nWARNING: Using default dev secret. Set JANUS_HMAC_SECRET for production.", file=sys.stderr)
        return {"status": "dry_run", "payload": payload}

    # Warn about default secret before attempting submission
    if _USING_DEFAULT_SECRET:
        print("WARNING: Using default dev secret.", file=sys.stderr)
        print("         Production submissions require JANUS_HMAC_SECRET.", file=sys.stderr)
        print("         Set via: export JANUS_HMAC_SECRET=<your-key>", file=sys.stderr)
        print("", file=sys.stderr)

    # Submit to FastAPI backend (NOT directly to Supabase)
    try:
        response = httpx.post(
            f"{API_URL}/api/submit",
            json=payload,
            headers={"Content-Type": "application/json"},
            timeout=30.0,
        )
    except httpx.ConnectError as e:
        raise RuntimeError(
            f"Connection failed: Could not reach {API_URL}\n"
            f"  Check your internet connection or try again later.\n"
            f"  Details: {e}"
        )
    except httpx.TimeoutException:
        raise RuntimeError(
            "Request timed out after 30 seconds.\n"
            "  The server may be under heavy load. Try again later."
        )

    if response.status_code == 201:
        data = response.json()

        print(f"\n{'='*50}")
        print("  SUBMITTED SUCCESSFULLY!")
        print(f"{'='*50}")
        print(f"  Score: {payload['score']} (Grade {payload['grade']})")
        print(f"  Rank: #{data.get('rank', '?')} on {payload['suite']}")
        print(f"  Percentile: Top {data.get('percentile', '?')}%")
        print(f"  Share: {data['share_url']}")
        print(f"{'='*50}\n")

        return {
            "status": "success",
            "submission_id": data["submission_id"],
            "share_url": data["share_url"],
            "percentile": data.get("percentile"),
            "rank": data.get("rank"),
            "score": payload["score"],
        }
    elif response.status_code == 429:
        raise RuntimeError(
            "Rate limit exceeded.\n"
            "  You can only submit once per minute. Try again later."
        )
    elif response.status_code in (401, 403):
        # Signature validation failed
        detail = response.json().get("detail", "Invalid signature")
        error_msg = f"Authentication failed: {detail}\n"
        if _USING_DEFAULT_SECRET:
            error_msg += (
                "\n"
                "  You are using the default dev secret which is not accepted\n"
                "  by the production server.\n"
                "\n"
                "  To submit to the public leaderboard:\n"
                "    1. Get an API key from https://janus-labs.dev/api-keys\n"
                "    2. Set: export JANUS_HMAC_SECRET=<your-key>\n"
                "    3. Re-run: janus-labs submit result.json\n"
            )
        else:
            error_msg += (
                "\n"
                "  Your JANUS_HMAC_SECRET may be incorrect or expired.\n"
                "  Get a new key from https://janus-labs.dev/api-keys\n"
            )
        raise RuntimeError(error_msg)
    elif response.status_code == 400:
        detail = response.json().get("detail", response.text)
        raise RuntimeError(
            f"Validation error: {detail}\n"
            "\n"
            "  This usually means the result.json format is incorrect.\n"
            "  Run with --dry-run to see the payload being submitted."
        )
    elif response.status_code == 422:
        # Schema validation error
        detail = response.json().get("detail", response.text)
        raise RuntimeError(
            f"Schema validation failed: {detail}\n"
            "\n"
            "  The result.json fields don't match the expected format.\n"
            "  This may be a CLI version mismatch. Try: pip install --upgrade janus-labs"
        )
    else:
        raise RuntimeError(
            f"Submit failed: HTTP {response.status_code}\n"
            f"  Response: {response.text[:200]}"
        )


def cmd_submit(args) -> int:
    """Handle submit subcommand."""
    try:
        result = submit_result(args.result_file, args.github, args.dry_run)
        return 0
    except FileNotFoundError:
        print(f"ERROR: File not found: {args.result_file}", file=sys.stderr)
        return 1
    except json.JSONDecodeError as e:
        print(f"ERROR: Invalid JSON: {e}", file=sys.stderr)
        return 1
    except RuntimeError as e:
        print(f"ERROR: {e}", file=sys.stderr)
        return 1
    except Exception as e:
        print(f"ERROR: {e}", file=sys.stderr)
        return 1
