"""
Tests for core Brynq modules (Phases 1-12).
Covers: swarm, incentives (pure functions), personality.
"""

import json
import tempfile
from pathlib import Path

import pytest


# ── Phase 12: Swarm ────────────────────────────────────────────────────────


class TestSwarm:
    """Swarm tests redirect SWARM_DIR to a temp directory."""

    def setup_method(self):
        import brynq.swarm as sw
        self._orig_dir = sw.SWARM_DIR
        sw.SWARM_DIR = Path(tempfile.mkdtemp())

    def teardown_method(self):
        import brynq.swarm as sw
        sw.SWARM_DIR = self._orig_dir

    def test_create_swarm(self):
        from brynq.swarm import create_swarm
        sid = create_swarm("task-1", ["python", "review"])
        assert isinstance(sid, str)
        assert len(sid) == 12

    def test_submit_bid(self):
        from brynq.swarm import create_swarm, submit_bid
        sid = create_swarm("task-2", ["python"])
        result = submit_bid(sid, "agent-1", reputation=85, skill_match=1.0)
        assert result is True

    def test_bid_on_nonexistent_swarm(self):
        from brynq.swarm import submit_bid
        result = submit_bid("doesnotexist", "agent-1", 50, 0.5)
        assert result is False

    def test_form_swarm_assigns_roles(self):
        from brynq.swarm import create_swarm, submit_bid, form_swarm
        sid = create_swarm("task-3", ["python", "review"], max_size=3)
        submit_bid(sid, "agent-A", reputation=95, skill_match=1.0)
        submit_bid(sid, "agent-B", reputation=80, skill_match=0.8)
        submit_bid(sid, "agent-C", reputation=70, skill_match=0.6)

        result = form_swarm(sid)
        assert result is True

        # Verify roles were assigned
        import brynq.swarm as sw
        swarm = json.loads((sw.SWARM_DIR / f"{sid}.json").read_text("utf-8"))
        assert swarm["status"] == "active"
        assert swarm["members"]["agent-A"] == "lead"
        assert len(swarm["members"]) == 3

    def test_form_swarm_no_bids(self):
        from brynq.swarm import create_swarm, form_swarm
        sid = create_swarm("task-4", ["python"])
        result = form_swarm(sid)
        assert result is False

    def test_propose_and_vote(self):
        from brynq.swarm import (
            create_swarm, submit_bid, form_swarm,
            propose_decision, vote_on_decision,
        )
        sid = create_swarm("task-5", ["python"], max_size=3)
        submit_bid(sid, "agent-X", 90, 1.0)
        submit_bid(sid, "agent-Y", 80, 0.9)
        submit_bid(sid, "agent-Z", 70, 0.8)
        form_swarm(sid)

        # Propose
        assert propose_decision(sid, "prop-1", "Should we deploy?") is True

        # Vote — 2 out of 3 yes should approve (quorum = int(3 * 0.66) = 1)
        r1 = vote_on_decision(sid, "prop-1", "agent-X", True)
        assert r1 == "approved"

    def test_vote_non_member_rejected(self):
        from brynq.swarm import (
            create_swarm, submit_bid, form_swarm,
            propose_decision, vote_on_decision,
        )
        sid = create_swarm("task-6", ["python"])
        submit_bid(sid, "agent-M", 90, 1.0)
        form_swarm(sid)
        propose_decision(sid, "prop-2", "Test proposal")

        result = vote_on_decision(sid, "prop-2", "outsider", True)
        assert result == "error"

    def test_dissolve_swarm(self):
        from brynq.swarm import create_swarm, dissolve_swarm
        sid = create_swarm("task-7", ["python"])
        result = dissolve_swarm(sid)
        assert result is True

        import brynq.swarm as sw
        swarm = json.loads((sw.SWARM_DIR / f"{sid}.json").read_text("utf-8"))
        assert swarm["status"] == "dissolved"

    def test_dissolve_nonexistent(self):
        from brynq.swarm import dissolve_swarm
        assert dissolve_swarm("ghost") is False

    def test_duplicate_proposal_rejected(self):
        from brynq.swarm import create_swarm, submit_bid, form_swarm, propose_decision
        sid = create_swarm("task-8", ["python"])
        submit_bid(sid, "agent-1", 90, 1.0)
        form_swarm(sid)

        assert propose_decision(sid, "dup-1", "First") is True
        assert propose_decision(sid, "dup-1", "Duplicate") is False

    def test_bid_after_formation_rejected(self):
        from brynq.swarm import create_swarm, submit_bid, form_swarm
        sid = create_swarm("task-9", ["python"])
        submit_bid(sid, "agent-1", 90, 1.0)
        form_swarm(sid)

        # Swarm is now active, bidding should fail
        result = submit_bid(sid, "agent-late", 50, 0.5)
        assert result is False


