"""``Clock`` Protocol — replay/live-agnostic monotonic + wall-clock time.

Frozen at AZ-398 v1.0.0 per the replay contract:
``_docs/02_document/contracts/replay/replay_protocol.md``.

The Protocol is Layer 1 cross-cutting per ``module-layout.md`` — every
component that previously called :func:`time.monotonic_ns`,
:func:`time.time_ns`, or :func:`time.sleep` MUST consume an injected
:class:`Clock` instead (Invariant 2). The strategy is selected exactly
once at composition time (Invariant — Single Clock per process):

- **Live / research / operator** binaries inject :class:`WallClock`.
- **Replay** binary injects :class:`TlogDerivedClock` (ASAP) or
  :class:`WallClock` (REALTIME pace).

Mode-specific behaviour lives in the strategy; consumers see only the
``Clock`` interface (R-DEMO-4 mitigation).
"""

from __future__ import annotations

from typing import Protocol, runtime_checkable


@runtime_checkable
class Clock(Protocol):
    """Monotonic + wall-clock + sleep-until abstraction (AZ-398 v1.0.0).

    All three methods are non-blocking except :meth:`sleep_until_ns`,
    which honours the configured replay pace:

    - ``WallClock.sleep_until_ns(t)`` blocks until ``time.monotonic_ns()``
      catches up to ``t`` (live + REALTIME replay).
    - ``TlogDerivedClock.sleep_until_ns(t)`` is a no-op (ASAP replay).

    Strategies MUST guarantee :meth:`monotonic_ns` is non-decreasing
    across calls within the same process (Invariant 3 spirit).
    """

    def monotonic_ns(self) -> int:
        """Return the strategy's monotonic time in nanoseconds.

        For :class:`WallClock` this delegates to
        :func:`time.monotonic_ns`. For :class:`TlogDerivedClock` this
        returns the most recently advanced tlog timestamp (advance-on-
        call semantics — see AC-6).
        """
        ...

    def time_ns(self) -> int:
        """Return the strategy's UTC wall-clock time in nanoseconds.

        Used for log timestamps that need calendar alignment (FDR
        records, STATUSTEXT). For :class:`WallClock` this is
        :func:`time.time_ns`; for :class:`TlogDerivedClock` this is the
        tlog message's wall-clock timestamp (the ``time_unix_usec`` /
        ``time_boot_ms`` field, normalised to ns).
        """
        ...

    def sleep_until_ns(self, target_ns: int) -> None:
        """Block until :meth:`monotonic_ns` would return ``target_ns``.

        Honours ``pace=REALTIME`` by sleeping the wall-clock delta; honours
        ``pace=ASAP`` by no-op'ing. ``target_ns`` already in the past is a
        no-op (no exception, no negative sleep). The Protocol does not
        prescribe spurious-wakeup behaviour; strategies SHOULD use
        :func:`time.sleep` (which retries internally on POSIX).
        """
        ...
