"""
Tests for Phase 43: Per-Agent Performance Analytics.
"""

import json
from datetime import datetime, timezone, timedelta

import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    channels = tmp_path / "channels"
    sessions = tmp_path / "sessions"
    analytics = tmp_path / "agent_analytics"
    for d in (channels, sessions, analytics):
        d.mkdir(parents=True, exist_ok=True)

    import brynq.server as srv
    monkeypatch.setattr(srv, "BROKER_DIR", tmp_path)
    monkeypatch.setattr(srv, "CHANNELS_DIR", channels)
    monkeypatch.setattr(srv, "SESSIONS_DIR", sessions)

    import brynq.agent_analytics as aa
    monkeypatch.setattr(aa, "AGENT_ANALYTICS_DIR", analytics)
    monkeypatch.setattr(aa, "BROKER_DIR", tmp_path)
    monkeypatch.setattr(aa, "CHANNELS_DIR", channels)
    monkeypatch.setattr(aa, "SESSIONS_DIR", sessions)


def _write_activity(tmp_path, agent_id, activity_type, details=None, ts=None):
    """Write a raw activity entry to the agent's log (bypasses validation for test setup)."""
    analytics = tmp_path / "agent_analytics"
    analytics.mkdir(parents=True, exist_ok=True)
    path = analytics / f"{agent_id}.jsonl"
    entry = {
        "agent_id": agent_id,
        "activity_type": activity_type,
        "timestamp": ts or datetime.now(timezone.utc).isoformat(),
        "details": details or {},
    }
    with open(path, "a", encoding="utf-8") as f:
        f.write(json.dumps(entry, default=str) + "\n")
    return entry


# ── 1. Record Activity ────────────────────────────────────────────────────


class TestRecordActivity:
    def test_basic_record(self):
        from brynq.agent_analytics import record_activity
        entry = record_activity("agent-1", "message_sent")
        assert entry["agent_id"] == "agent-1"
        assert entry["activity_type"] == "message_sent"
        assert "timestamp" in entry

    def test_record_with_details(self):
        from brynq.agent_analytics import record_activity
        entry = record_activity("agent-1", "task_completed", {"task_id": "t1", "duration_minutes": 15})
        assert entry["details"]["task_id"] == "t1"

    def test_invalid_activity_type(self):
        from brynq.agent_analytics import record_activity
        result = record_activity("agent-1", "dancing")
        assert "error" in result

    def test_all_valid_types(self):
        from brynq.agent_analytics import record_activity, VALID_ACTIVITY_TYPES
        for atype in VALID_ACTIVITY_TYPES:
            entry = record_activity("typetest", atype)
            assert entry["activity_type"] == atype

    def test_persists_to_disk(self, tmp_path):
        from brynq.agent_analytics import record_activity, _read_activities
        record_activity("persist", "login")
        record_activity("persist", "message_sent")
        entries = _read_activities("persist")
        assert len(entries) == 2


# ── 2. Performance Summary ────────────────────────────────────────────────


