"""
Tests for Phase 24: Emergent Specialization.
"""

import json
from pathlib import Path

import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    channels = tmp_path / "channels"
    spec = tmp_path / "specialization"
    agents = spec / "agents"
    for d in (channels, spec, agents):
        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)

    import brynq.specialization as sp
    monkeypatch.setattr(sp, "SPEC_DIR", spec)
    monkeypatch.setattr(sp, "AGENTS_DIR", agents)
    monkeypatch.setattr(sp, "CLUSTERS_FILE", spec / "clusters.json")
    monkeypatch.setattr(sp, "CHANNELS_DIR", channels)


def _record_tasks(session_id, category, skills, n=5, rating=4.5):
    from brynq.specialization import record_task_performance
    for i in range(n):
        record_task_performance(
            session_id=session_id,
            task_id=f"task-{i}",
            category=category,
            skills_used=skills,
            rating=rating,
            duration_seconds=60.0,
        )


class TestPerformanceRecording:
    def test_record_task(self):
        from brynq.specialization import record_task_performance
        entry = record_task_performance("a1", "t1", "coding", ["python"], 4.5, 120.0)
        assert entry["category"] == "coding"
        assert entry["rating"] == 4.5

    def test_rating_clamp(self):
        from brynq.specialization import record_task_performance
        e = record_task_performance("a1", "t2", "coding", ["python"], 10.0, 60.0)
        assert e["rating"] == 5.0
        e2 = record_task_performance("a1", "t3", "coding", ["python"], -1.0, 60.0)
        assert e2["rating"] == 1.0

    def test_get_history(self):
        from brynq.specialization import record_task_performance, get_performance_history
        record_task_performance("h1", "t1", "coding", ["python"], 4.0, 60.0)
        record_task_performance("h1", "t2", "testing", ["pytest"], 5.0, 30.0)
        history = get_performance_history("h1")
        assert len(history) == 2

    def test_get_history_filter_category(self):
        from brynq.specialization import record_task_performance, get_performance_history
        record_task_performance("h2", "t1", "coding", ["python"], 4.0, 60.0)
        record_task_performance("h2", "t2", "testing", ["pytest"], 5.0, 30.0)
        coding = get_performance_history("h2", category="coding")
        assert len(coding) == 1

    def test_empty_history(self):
        from brynq.specialization import get_performance_history
        assert get_performance_history("nobody") == []


class TestAnalysis:
    def test_analyze_empty(self):
        from brynq.specialization import analyze_strengths
        result = analyze_strengths("empty")
        assert result["total_tasks"] == 0
        assert result["strengths"] == []

    def test_analyze_strengths(self):
        _record_tasks("strong", "coding", ["python", "fastapi"], n=10, rating=4.8)
        from brynq.specialization import analyze_strengths
        result = analyze_strengths("strong")
        assert result["total_tasks"] == 10
        assert len(result["strengths"]) >= 1
        assert result["strengths"][0]["category"] == "coding"
        assert result["strengths"][0]["avg_rating"] >= 4.5

    def test_analyze_weaknesses(self):
        _record_tasks("weak", "coding", ["python"], n=5, rating=2.0)
        from brynq.specialization import analyze_strengths
        result = analyze_strengths("weak")
        assert len(result["weaknesses"]) >= 1

    def test_recommended_skills(self):
        _record_tasks("skilled", "coding", ["python", "asyncio"], n=5, rating=4.5)
        from brynq.specialization import analyze_strengths
        result = analyze_strengths("skilled")
        skill_names = [r["skill"] for r in result["recommended_skills"]]
        assert "python" in skill_names

    def test_specialization_score(self):
        # Agent focused on one category = high specialization
        _record_tasks("focused", "coding", ["python"], n=10, rating=4.5)
        from brynq.specialization import analyze_strengths
        r1 = analyze_strengths("focused")
        assert r1["specialization_score"] == 1.0

        # Agent spread across multiple categories = lower specialization
        _record_tasks("spread", "coding", ["python"], n=5, rating=4.5)
        _record_tasks("spread", "testing", ["pytest"], n=5, rating=4.5)
        _record_tasks("spread", "review", ["code_review"], n=5, rating=4.5)
        r2 = analyze_strengths("spread")
        assert r2["specialization_score"] < 1.0


class TestEvolution:
    def test_evolve_no_apply(self):
        _record_tasks("evo1", "coding", ["python", "rust"], n=5, rating=4.8)
        from brynq.specialization import evolve_skills
        result = evolve_skills("evo1", apply=False)
        assert result["applied"] is False
        assert len(result["recommended_additions"]) >= 1

    def test_evolve_empty(self):
        from brynq.specialization import evolve_skills
        result = evolve_skills("nobody")
        assert result["recommended_additions"] == []


class TestArchetypes:
    def test_newcomer(self):
        from brynq.specialization import get_agent_archetype
        result = get_agent_archetype("newbie")
        assert result["archetype"] == "newcomer"

    def test_builder_archetype(self):
        _record_tasks("dev", "coding", ["python"], n=10, rating=4.8)
        from brynq.specialization import get_agent_archetype
        result = get_agent_archetype("dev")
        assert result["archetype"] == "builder"
        assert result["confidence"] > 0.3

    def test_guardian_archetype(self):
        _record_tasks("guard", "testing", ["pytest"], n=8, rating=4.5)
        _record_tasks("guard", "security", ["scanning"], n=5, rating=4.7)
        from brynq.specialization import get_agent_archetype
        result = get_agent_archetype("guard")
        assert result["archetype"] == "guardian"

    def test_generalist(self):
        _record_tasks("gen", "coding", ["python"], n=3, rating=4.0)
        _record_tasks("gen", "testing", ["pytest"], n=3, rating=4.0)
        _record_tasks("gen", "review", ["code_review"], n=3, rating=4.0)
        _record_tasks("gen", "architecture", ["design"], n=3, rating=4.0)
        from brynq.specialization import get_agent_archetype
        result = get_agent_archetype("gen")
        assert result["archetype"] in ("generalist", "builder", "guardian", "architect")


class TestClusters:
    def test_compute_empty(self):
        from brynq.specialization import compute_clusters
        result = compute_clusters()
        assert result["total_agents"] == 0

    def test_compute_clusters(self):
        _record_tasks("dev1", "coding", ["python"], n=10, rating=4.8)
        _record_tasks("dev2", "coding", ["rust"], n=10, rating=4.5)
        _record_tasks("qa1", "testing", ["pytest"], n=10, rating=4.7)
        from brynq.specialization import compute_clusters
        result = compute_clusters()
        assert result["total_agents"] == 3
        assert "builder" in result["clusters"]
        assert "guardian" in result["clusters"]

    def test_get_clusters_cached(self):
        _record_tasks("x", "coding", ["python"], n=5, rating=4.5)
        from brynq.specialization import compute_clusters, get_clusters
        compute_clusters()
        cached = get_clusters()
        assert cached["total_agents"] > 0

    def test_get_clusters_empty(self):
        from brynq.specialization import get_clusters
        result = get_clusters()
        assert result["total_agents"] == 0


class TestFindSpecialist:
    def test_find_specialist(self):
        _record_tasks("expert", "coding", ["python"], n=15, rating=5.0)
        _record_tasks("novice", "coding", ["python"], n=3, rating=3.5)
        from brynq.specialization import find_specialist
        results = find_specialist("coding")
        assert len(results) >= 1
        assert results[0]["session_id"] == "expert"

    def test_find_specialist_none(self):
        from brynq.specialization import find_specialist
        results = find_specialist("quantum_computing")
        assert results == []
