"""Unit tests for BridgeController synchronization behavior."""

from __future__ import annotations

import pytest

from tests._support.controller import BridgeController

pytestmark = pytest.mark.unit


class _FakeUth:
    def __init__(self, scripted_reads: list[tuple[str, object]]) -> None:
        self._reads = scripted_reads
        self.put_calls: list[tuple[str, object]] = []

    def pvput(self, pv: str, value: object) -> None:
        self.put_calls.append((pv, value))

    def pvget(self, pv: str):
        if not self._reads:
            raise AssertionError(
                f"Unexpected read for {pv!r}; no scripted values left."
            )
        expected_pv, value = self._reads.pop(0)
        assert pv == expected_pv, f"Expected pvget({expected_pv!r}), got pvget({pv!r})"
        return value


def test_run_task_cycle_allows_missed_busy_high(monkeypatch) -> None:
    """Cycle succeeds even if polling never samples Busy=1."""

    def _fast_wait_until(predicate, timeout=10.0, interval=0.1, message="Timeout"):
        for _ in range(3):
            if predicate():
                return True
        raise TimeoutError(message)

    monkeypatch.setattr("tests._support.controller.wait_until", _fast_wait_until)

    prefix = "Test:Bridge:"
    reads = [
        (f"{prefix}Busy", 0),
        (f"{prefix}Trigger", 0),
        (f"{prefix}Busy", 0),
        (f"{prefix}Trigger", 0),
        (f"{prefix}TaskStatus", 0),
    ]
    uth = _FakeUth(reads)
    controller = BridgeController(uth, prefix=prefix)

    controller.run_task_cycle(timeout=0.1)

    assert uth.put_calls == [(f"{prefix}Trigger", 1)]


def test_run_task_cycle_times_out_when_trigger_not_consumed(monkeypatch) -> None:
    """Cycle fails when daemon never consumes Trigger request."""

    def _fast_wait_until(predicate, timeout=10.0, interval=0.1, message="Timeout"):
        for _ in range(3):
            if predicate():
                return True
        raise TimeoutError(message)

    monkeypatch.setattr("tests._support.controller.wait_until", _fast_wait_until)

    prefix = "Test:Bridge:"
    reads = [
        (f"{prefix}Busy", 0),
        (f"{prefix}Trigger", 1),
        (f"{prefix}Busy", 0),
        (f"{prefix}Trigger", 1),
        (f"{prefix}Busy", 0),
        (f"{prefix}Trigger", 1),
    ]
    uth = _FakeUth(reads)
    controller = BridgeController(uth, prefix=prefix)

    with pytest.raises(TimeoutError, match="Last Busy: 0"):
        controller.run_task_cycle(timeout=0.1)