# ── Phase 8: Incentives (pure functions) ───────────────────────────────────


class TestIncentivesPure:
    """Test pure functions that don't need filesystem."""

    def test_get_level_rookie(self):
        from brynq.incentives import get_level
        assert get_level(0) == "Rookie"
        assert get_level(49) == "Rookie"

    def test_get_level_contributor(self):
        from brynq.incentives import get_level
        assert get_level(50) == "Contributor"
        assert get_level(199) == "Contributor"

    def test_get_level_specialist(self):
        from brynq.incentives import get_level
        assert get_level(200) == "Specialist"

    def test_get_level_expert(self):
        from brynq.incentives import get_level
        assert get_level(500) == "Expert"

    def test_get_level_legend(self):
        from brynq.incentives import get_level
        assert get_level(1000) == "Legend"

    def test_get_level_mythic(self):
        from brynq.incentives import get_level
        assert get_level(2500) == "Mythic"

    def test_level_progress_rookie(self):
        from brynq.incentives import get_level_progress
        p = get_level_progress(25)
        assert p["level"] == "Rookie"
        assert p["next_level"] == "Contributor"
        assert p["progress_pct"] == 50.0

    def test_level_progress_at_boundary(self):
        from brynq.incentives import get_level_progress
        p = get_level_progress(50)
        assert p["level"] == "Contributor"

    def test_level_progress_mythic_no_next(self):
        from brynq.incentives import get_level_progress
        p = get_level_progress(5000)
        assert p["level"] == "Mythic"
        assert p["next_level"] is None

    def test_xp_rewards_exist(self):
        from brynq.incentives import XP_REWARDS
        assert "task_complete" in XP_REWARDS
        assert XP_REWARDS["task_complete"] == 10

    def test_achievement_defs_exist(self):
        from brynq.incentives import ACHIEVEMENT_DEFS
        assert "first_blood" in ACHIEVEMENT_DEFS
        assert "hat_trick" in ACHIEVEMENT_DEFS
        assert len(ACHIEVEMENT_DEFS) >= 8

    def test_achievement_conditions(self):
        from brynq.incentives import ACHIEVEMENT_DEFS
        # first_blood: 1 completed task
        assert ACHIEVEMENT_DEFS["first_blood"]["condition"]({"tasks_completed": 1}) is True
        assert ACHIEVEMENT_DEFS["first_blood"]["condition"]({"tasks_completed": 0}) is False

    def test_speed_demon_condition(self):
        from brynq.incentives import ACHIEVEMENT_DEFS
        assert ACHIEVEMENT_DEFS["speed_demon"]["condition"]({"fastest_task_secs": 30}) is True
        assert ACHIEVEMENT_DEFS["speed_demon"]["condition"]({"fastest_task_secs": 90}) is False


# ── Phase 10: Personality ──────────────────────────────────────────────────


class TestPersonality:
    def test_list_templates(self):
        from brynq.personality import list_templates
        templates = list_templates()
        assert isinstance(templates, list)
        assert len(templates) >= 8

    def test_assign_personality(self):
        from brynq.personality import assign_personality
        result = assign_personality("The Captain")
        assert result is not None

    def test_assign_invalid_personality(self):
        from brynq.personality import assign_personality
        with pytest.raises(ValueError):
            assign_personality("Nonexistent Template")


# ── Cross-module import checks ─────────────────────────────────────────────


class TestImports:
    """Verify all phase modules import cleanly."""

    def test_import_server(self):
        import brynq.server
        assert hasattr(brynq.server, 'BROKER_DIR')

    def test_import_tasks(self):
        import brynq.tasks
        assert hasattr(brynq.tasks, 'create_task')

    def test_import_capability(self):
        import brynq.capability
        assert hasattr(brynq.capability, 'register_capability')

    def test_import_swarm(self):
        import brynq.swarm
        assert hasattr(brynq.swarm, 'create_swarm')

    def test_import_incentives(self):
        import brynq.incentives
        assert hasattr(brynq.incentives, 'get_level')

    def test_import_personality(self):
        import brynq.personality
        assert hasattr(brynq.personality, 'list_templates')

    def test_import_profiles(self):
        import brynq.profiles
        assert True  # Import succeeded

    def test_import_jobs(self):
        import brynq.jobs
        assert True

    def test_import_enforcer(self):
        import brynq.enforcer
        assert True

    def test_import_guardrails(self):
        import brynq.guardrails
        assert True

    def test_import_billing(self):
        import brynq.billing
        assert hasattr(brynq.billing, '_configured')

    def test_import_web(self):
        import brynq.web
        assert True
