"""C10 manifest + engine-cache DTOs."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable

if TYPE_CHECKING:
    from gps_denied_onboard._types.matching import CorrespondenceSet, KeypointSet


@dataclass(frozen=True)
class Manifest:
    """C10 cache-provisioning manifest (D-C10-1 idempotence hash)."""

    manifest_id: str
    flight_id: str
    created_at: datetime
    content_hash: str
    entries: tuple[Any, ...] = ()
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
class EngineCacheKey:
    """Parsed tuple of a self-describing `.engine` filename (D-C10-7, AZ-281).

    Filename schema: ``{model}__sm{SM}_jp{JP_dotted}_trt{TRT_dotted}_{precision}.engine``.
    The dotted-version strings (``jetpack``, ``trt``) are stored verbatim so the
    round-trip ``parse(build(*args)) == EngineCacheKey(*args)`` invariant holds.
    """

    model_name: str
    sm: int
    jetpack: str
    trt: str
    precision: str


@runtime_checkable
class EngineHandle(Protocol):
    """Consumer-side structural cut of a C7 engine handle for the
    LightGlue helper (AZ-278 / R14 fix).

    Intentionally distinct from
    :class:`gps_denied_onboard.components.c7_inference.EngineHandle`
    (the AZ-297 opaque marker class): same name, different shape, by
    design. The C7 Protocol says EngineHandle is opaque to general
    consumers (Invariant I-4); LightGlueRuntime is a specific helper
    that needs a narrower structural Protocol with
    ``descriptor_dim`` + ``forward``. Concrete C7 strategies
    (TensorrtRuntime / OnnxTrtEpRuntime / PytorchFp16Runtime)
    subclass the c7_inference opaque marker AND structurally satisfy
    this Protocol when paired with a matching engine binary.

    The helper depends on THIS Protocol from ``_types`` so Layer 1
    never imports C7 (R14 fix). See ADR for the dual-Protocol
    design rationale (mirrors the C4/C5 ISam2GraphHandle split).
    """

    @property
    def descriptor_dim(self) -> int:
        """Expected descriptor dimension for input KeypointSets."""

    def forward(self, features_a: KeypointSet, features_b: KeypointSet) -> CorrespondenceSet:
        """Run a single matching pass and return the correspondences."""


@dataclass(frozen=True)
class HostCapabilities:
    """Host-side TensorRT capability tuple consulted by AZ-281's ``matches_host``.

    Captures the same five-tuple the engine filename encodes; ``matches_host`` is
    true iff every element matches exactly. Precision and ``model_name`` are
    excluded from the comparison: the predicate is host-vs-binary, not
    engine-vs-engine.
    """

    sm: int
    jetpack: str
    trt: str
