"""
Tests for Phase 89: Advanced Agent Reputation v2.
"""

import json

import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    """Redirect reputation storage to a temp directory."""
    rep_dir = tmp_path / "reputation_v2"
    rep_dir.mkdir(parents=True, exist_ok=True)

    import brynq.server as srv
    monkeypatch.setattr(srv, "BROKER_DIR", tmp_path)

    import brynq.agent_reputation_v2 as mod
    monkeypatch.setattr(mod, "REP_DIR", rep_dir)
    monkeypatch.setattr(mod, "OUTCOMES_FILE", rep_dir / "outcomes.jsonl")
    monkeypatch.setattr(mod, "SCORES_FILE", rep_dir / "scores.json")


# ── 1. Record Outcome ─────────────────────────────────────────────────────


class TestRecordOutcome:
    def test_record_success(self):
        from brynq.agent_reputation_v2 import record_outcome
        result = record_outcome("agent1", "testing", True, quality_score=8)
        assert result["ok"] is True
        assert result["agent_id"] == "agent1"
        assert result["category"] == "testing"

    def test_record_failure(self):
        from brynq.agent_reputation_v2 import record_outcome
        result = record_outcome("agent1", "debugging", False, quality_score=3)
        assert result["ok"] is True
        assert result["success"] is False

    def test_invalid_category(self):
        from brynq.agent_reputation_v2 import record_outcome
        result = record_outcome("agent1", "invalid_cat", True)
        assert "error" in result

    def test_invalid_quality_score_too_low(self):
        from brynq.agent_reputation_v2 import record_outcome
        result = record_outcome("agent1", "testing", True, quality_score=0)
        assert "error" in result

    def test_invalid_quality_score_too_high(self):
        from brynq.agent_reputation_v2 import record_outcome
        result = record_outcome("agent1", "testing", True, quality_score=11)
        assert "error" in result

    def test_with_reviewer(self):
        from brynq.agent_reputation_v2 import record_outcome
        result = record_outcome("agent1", "code_review", True, reviewer_id="reviewer1")
        assert result["reviewer_id"] == "reviewer1"


# ── 2. Get Reputation ─────────────────────────────────────────────────────


class TestGetReputation:
    def test_no_history(self):
        from brynq.agent_reputation_v2 import get_reputation
        rep = get_reputation("unknown_agent")
        assert rep["overall"] == 0
        assert rep["categories"] == {}

    def test_single_outcome(self):
        from brynq.agent_reputation_v2 import record_outcome, get_reputation
        record_outcome("agent1", "testing", True, quality_score=8)
        rep = get_reputation("agent1")
        assert rep["overall"] > 0
        assert "testing" in rep["categories"]

    def test_multiple_categories(self):
        from brynq.agent_reputation_v2 import record_outcome, get_reputation
        record_outcome("agent1", "testing", True, quality_score=7)
        record_outcome("agent1", "docs", True, quality_score=9)
        rep = get_reputation("agent1")
        assert "testing" in rep["categories"]
        assert "docs" in rep["categories"]
        assert rep["count"] == 2


# ── 3. Decay Reputations ──────────────────────────────────────────────────


class TestDecay:
    def test_decay_moves_toward_50(self):
        from brynq.agent_reputation_v2 import record_outcome, get_reputation, decay_reputations
        record_outcome("agent1", "testing", True, quality_score=10)
        before = get_reputation("agent1")["categories"]["testing"]["score"]
        decay_reputations(decay_rate=0.1)
        after = get_reputation("agent1")["categories"]["testing"]["score"]
        # Score should move toward 50
        assert abs(after - 50) < abs(before - 50)

    def test_decay_returns_count(self):
        from brynq.agent_reputation_v2 import record_outcome, decay_reputations
        record_outcome("a1", "testing", True)
        record_outcome("a2", "docs", True)
        result = decay_reputations()
        assert result["agents_affected"] == 2

    def test_decay_no_agents(self):
        from brynq.agent_reputation_v2 import decay_reputations
        result = decay_reputations()
        assert result["ok"] is True
        assert result["agents_affected"] == 0


# ── 4. Reputation Proof ───────────────────────────────────────────────────


class TestReputationProof:
    def test_generate_and_verify(self):
        from brynq.agent_reputation_v2 import record_outcome, get_reputation_proof, verify_reputation_proof
        record_outcome("agent1", "architecture", True, quality_score=9)
        proof = get_reputation_proof("agent1")
        assert "payload" in proof
        assert "signature" in proof
        assert proof["payload"]["agent_id"] == "agent1"

        verification = verify_reputation_proof(proof)
        assert verification["valid"] is True

    def test_tampered_proof_fails(self):
        from brynq.agent_reputation_v2 import record_outcome, get_reputation_proof, verify_reputation_proof
        record_outcome("agent1", "testing", True)
        proof = get_reputation_proof("agent1")
        proof["payload"]["overall"] = 999
        verification = verify_reputation_proof(proof)
        assert verification["valid"] is False

    def test_missing_payload(self):
        from brynq.agent_reputation_v2 import verify_reputation_proof
        result = verify_reputation_proof({"signature": "abc"})
        assert result["valid"] is False

    def test_missing_signature(self):
        from brynq.agent_reputation_v2 import verify_reputation_proof
        result = verify_reputation_proof({"payload": {}})
        assert result["valid"] is False


# ── 5. Leaderboard ────────────────────────────────────────────────────────


class TestLeaderboard:
    def test_empty_leaderboard(self):
        from brynq.agent_reputation_v2 import get_leaderboard
        lb = get_leaderboard()
        assert lb == []

    def test_ranked_order(self):
        from brynq.agent_reputation_v2 import record_outcome, get_leaderboard
        record_outcome("low", "testing", True, quality_score=2)
        record_outcome("high", "testing", True, quality_score=10)
        lb = get_leaderboard()
        assert lb[0]["agent_id"] == "high"
        assert lb[0]["rank"] == 1

    def test_category_filter(self):
        from brynq.agent_reputation_v2 import record_outcome, get_leaderboard
        record_outcome("agent1", "testing", True, quality_score=9)
        record_outcome("agent1", "docs", True, quality_score=3)
        record_outcome("agent2", "docs", True, quality_score=10)
        lb = get_leaderboard(category="docs")
        assert lb[0]["agent_id"] == "agent2"

    def test_limit(self):
        from brynq.agent_reputation_v2 import record_outcome, get_leaderboard
        for i in range(5):
            record_outcome(f"agent{i}", "testing", True, quality_score=5 + i)
        lb = get_leaderboard(limit=3)
        assert len(lb) == 3


# ── 6. Compare Agents ─────────────────────────────────────────────────────


class TestCompareAgents:
    def test_compare_two(self):
        from brynq.agent_reputation_v2 import record_outcome, compare_agents
        record_outcome("a", "testing", True, quality_score=8)
        record_outcome("b", "testing", True, quality_score=5)
        result = compare_agents(["a", "b"])
        assert "a" in result["agents"]
        assert "b" in result["agents"]

    def test_compare_needs_two(self):
        from brynq.agent_reputation_v2 import compare_agents
        result = compare_agents(["only_one"])
        assert "error" in result
