"""
Phase 89: Advanced Agent Reputation v2.

Per-category reputation tracking with time decay, portable HMAC-signed proofs,
leaderboards, and side-by-side agent comparison.

Categories: code_review, testing, architecture, debugging, docs, deployment.

Storage:
  state/broker/reputation_v2/outcomes.jsonl   -- every recorded outcome
  state/broker/reputation_v2/scores.json      -- materialised scores
"""

import hashlib
import hmac
import json
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from brynq.server import BROKER_DIR, _write_json_sync, _read_jsonl_sync, _append_jsonl_sync

# ── Paths ──────────────────────────────────────────────────────────────────

REP_DIR = BROKER_DIR / "reputation_v2"
REP_DIR.mkdir(parents=True, exist_ok=True)

OUTCOMES_FILE = REP_DIR / "outcomes.jsonl"
SCORES_FILE = REP_DIR / "scores.json"

# ── Constants ──────────────────────────────────────────────────────────────

CATEGORIES = frozenset({
    "code_review", "testing", "architecture",
    "debugging", "docs", "deployment",
})

_PROOF_SECRET = b"brynq-reputation-v2-hmac-key"

# ── Persistence Helpers ────────────────────────────────────────────────────


def _load_scores() -> dict:
    try:
        if SCORES_FILE.exists():
            return json.loads(SCORES_FILE.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        pass
    return {}


def _save_scores(data: dict) -> None:
    _write_json_sync(SCORES_FILE, data)


# ── Public API ─────────────────────────────────────────────────────────────


def record_outcome(
    agent_id: str,
    category: str,
    success: bool,
    quality_score: int = 5,
    reviewer_id: Optional[str] = None,
) -> dict:
    """Record a task outcome for *agent_id* in *category*."""
    if category not in CATEGORIES:
        return {"error": f"Invalid category '{category}'. Must be one of {sorted(CATEGORIES)}"}
    if not (1 <= quality_score <= 10):
        return {"error": "quality_score must be between 1 and 10"}

    entry = {
        "agent_id": agent_id,
        "category": category,
        "success": success,
        "quality_score": quality_score,
        "reviewer_id": reviewer_id,
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }
    _append_jsonl_sync(OUTCOMES_FILE, entry)

    # Update materialised scores
    scores = _load_scores()
    agent = scores.setdefault(agent_id, {"overall": 50.0, "categories": {}, "count": 0})
    cat_data = agent["categories"].setdefault(category, {"score": 50.0, "count": 0, "successes": 0})

    cat_data["count"] += 1
    if success:
        cat_data["successes"] += 1
    # Weighted rolling update: blend toward quality_score * 10
    target = quality_score * 10
    weight = 0.3
    cat_data["score"] = round(cat_data["score"] * (1 - weight) + target * weight, 2)

    agent["count"] += 1
    # Overall = average of category scores
    cat_scores = [c["score"] for c in agent["categories"].values()]
    agent["overall"] = round(sum(cat_scores) / len(cat_scores), 2)
    agent["last_active"] = entry["timestamp"]

    _save_scores(scores)
    return {"ok": True, **entry}


def get_reputation(agent_id: str) -> dict:
    """Return overall score and per-category breakdown for *agent_id*."""
    scores = _load_scores()
    agent = scores.get(agent_id)
    if not agent:
        return {"agent_id": agent_id, "overall": 0, "categories": {}, "count": 0}
    return {"agent_id": agent_id, **agent}


def decay_reputations(decay_rate: float = 0.01) -> dict:
    """Apply time decay to all agents. Scores drift toward 50 by *decay_rate*."""
    scores = _load_scores()
    affected = 0
    for agent_id, agent in scores.items():
        for cat_data in agent.get("categories", {}).values():
            old = cat_data["score"]
            cat_data["score"] = round(old + (50 - old) * decay_rate, 2)
        cat_scores = [c["score"] for c in agent.get("categories", {}).values()]
        if cat_scores:
            agent["overall"] = round(sum(cat_scores) / len(cat_scores), 2)
        affected += 1
    _save_scores(scores)
    return {"ok": True, "agents_affected": affected, "decay_rate": decay_rate}


def get_reputation_proof(agent_id: str) -> dict:
    """Generate an HMAC-signed portable proof of reputation."""
    rep = get_reputation(agent_id)
    payload = {
        "agent_id": agent_id,
        "overall": rep.get("overall", 0),
        "categories": rep.get("categories", {}),
        "count": rep.get("count", 0),
        "generated_at": datetime.now(timezone.utc).isoformat(),
    }
    payload_json = json.dumps(payload, sort_keys=True, default=str)
    sig = hmac.new(_PROOF_SECRET, payload_json.encode(), hashlib.sha256).hexdigest()
    return {"payload": payload, "signature": sig}


def verify_reputation_proof(proof: dict) -> dict:
    """Verify that a reputation proof is authentic."""
    payload = proof.get("payload")
    sig = proof.get("signature")
    if not payload or not sig:
        return {"valid": False, "error": "Missing payload or signature"}
    payload_json = json.dumps(payload, sort_keys=True, default=str)
    expected = hmac.new(_PROOF_SECRET, payload_json.encode(), hashlib.sha256).hexdigest()
    valid = hmac.compare_digest(sig, expected)
    return {"valid": valid, "agent_id": payload.get("agent_id")}


def get_leaderboard(category: Optional[str] = None, limit: int = 10) -> list[dict]:
    """Ranked agents, optionally filtered by *category*."""
    scores = _load_scores()
    entries = []
    for agent_id, agent in scores.items():
        if category:
            cat = agent.get("categories", {}).get(category)
            score = cat["score"] if cat else 0
        else:
            score = agent.get("overall", 0)
        entries.append({"agent_id": agent_id, "score": score})
    entries.sort(key=lambda x: x["score"], reverse=True)
    for i, e in enumerate(entries):
        e["rank"] = i + 1
    return entries[:limit]


def compare_agents(agent_ids: list[str]) -> dict:
    """Side-by-side reputation comparison."""
    if len(agent_ids) < 2:
        return {"error": "Need at least 2 agent IDs to compare"}
    profiles = {}
    for aid in agent_ids:
        profiles[aid] = get_reputation(aid)
    return {"agents": profiles}
