"""FrameLimiter — paces a loop to a target frame rate using time.perf_counter."""

import time


class FrameLimiter:
    """Sleep helper to pace a streaming loop at a target FPS.

    Tracks per-frame timing and exposes simple stats. The minimum sleep is
    clamped to 1ms (matches the historical ``max(..., 0.001)`` guard) so a
    bound is always present even when the loop is over budget.
    """

    def __init__(self, target_fps: float):
        self._frame_time: float = 1.0 / max(target_fps, 0.001)
        self._loop_start: float = time.perf_counter()
        self._last_dt: float = 0.0
        self._sum_dt: float = 0.0
        self._count: int = 0

    def set_target_fps(self, target_fps: float) -> None:
        """Update the target FPS for subsequent ``wait()`` calls."""
        self._frame_time = 1.0 / max(target_fps, 0.001)

    @property
    def frame_time(self) -> float:
        """Configured frame interval (seconds)."""
        return self._frame_time

    def begin(self) -> None:
        """Mark the start of the current frame's work.

        Call this immediately before the work that should be paced.  The next
        ``wait()`` will sleep so that the frame's total duration matches the
        configured frame interval.
        """
        self._loop_start = time.perf_counter()

    def wait(self, frame_time: float | None = None) -> None:
        """Sleep until the configured frame interval elapses since ``begin()``.

        Sleeps at least 1ms to ensure cooperative yielding even when the loop
        is already over budget.  Pass ``frame_time`` to override the
        per-iteration target (useful when callers vary the sleep target
        based on runtime state).
        """
        ft = self._frame_time if frame_time is None else frame_time
        elapsed = time.perf_counter() - self._loop_start
        self._last_dt = elapsed
        self._sum_dt += elapsed
        self._count += 1
        time.sleep(max(ft - elapsed, 0.001))

    @property
    def stats(self) -> dict:
        """Return last and rolling-average frame work time (seconds)."""
        avg = self._sum_dt / self._count if self._count else 0.0
        return {"last_dt": self._last_dt, "avg_dt": avg, "count": self._count}
