"""Tests for timmy.agents.timmy — orchestrator, personas, context building."""

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

import pytest

# Ensure mcp.registry stub with tool_registry exists before importing agents
if "mcp" not in sys.modules:
    _mock_mcp = MagicMock()
    _mock_registry_mod = MagicMock()
    _mock_tool_reg = MagicMock()
    _mock_tool_reg.get_handler.return_value = None
    _mock_registry_mod.tool_registry = _mock_tool_reg
    sys.modules["mcp"] = _mock_mcp
    sys.modules["mcp.registry"] = _mock_registry_mod

from timmy.agents.timmy import (
    _PERSONAS,
    ORCHESTRATOR_PROMPT_BASE,
    TimmyOrchestrator,
    _load_hands_async,
    build_timmy_context_async,
    build_timmy_context_sync,
    create_timmy_swarm,
    format_timmy_prompt,
)


class TestLoadHandsAsync:
    """Test _load_hands_async."""

    async def test_returns_empty_list(self):
        result = await _load_hands_async()
        assert result == []


class TestBuildContext:
    """Test context building functions."""

    @patch("timmy.agents.timmy.settings")
    def test_build_context_sync_graceful_failures(self, mock_settings):
        mock_settings.repo_root = "/nonexistent"
        ctx = build_timmy_context_sync()

        assert "timestamp" in ctx
        assert isinstance(ctx["agents"], list)
        assert isinstance(ctx["hands"], list)
        # Git log should fall back gracefully
        assert isinstance(ctx["git_log"], str)
        # Memory should fall back gracefully
        assert isinstance(ctx["memory"], str)

    @patch("timmy.agents.timmy.settings")
    async def test_build_context_async(self, mock_settings):
        mock_settings.repo_root = "/nonexistent"
        ctx = await build_timmy_context_async()
        assert ctx["hands"] == []

    @patch("timmy.agents.timmy.settings")
    def test_build_context_reads_memory_file(self, mock_settings, tmp_path):
        memory_file = tmp_path / "MEMORY.md"
        memory_file.write_text("# Important memories\nRemember this.")
        mock_settings.repo_root = str(tmp_path)

        # Patch HotMemory path so it reads from tmp_path
        from timmy.memory_system import memory_system

        original_path = memory_system.hot.path
        memory_system.hot.path = memory_file
        memory_system.hot._content = None  # Clear cache
        try:
            ctx = build_timmy_context_sync()
            assert "Important memories" in ctx["memory"]
        finally:
            memory_system.hot.path = original_path
            memory_system.hot._content = None


class TestFormatPrompt:
    """Test format_timmy_prompt."""

    def test_inserts_context_block(self):
        base = "Line one.\nLine two."
        ctx = {
            "timestamp": "2026-03-06T00:00:00Z",
            "repo_root": "/home/user/project",
            "git_log": "abc123 initial commit",
            "agents": [],
            "hands": [],
            "memory": "some memory",
        }
        result = format_timmy_prompt(base, ctx)
        assert "Line one." in result
        assert "Line two." in result
        assert "abc123 initial commit" in result
        assert "some memory" in result

    def test_agents_list_formatted(self):
        ctx = {
            "timestamp": "now",
            "repo_root": "/tmp",
            "git_log": "",
            "agents": [
                {"name": "Forge", "capabilities": "code", "status": "ready"},
                {"name": "Seer", "capabilities": "research", "status": "ready"},
            ],
            "hands": [],
            "memory": "",
        }
        result = format_timmy_prompt("Base.", ctx)
        assert "Forge" in result
        assert "Seer" in result

    def test_hands_list_formatted(self):
        ctx = {
            "timestamp": "now",
            "repo_root": "/tmp",
            "git_log": "",
            "agents": [],
            "hands": [
                {"name": "backup", "schedule": "daily", "enabled": True},
            ],
            "memory": "",
        }
        result = format_timmy_prompt("Base.", ctx)
        assert "backup" in result
        assert "enabled" in result

    def test_repo_root_placeholder_replaced(self):
        ctx = {
            "timestamp": "now",
            "repo_root": "/my/repo",
            "git_log": "",
            "agents": [],
            "hands": [],
            "memory": "",
        }
        result = format_timmy_prompt("Root is {REPO_ROOT}.", ctx)
        assert "/my/repo" in result
        assert "{REPO_ROOT}" not in result


