"""Tests for fs_watcher.transports.callback — direct-callback transport."""

from __future__ import annotations

import threading
import time

import pytest

from recoil.pipeline._lib.fs_watcher.events import FsEvent, FsEventType
from recoil.pipeline._lib.fs_watcher.pubsub import EventBroker
from recoil.pipeline._lib.fs_watcher.transports.callback import CallbackTransport


def _make_event(event_id: str) -> FsEvent:
    return FsEvent(
        event_id=event_id,
        event_type=FsEventType.MODIFIED,
        path="projects/test/file.txt",
        project="test",
        asset_type=None,
        asset_id=None,
        src_path=None,
        is_directory=False,
        size_bytes=100,
        sha256=None,
        mtime=1700000000.0,
        ts=1700000001.0,
    )


def test_callback_invoked_on_publish():
    broker = EventBroker()
    received: list[FsEvent] = []
    transport = CallbackTransport(broker, callback=received.append)
    transport.start()

    try:
        broker.publish(_make_event("evt_1"))
        # Let the background thread pick it up
        for _ in range(20):
            if received:
                break
            time.sleep(0.05)

        assert len(received) == 1
        assert received[0].event_id == "evt_1"
    finally:
        transport.stop()


def test_callback_multiple_events_in_order():
    broker = EventBroker()
    received: list[FsEvent] = []
    transport = CallbackTransport(broker, callback=received.append)
    transport.start()

    try:
        for i in range(5):
            broker.publish(_make_event(f"evt_{i}"))

        # Wait for all events to be dispatched
        for _ in range(40):
            if len(received) >= 5:
                break
            time.sleep(0.05)

        ids = [ev.event_id for ev in received]
        assert ids == ["evt_0", "evt_1", "evt_2", "evt_3", "evt_4"]
    finally:
        transport.stop()


def test_stop_unsubscribes_cleanly():
    broker = EventBroker()
    received: list[FsEvent] = []
    transport = CallbackTransport(broker, callback=received.append)
    transport.start()
    transport.stop()

    # After stop, publish should not reach the callback
    broker.publish(_make_event("evt_after_stop"))
    time.sleep(0.1)
    assert received == []
