"""Tests for the watch mode utility."""

import time

import pytest

from imbue.mngr.cli.watch_mode import run_watch_loop
from imbue.mngr.errors import MngrError


def test_run_watch_loop_runs_iteration_function() -> None:
    """run_watch_loop should call the iteration function."""
    call_count = [0]

    def iteration_fn() -> None:
        call_count[0] += 1
        if call_count[0] >= 2:
            raise KeyboardInterrupt()

    with pytest.raises(KeyboardInterrupt):
        run_watch_loop(iteration_fn, interval_seconds=1)

    # Should have called the function at least once before KeyboardInterrupt
    assert call_count[0] >= 1


def test_run_watch_loop_waits_between_iterations() -> None:
    """run_watch_loop should wait for the specified interval between iterations."""
    timestamps: list[float] = []

    def iteration_fn() -> None:
        timestamps.append(time.monotonic())
        if len(timestamps) >= 2:
            raise KeyboardInterrupt()

    # Use interval_seconds=2 so we can verify the parameter actually controls wait duration
    with pytest.raises(KeyboardInterrupt):
        run_watch_loop(iteration_fn, interval_seconds=2)

    # Should have at least 2 calls
    assert len(timestamps) >= 2
    # The gap should reflect the 2-second interval. Asserting >= 1.5 ensures the interval
    # parameter is actually used (a hardcoded 1s wait would fail this check).
    gap = timestamps[1] - timestamps[0]
    assert gap >= 1.5, f"Expected gap >= 1.5s (interval=2), got {gap:.2f}s"


def test_run_watch_loop_continues_on_mngr_error_by_default() -> None:
    """run_watch_loop should continue on MngrError when on_error_continue is True."""
    call_count = [0]

    def iteration_fn() -> None:
        call_count[0] += 1
        if call_count[0] == 1:
            raise MngrError("Test error")
        if call_count[0] >= 3:
            raise KeyboardInterrupt()

    with pytest.raises(KeyboardInterrupt):
        run_watch_loop(iteration_fn, interval_seconds=1, on_error_continue=True)

    # Should have continued past the error
    assert call_count[0] >= 2


def test_run_watch_loop_stops_on_mngr_error_when_configured() -> None:
    """run_watch_loop should re-raise MngrError when on_error_continue is False."""
    call_count = [0]

    def iteration_fn() -> None:
        call_count[0] += 1
        raise MngrError("Test error")

    with pytest.raises(MngrError, match="Test error"):
        run_watch_loop(iteration_fn, interval_seconds=1, on_error_continue=False)

    # Should have stopped after the first error
    assert call_count[0] == 1