class TestPerformance:
    def test_empty_agent(self):
        from brynq.agent_analytics import get_performance
        perf = get_performance("nobody")
        assert perf["total_tasks"] == 0
        assert perf["success_rate"] == 0.0
        assert perf["messages_sent"] == 0

    def test_tasks_counted(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_performance
        record_activity("a1", "task_completed", {"duration_minutes": 10})
        record_activity("a1", "task_completed", {"duration_minutes": 20})
        record_activity("a1", "task_failed")
        perf = get_performance("a1")
        assert perf["total_tasks"] == 3
        assert perf["completed"] == 2
        assert perf["failed"] == 1
        assert perf["success_rate"] == pytest.approx(66.7, abs=0.1)

    def test_avg_completion_time(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_performance
        record_activity("a2", "task_completed", {"duration_minutes": 10})
        record_activity("a2", "task_completed", {"duration_minutes": 30})
        perf = get_performance("a2")
        assert perf["avg_completion_time_minutes"] == 20.0

    def test_messages_counted(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_performance
        for _ in range(5):
            record_activity("msg", "message_sent")
        perf = get_performance("msg")
        assert perf["messages_sent"] == 5

    def test_skills_tracked(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_performance
        record_activity("sk", "skill_used", {"skill": "python"})
        record_activity("sk", "skill_used", {"skill": "python"})
        record_activity("sk", "skill_used", {"skill": "rust"})
        perf = get_performance("sk")
        assert perf["skills_used"]["python"] == 2
        assert perf["skills_used"]["rust"] == 1

    def test_collaboration_count(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_performance
        record_activity("col", "collaboration", {"partner_id": "b1"})
        record_activity("col", "collaboration", {"partner_id": "b2"})
        record_activity("col", "collaboration", {"partner_id": "b1"})  # duplicate partner
        perf = get_performance("col")
        assert perf["collaboration_count"] == 2

    def test_reputation_trend_up(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_performance
        # Record old activity (>24h ago) -- none
        # Record recent activity (within 24h)
        record_activity("trend_up", "task_completed")
        perf = get_performance("trend_up", hours=24)
        # Current has 1 completed, previous window has 0 -> up
        assert perf["reputation_trend"] == "up"

    def test_time_filtering(self, tmp_path):
        from brynq.agent_analytics import get_performance
        # Write an entry 48 hours ago -- should be excluded from 24h window
        old_ts = (datetime.now(timezone.utc) - timedelta(hours=48)).isoformat()
        _write_activity(tmp_path, "old", "task_completed", ts=old_ts)
        # Write a recent entry
        _write_activity(tmp_path, "old", "message_sent")
        perf = get_performance("old", hours=24)
        assert perf["total_tasks"] == 0  # task_completed was too old
        assert perf["messages_sent"] == 1


# ── 3. Skill Growth ───────────────────────────────────────────────────────


class TestSkillGrowth:
    def test_empty(self):
        from brynq.agent_analytics import get_skill_growth
        result = get_skill_growth("nobody")
        assert result["skills"] == {}

    def test_basic_growth(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_skill_growth
        record_activity("g1", "skill_used", {"skill": "python", "success": True})
        record_activity("g1", "skill_used", {"skill": "python", "success": True})
        record_activity("g1", "skill_used", {"skill": "rust", "success": False})
        result = get_skill_growth("g1")
        assert result["skills"]["python"]["times_used"] == 2
        assert result["skills"]["python"]["success_rate"] == 100.0
        assert result["skills"]["rust"]["times_used"] == 1
        assert result["skills"]["rust"]["failures"] == 1

    def test_trend_improving(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_skill_growth
        # First half: failures, second half: successes -> improving
        for _ in range(4):
            record_activity("imp", "skill_used", {"skill": "go", "success": False})
        for _ in range(4):
            record_activity("imp", "skill_used", {"skill": "go", "success": True})
        result = get_skill_growth("imp")
        assert result["skills"]["go"]["trend"] == "improving"

    def test_trend_declining(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_skill_growth
        # First half: successes, second half: failures -> declining
        for _ in range(4):
            record_activity("dec", "skill_used", {"skill": "java", "success": True})
        for _ in range(4):
            record_activity("dec", "skill_used", {"skill": "java", "success": False})
        result = get_skill_growth("dec")
        assert result["skills"]["java"]["trend"] == "declining"

    def test_trend_stable(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_skill_growth
        # All successes -> stable
        for _ in range(6):
            record_activity("stb", "skill_used", {"skill": "sql", "success": True})
        result = get_skill_growth("stb")
        assert result["skills"]["sql"]["trend"] == "stable"


# ── 4. Activity Heatmap ──────────────────────────────────────────────────


class TestActivityHeatmap:
    def test_empty(self):
        from brynq.agent_analytics import get_activity_heatmap
        result = get_activity_heatmap("nobody")
        assert result["heatmap"] == {}
        assert result["total_activities"] == 0

    def test_single_day(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_activity_heatmap
        record_activity("hm1", "login")
        record_activity("hm1", "message_sent")
        result = get_activity_heatmap("hm1", days=1)
        assert result["total_activities"] == 2
        assert len(result["heatmap"]) >= 1
        # Check that the current day has a non-zero hour
        today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        assert today in result["heatmap"]

    def test_hourly_buckets(self, tmp_path):
        from brynq.agent_analytics import get_activity_heatmap
        now = datetime.now(timezone.utc)
        for i in range(3):
            _write_activity(tmp_path, "hm2", "message_sent", ts=now.isoformat())
        result = get_activity_heatmap("hm2", days=1)
        day_key = now.strftime("%Y-%m-%d")
        assert result["heatmap"][day_key][now.hour] == 3


# ── 5. Comparison ─────────────────────────────────────────────────────────


class TestCompareAgents:
    def test_need_two(self):
        from brynq.agent_analytics import compare_agents
        result = compare_agents(["only_one"])
        assert "error" in result

    def test_basic_comparison(self, tmp_path):
        from brynq.agent_analytics import record_activity, compare_agents
        record_activity("cmp1", "task_completed")
        record_activity("cmp1", "task_completed")
        record_activity("cmp2", "task_completed")
        result = compare_agents(["cmp1", "cmp2"])
        assert "agents" in result
        assert "leaders" in result
        assert result["leaders"]["completed"] == "cmp1"

    def test_comparison_leaders(self, tmp_path):
        from brynq.agent_analytics import record_activity, compare_agents
        # Agent A: many messages, few tasks
        for _ in range(10):
            record_activity("A", "message_sent")
        record_activity("A", "task_completed")
        # Agent B: few messages, many tasks
        record_activity("B", "message_sent")
        for _ in range(5):
            record_activity("B", "task_completed")
        result = compare_agents(["A", "B"])
        assert result["leaders"]["messages_sent"] == "A"
        assert result["leaders"]["completed"] == "B"


# ── 6. Leaderboard ───────────────────────────────────────────────────────


class TestLeaderboard:
    def test_invalid_metric(self):
        from brynq.agent_analytics import get_leaderboard
        result = get_leaderboard(metric="vibes")
        assert "error" in result

    def test_empty_leaderboard(self):
        from brynq.agent_analytics import get_leaderboard
        result = get_leaderboard()
        assert result["leaderboard"] == []
        assert result["total_agents"] == 0

    def test_ranked_leaderboard(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_leaderboard
        for _ in range(3):
            record_activity("lb1", "task_completed")
        record_activity("lb2", "task_completed")
        for _ in range(5):
            record_activity("lb3", "task_completed")
        result = get_leaderboard(metric="tasks_completed")
        lb = result["leaderboard"]
        assert lb[0]["agent_id"] == "lb3"
        assert lb[0]["rank"] == 1
        assert lb[1]["agent_id"] == "lb1"
        assert lb[2]["agent_id"] == "lb2"

    def test_limit(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_leaderboard
        for i in range(5):
            record_activity(f"lim{i}", "task_completed")
        result = get_leaderboard(limit=2)
        assert len(result["leaderboard"]) == 2

    def test_messages_metric(self, tmp_path):
        from brynq.agent_analytics import record_activity, get_leaderboard
        for _ in range(7):
            record_activity("talker", "message_sent")
        record_activity("quiet", "message_sent")
        result = get_leaderboard(metric="messages_sent")
        assert result["leaderboard"][0]["agent_id"] == "talker"


# ── 7. Report ────────────────────────────────────────────────────────────


class TestReport:
    def test_empty_report(self):
        from brynq.agent_analytics import generate_report
        report = generate_report("nobody")
        assert "No recorded activity" in report["summary"]
        assert report["performance"]["total_tasks"] == 0

    def test_active_report(self, tmp_path):
        from brynq.agent_analytics import record_activity, generate_report
        for _ in range(5):
            record_activity("rpt", "task_completed", {"duration_minutes": 10})
        record_activity("rpt", "task_failed")
        record_activity("rpt", "collaboration", {"partner_id": "other"})
        for _ in range(3):
            record_activity("rpt", "skill_used", {"skill": "python", "success": True})
        report = generate_report("rpt", period_hours=24)
        assert "agent_id" in report
        assert report["performance"]["completed"] == 5
        assert "summary" in report
        assert "strengths" in report
        assert "areas_for_improvement" in report
        assert "recommendations" in report
        assert "skill_growth" in report
        assert "activity_heatmap" in report

    def test_report_strengths_detected(self, tmp_path):
        from brynq.agent_analytics import record_activity, generate_report
        for _ in range(10):
            record_activity("strong", "task_completed")
        record_activity("strong", "collaboration", {"partner_id": "p1"})
        record_activity("strong", "collaboration", {"partner_id": "p2"})
        report = generate_report("strong", period_hours=24)
        assert any("success rate" in s.lower() for s in report["strengths"])
        assert any("collaborat" in s.lower() for s in report["strengths"])

    def test_report_areas_for_improvement(self, tmp_path):
        from brynq.agent_analytics import record_activity, generate_report
        record_activity("weak", "task_completed")
        for _ in range(5):
            record_activity("weak", "task_failed")
        report = generate_report("weak", period_hours=24)
        assert len(report["areas_for_improvement"]) >= 1
