"""Tests for the event bus system."""

import pytest

from agent_scout.events import Event, EventBus, EventType


@pytest.fixture
def bus():
    return EventBus()


def make_event(
    event_type: EventType = EventType.THOUGHT,
    data: dict | None = None,
    task_id: str | None = "task-1",
) -> Event:
    return Event(event_type=event_type, data=data or {}, task_id=task_id)


async def test_emit_calls_handler(bus: EventBus):
    received = []

    async def handler(event: Event):
        received.append(event)

    bus.subscribe(handler, EventType.THOUGHT)
    event = make_event()
    await bus.emit(event)

    assert len(received) == 1
    assert received[0] is event


async def test_wildcard_handler_receives_all_events(bus: EventBus):
    received = []

    async def handler(event: Event):
        received.append(event)

    bus.subscribe(handler, event_type=None)
    await bus.emit(make_event(EventType.THOUGHT))
    await bus.emit(make_event(EventType.ACTION))
    await bus.emit(make_event(EventType.ERROR))

    assert len(received) == 3


async def test_handler_only_receives_subscribed_type(bus: EventBus):
    received = []

    async def handler(event: Event):
        received.append(event)

    bus.subscribe(handler, EventType.THOUGHT)
    await bus.emit(make_event(EventType.THOUGHT))
    await bus.emit(make_event(EventType.ACTION))

    assert len(received) == 1
    assert received[0].event_type == EventType.THOUGHT


async def test_unsubscribe(bus: EventBus):
    received = []

    async def handler(event: Event):
        received.append(event)

    unsub = bus.subscribe(handler, EventType.THOUGHT)
    await bus.emit(make_event(EventType.THOUGHT))
    assert len(received) == 1

    unsub()
    await bus.emit(make_event(EventType.THOUGHT))
    assert len(received) == 1  # no new events after unsubscribe


async def test_multiple_handlers(bus: EventBus):
    results = {"a": 0, "b": 0}

    async def handler_a(event: Event):
        results["a"] += 1

    async def handler_b(event: Event):
        results["b"] += 1

    bus.subscribe(handler_a, EventType.THOUGHT)
    bus.subscribe(handler_b, EventType.THOUGHT)
    await bus.emit(make_event(EventType.THOUGHT))

    assert results["a"] == 1
    assert results["b"] == 1


async def test_event_history(bus: EventBus):
    await bus.emit(make_event(EventType.THOUGHT, task_id="t1"))
    await bus.emit(make_event(EventType.ACTION, task_id="t1"))
    await bus.emit(make_event(EventType.THOUGHT, task_id="t2"))

    assert len(bus.get_history()) == 3
    assert len(bus.get_history(event_type=EventType.THOUGHT)) == 2
    assert len(bus.get_history(task_id="t1")) == 2
    assert len(bus.get_history(event_type=EventType.THOUGHT, task_id="t1")) == 1


async def test_event_history_limit(bus: EventBus):
    for i in range(10):
        await bus.emit(make_event(data={"i": i}))

    assert len(bus.get_history(limit=3)) == 3


async def test_event_to_dict():
    event = make_event(EventType.TOOL_CALL_START, data={"tool": "web_search"})
    d = event.to_dict()

    assert d["event_type"] == "tool_call_start"
    assert d["data"]["tool"] == "web_search"
    assert "event_id" in d
    assert "timestamp" in d


async def test_handler_count(bus: EventBus):
    async def handler(event: Event):
        pass

    assert bus.handler_count == 0
    bus.subscribe(handler, EventType.THOUGHT)
    assert bus.handler_count == 1
    bus.subscribe(handler, EventType.ACTION)
    assert bus.handler_count == 2


async def test_handler_exception_does_not_crash_bus(bus: EventBus):
    received = []

    async def bad_handler(event: Event):
        raise ValueError("boom")

    async def good_handler(event: Event):
        received.append(event)

    bus.subscribe(bad_handler, EventType.THOUGHT)
    bus.subscribe(good_handler, EventType.THOUGHT)

    # Should not raise — exceptions gathered silently
    await bus.emit(make_event(EventType.THOUGHT))
    assert len(received) == 1


async def test_clear_history(bus: EventBus):
    await bus.emit(make_event())
    assert len(bus.get_history()) == 1
    bus.clear_history()
    assert len(bus.get_history()) == 0
