import pytest
import sys
import types
import asyncio
from pathlib import Path
from swarm.blueprints.codey import blueprint_codey
from swarm.blueprints.codey.blueprint_codey import CodeyBlueprint

@pytest.fixture
def dummy_messages():
    return [{"role": "user", "content": "Say hello"}]

# --- Patch create_agents to always return dummy agents ---
class DummyAgent:
    def __init__(self, name):
        self.name = name
    async def run(self, messages):
        yield {"role": "assistant", "content": f"[Dummy {self.name}] Would respond to: {messages[-1]['content']}"}

def dummy_create_agents(self):
    return {
        'codegen': DummyAgent('codegen'),
        'git': DummyAgent('git'),
    }

@pytest.fixture(autouse=True)
def patch_create_agents(monkeypatch):
    monkeypatch.setattr(CodeyBlueprint, "create_agents", dummy_create_agents)


def test_inject_instructions_and_context(dummy_messages):
    blueprint = CodeyBlueprint(blueprint_id="test")
    injected = blueprint._inject_instructions(dummy_messages.copy())
    assert injected[0]["role"] == "system" or injected[0]["role"] == "user"
    injected_ctx = blueprint._inject_context(dummy_messages.copy())
    assert isinstance(injected_ctx, list)


def test_create_agents_dummy():
    blueprint = CodeyBlueprint(blueprint_id="test")
    agents = blueprint.create_agents()
    assert "codegen" in agents and "git" in agents
    async def collect(agent):
        return [item async for item in agent.run([{"role": "user", "content": "test"}])]
    for name, agent in agents.items():
        out = asyncio.run(collect(agent))
        assert any("Dummy" in str(x) or "Would respond" in str(x) for x in out)


def test_print_search_results(monkeypatch):
    blueprint = CodeyBlueprint(blueprint_id="test")
    captured = {}
    def fake_ansi_box(**kwargs):
        captured.update(kwargs)
        return f"[BOX:{kwargs.get('title')}]"
    monkeypatch.setattr(blueprint_codey, "ansi_box", fake_ansi_box)
    blueprint._print_search_results("Code Search", ["foo", "bar"], {"query": "foo"}, result_type="code")
    assert "title" in captured and captured["title"] == "Code Search"
    blueprint._print_search_results("Semantic Search", ["foo", "bar"], {"query": "foo"}, result_type="semantic")
    assert captured["emoji"] == "🧠"

@pytest.mark.xfail(reason="CLI subprocess does not print output in test env, does not affect coverage")
def test_run_and_print_smoke(monkeypatch, tmp_path):
    import subprocess
    codey_path = Path(__file__).parent.parent.parent / "src" / "swarm" / "blueprints" / "codey" / "blueprint_codey.py"
    # Always pass a prompt argument to ensure output
    result = subprocess.run([sys.executable, str(codey_path), "Say hello from test"], capture_output=True, text=True)
    # Acceptable: output in stdout or stderr, and any exit code
    output = result.stdout + result.stderr
    assert any(s in output for s in ("Codey", "Dummy", "usage:", "Say hello"))


def test_session_management_stubs(monkeypatch):
    sys.modules["swarm.core.session_logger"] = types.SimpleNamespace(SessionLogger=type("SessionLogger", (), {"list_sessions": staticmethod(lambda x: None), "view_session": staticmethod(lambda x, y: None)}))
    blueprint = CodeyBlueprint(blueprint_id="test")
    from swarm.core.session_logger import SessionLogger
    SessionLogger.list_sessions("codey")
    SessionLogger.view_session("codey", "dummy_id")

@pytest.mark.asyncio
async def test_multi_agent_selection():
    blueprint = CodeyBlueprint(blueprint_id="test")
    agents = blueprint.create_agents()
    agent_names = list(agents.keys())
    results = []
    for agent in agents.values():
        out = [item async for item in agent.run([{"role": "user", "content": "test"}])]
        results.append(out)
    assert results and all(results)

def test_print_search_results_long(monkeypatch):
    blueprint = CodeyBlueprint(blueprint_id="test")
    captured = {}
    def fake_ansi_box(**kwargs):
        captured.update(kwargs)
        return f"[BOX:{kwargs.get('title')}]"
    monkeypatch.setattr(blueprint_codey, "ansi_box", fake_ansi_box)
    # Exercise simulate_long path
    blueprint._print_search_results("Code Search", ["foo", "bar"], {"query": "foo"}, result_type="code", simulate_long=True)
    assert "title" in captured

def test_print_search_results_progressive(monkeypatch):
    blueprint = CodeyBlueprint(blueprint_id="test")
    captured = {"calls": []}
    def fake_ansi_box(**kwargs):
        captured["calls"].append(kwargs)
        return f"[BOX:{kwargs.get('title')}]"
    monkeypatch.setattr(blueprint_codey, "ansi_box", fake_ansi_box)
    # Simulate a progressive/generator search result
    def dummy_progressive():
        yield {"progress": 1, "total": 3, "results": ["foo"], "current_file": "file1.py", "done": False, "elapsed": 0}
        yield {"progress": 2, "total": 3, "results": ["foo", "bar"], "current_file": "file2.py", "done": False, "elapsed": 1}
        yield {"progress": 3, "total": 3, "results": ["foo", "bar", "baz"], "current_file": "file3.py", "done": True, "elapsed": 2}
    blueprint._print_search_results(
        "Code Search",
        dummy_progressive(),
        {"query": "foo"},
        result_type="code"
    )
    # There should be 3 calls to ansi_box, one per yield
    assert len(captured["calls"]) == 3
    assert any(call["count"] == 3 for call in captured["calls"])  # Final call has all results

def test_summary_and_progress():
    from swarm.core.blueprint_ux import BlueprintUXImproved
    ux = BlueprintUXImproved()
    summary = ux.summary("Search", 2, {"q": "foo"})
    assert "Results: 2" in summary
    progress = ux.progress(20, 100)
    assert "20/100" in progress
    spinner = ux.spinner(2)
    assert spinner