class TestExtractAgent:
    """Test TimmyOrchestrator._extract_agent static method."""

    def test_extracts_known_agents(self):
        assert TimmyOrchestrator._extract_agent("Primary Agent: Seer") == "seer"
        assert TimmyOrchestrator._extract_agent("Use Forge for this") == "forge"
        assert TimmyOrchestrator._extract_agent("Route to quill") == "quill"
        assert TimmyOrchestrator._extract_agent("echo can recall") == "echo"
        assert TimmyOrchestrator._extract_agent("helm decides") == "helm"

    def test_defaults_to_orchestrator(self):
        assert TimmyOrchestrator._extract_agent("no agent mentioned") == "orchestrator"

    def test_case_insensitive(self):
        assert TimmyOrchestrator._extract_agent("Use FORGE") == "forge"


class TestTimmyOrchestrator:
    """Test TimmyOrchestrator init and methods."""

    @patch("timmy.agents.timmy.settings")
    def test_init(self, mock_settings):
        mock_settings.repo_root = "/tmp"
        mock_settings.ollama_model = "test"
        mock_settings.ollama_url = "http://localhost:11434"
        mock_settings.telemetry_enabled = False

        orch = TimmyOrchestrator()
        assert orch.agent_id == "orchestrator"
        assert orch.name == "Orchestrator"
        assert orch.sub_agents == {}
        assert orch._session_initialized is False

    @patch("timmy.agents.timmy.settings")
    def test_register_sub_agent(self, mock_settings):
        mock_settings.repo_root = "/tmp"
        mock_settings.ollama_model = "test"
        mock_settings.ollama_url = "http://localhost:11434"
        mock_settings.telemetry_enabled = False

        orch = TimmyOrchestrator()

        from timmy.agents.base import SubAgent

        agent = SubAgent(
            agent_id="test-agent",
            name="Test",
            role="test",
            system_prompt="You are a test agent.",
        )
        orch.register_sub_agent(agent)
        assert "test-agent" in orch.sub_agents

    @patch("timmy.agents.timmy.settings")
    def test_get_swarm_status(self, mock_settings):
        mock_settings.repo_root = "/tmp"
        mock_settings.ollama_model = "test"
        mock_settings.ollama_url = "http://localhost:11434"
        mock_settings.telemetry_enabled = False

        orch = TimmyOrchestrator()
        status = orch.get_swarm_status()
        assert "orchestrator" in status
        assert status["total_agents"] == 1

    @patch("timmy.agents.timmy.settings")
    def test_get_enhanced_system_prompt_with_attr(self, mock_settings):
        mock_settings.repo_root = "/tmp"
        mock_settings.ollama_model = "test"
        mock_settings.ollama_url = "http://localhost:11434"
        mock_settings.telemetry_enabled = False

        orch = TimmyOrchestrator()
        # BaseAgent doesn't store system_prompt as attr; set it manually
        orch.system_prompt = "Test prompt.\nWith context."
        prompt = orch._get_enhanced_system_prompt()
        assert isinstance(prompt, str)
        assert "Test prompt." in prompt


class TestCreateTimmySwarm:
    """Test create_timmy_swarm factory."""

    @patch("timmy.agents.timmy.settings")
    def test_creates_all_personas(self, mock_settings):
        mock_settings.repo_root = "/tmp"
        mock_settings.ollama_model = "test"
        mock_settings.ollama_url = "http://localhost:11434"
        mock_settings.telemetry_enabled = False

        swarm = create_timmy_swarm()
        assert len(swarm.sub_agents) == len(_PERSONAS)
        assert "seer" in swarm.sub_agents
        assert "forge" in swarm.sub_agents
        assert "quill" in swarm.sub_agents
        assert "echo" in swarm.sub_agents
        assert "helm" in swarm.sub_agents


class TestPersonas:
    """Test persona definitions."""

    def test_all_personas_have_required_fields(self):
        required = {"agent_id", "name", "role", "system_prompt"}
        for persona in _PERSONAS:
            assert required.issubset(persona.keys()), f"Missing fields in {persona['name']}"

    def test_persona_ids_unique(self):
        ids = [p["agent_id"] for p in _PERSONAS]
        assert len(ids) == len(set(ids))

    def test_six_personas(self):
        assert len(_PERSONAS) == 6


class TestOrchestratorPrompt:
    """Test the ORCHESTRATOR_PROMPT_BASE constant."""

    def test_contains_hard_rules(self):
        assert "NEVER fabricate" in ORCHESTRATOR_PROMPT_BASE
        assert "do not know" in ORCHESTRATOR_PROMPT_BASE.lower()

    def test_contains_repo_root_placeholder(self):
        assert "{REPO_ROOT}" in ORCHESTRATOR_PROMPT_BASE
