"""C7 ``InferenceRuntime`` Protocol (AZ-297).

PEP 544 ``typing.Protocol`` with ``runtime_checkable=True``; six
methods that span engine lifecycle (compile / deserialize / release),
per-call inference, thermal-state telemetry, and runtime-label
self-report. Concrete impls — :class:`TensorrtRuntime` (AZ-298),
:class:`OnnxTrtEpRuntime` (AZ-299), :class:`PytorchFp16Runtime`
(AZ-300) — live in sibling modules and are imported lazily by
:mod:`gps_denied_onboard.runtime_root.inference_factory`.

The contract at
``_docs/02_document/contracts/c7_inference/inference_runtime_protocol.md``
v1.0.0 is the authoritative shape; this module mirrors it 1:1.
"""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Literal, Protocol, runtime_checkable

from gps_denied_onboard._types.inference import (
    BuildConfig,
    EngineCacheEntry,
    EngineHandle,
)
from gps_denied_onboard._types.thermal import ThermalState

if TYPE_CHECKING:
    import numpy as np

__all__ = ["InferenceRuntime"]


@runtime_checkable
class InferenceRuntime(Protocol):
    """On-Jetson inference runtime. See ``inference_runtime_protocol.md`` v1.0.0.

    Implementations: :class:`TensorrtRuntime` (production-default, TRT 10.3),
    :class:`OnnxTrtEpRuntime` (ONNX Runtime + TensorRT EP fallback),
    :class:`PytorchFp16Runtime` (research / Tier-0 baseline). Selection
    is owned by the composition root.
    """

    def compile_engine(
        self, model_path: Path, build_config: BuildConfig
    ) -> EngineCacheEntry:
        """Offline (minutes for INT8) engine compilation.

        Raises :class:`EngineBuildError` on compile failure;
        :class:`CalibrationCacheError` when
        ``build_config.precision == INT8`` and the calibration data
        cannot be loaded.
        """
        ...

    def deserialize_engine(self, entry: EngineCacheEntry) -> EngineHandle:
        """Load a compiled engine into GPU memory.

        Raises :class:`EngineDeserializeError` /
        :class:`EngineHashMismatchError` /
        :class:`EngineSchemaMismatchError` /
        :class:`EngineSidecarMissingError` /
        :class:`OutOfMemoryError` per the contract envelope.
        """
        ...

    def infer(
        self,
        handle: EngineHandle,
        inputs: "dict[str, np.ndarray]",
    ) -> "dict[str, np.ndarray]":
        """Synchronous request/response inference (Invariant I-8).

        Returns AFTER the GPU stream has synchronised; output tensors
        are host-resident :class:`numpy.ndarray` ready for consumer
        use. Raises :class:`InferenceError` /
        :class:`OutOfMemoryError`.
        """
        ...

    def release_engine(self, handle: EngineHandle) -> None:
        """Drop the engine + free GPU memory.

        Idempotent (Invariant I-7) — second-and-later calls on the
        same handle return silently.
        """
        ...

    def thermal_state(self) -> ThermalState:
        """Most-recent thermal reading from ``tegrastats`` / ``jtop``.

        Raises :class:`TelemetryUnavailableError` only on cold-start
        binding failure; steady-state telemetry absence is signalled
        by ``ThermalState.is_telemetry_available == False`` (Invariant I-6).
        """
        ...

    def current_runtime_label(
        self,
    ) -> Literal["tensorrt", "onnx_trt_ep", "pytorch_fp16"]:
        """Identify which concrete strategy is wired here.

        Returned string equals ``config.inference.runtime`` exactly
        (Invariant I-1); AC-NEW-3 FDR audit relies on this property.
        """
        ...
