"""Tests for colony agent runners."""

from pathlib import Path
from unittest.mock import AsyncMock, patch

import pytest

from fliiq.runtime.colony.agents import AGENT_TOOLS, _build_system_prompt, _get_capabilities_inventory, _get_reflection_context
from fliiq.runtime.colony.models import AgentReflection, AgentRole
from fliiq.runtime.colony.state import StateManager


@pytest.fixture()
def state(tmp_path):
    sm = StateManager(tmp_path)
    sm.ensure_dirs()
    return sm


class TestAgentTools:
    def test_intelligence_no_shell(self):
        assert "shell" not in AGENT_TOOLS["intelligence"]

    def test_intelligence_has_web(self):
        assert "web_search" in AGENT_TOOLS["intelligence"]
        assert "web_fetch" in AGENT_TOOLS["intelligence"]

    def test_governance_has_shell(self):
        assert "shell" in AGENT_TOOLS["governance"]
        assert "edit_file" in AGENT_TOOLS["governance"]

    def test_qa_has_shell(self):
        assert "shell" in AGENT_TOOLS["qa"]

    def test_scout_no_shell(self):
        assert "shell" not in AGENT_TOOLS["scout"]

    def test_research_no_shell(self):
        assert "shell" not in AGENT_TOOLS["research"]

    def test_no_agent_has_send_telegram(self):
        for _role, tools in AGENT_TOOLS.items():
            assert "send_telegram" not in tools

    def test_all_agents_have_read_file(self):
        for _role, tools in AGENT_TOOLS.items():
            assert "read_file" in tools


class TestReflectionContext:
    def test_empty_reflections(self, state):
        ctx = _get_reflection_context(state, AgentRole.INTELLIGENCE)
        assert "first cycle" in ctx.lower()

    def test_with_reflections(self, state):
        for i in range(3):
            state.append_reflection(AgentReflection(
                agent=AgentRole.RESEARCH,
                cycle_number=i,
                observation=f"Obs {i}",
                action_taken=f"Action {i}",
                outcome=f"Outcome {i}",
                lesson=f"Lesson {i}" if i > 0 else "",
            ))
        ctx = _get_reflection_context(state, AgentRole.RESEARCH)
        assert "Cycle 0" in ctx
        assert "Lesson 1" in ctx


class TestBuildSystemPrompt:
    def test_intelligence_prompt(self, state, tmp_path):
        # Create minimal project structure
        (tmp_path.parent / "README.md").write_text("# Test Project\nA test.")
        prompt = _build_system_prompt(AgentRole.INTELLIGENCE, state, tmp_path.parent)
        assert "Intelligence Agent" in prompt
        assert "Do NOT" in prompt

    def test_governance_prompt(self, state, tmp_path):
        (tmp_path.parent / "README.md").write_text("# Test")
        prompt = _build_system_prompt(AgentRole.GOVERNANCE, state, tmp_path.parent)
        assert "Governance Agent" in prompt
        assert "FINAL APPROVAL" in prompt

    def test_qa_prompt(self, state, tmp_path):
        (tmp_path.parent / "README.md").write_text("# Test")
        with patch("fliiq.runtime.colony.git_ops.get_diff_summary", return_value="no changes"):
            prompt = _build_system_prompt(AgentRole.QA, state, tmp_path.parent)
        assert "QA Agent" in prompt
        assert "pytest" in prompt

    def test_scout_prompt(self, state, tmp_path):
        (tmp_path.parent / "README.md").write_text("# Test")
        prompt = _build_system_prompt(AgentRole.SCOUT, state, tmp_path.parent)
        assert "Scout Agent" in prompt
        assert "Demand-Sensing" in prompt or "demand-sensing" in prompt.lower()

    def test_research_prompt(self, state, tmp_path):
        (tmp_path.parent / "README.md").write_text("# Test")
        prompt = _build_system_prompt(AgentRole.RESEARCH, state, tmp_path.parent)
        assert "Research Agent" in prompt

    def test_all_prompts_include_capabilities_section(self, state, tmp_path):
        (tmp_path.parent / "README.md").write_text("# Test")
        for role in AgentRole:
            with patch("fliiq.runtime.colony.git_ops.get_diff_summary", return_value=""):
                prompt = _build_system_prompt(role, state, tmp_path.parent)
            assert "Current Fliiq Capabilities" in prompt, f"Missing capabilities section for {role}"

    def test_unknown_role_raises(self, state, tmp_path):
        with pytest.raises(ValueError, match="Unknown agent role"):
            _build_system_prompt("invalid", state, tmp_path.parent)


class TestCapabilitiesInventory:
    def test_with_skills(self, tmp_path):
        # Create a minimal skill directory
        skill_dir = tmp_path / "fliiq" / "data" / "skills" / "core" / "test_skill"
        skill_dir.mkdir(parents=True)
        (skill_dir / "SKILL.md").write_text("---\nname: test_skill\ndescription: A test skill.\n---\n# test_skill\n")
        (skill_dir / "fliiq.yaml").write_text("input_schema:\n  type: object\n  properties: {}\n")
        (skill_dir / "main.py").write_text("async def handler(params: dict) -> dict:\n    return {}\n")

        with patch("fliiq.runtime.colony.agents.discover_skills") as mock_discover:
            from fliiq.runtime.skills.base import SkillBase
            mock_discover.return_value = {"test_skill": SkillBase(str(skill_dir))}
            result = _get_capabilities_inventory(tmp_path)

        assert "**test_skill**" in result
        assert "A test skill." in result

    def test_no_skills(self, tmp_path):
        with patch("fliiq.runtime.colony.agents.discover_skills", return_value={}):
            result = _get_capabilities_inventory(tmp_path)
        assert result == "No skills discovered."

    def test_discover_error(self, tmp_path):
        with patch("fliiq.runtime.colony.agents.discover_skills", side_effect=RuntimeError("fail")):
            result = _get_capabilities_inventory(tmp_path)
        assert "Could not load" in result
