"""C5 ``StateEstimator`` output DTOs + canonical pose-provenance enum.

AZ-381 owns this module per the C5 contract
(``_docs/02_document/contracts/c5_state/state_estimator_protocol.md`` v1.0.0).
C4 reuses the same ``Quat`` + ``PoseSourceLabel`` enums so the C4/C5
boundary stays single-typed (see the C4 contract
``_docs/02_document/contracts/c4_pose/pose_estimator_protocol.md`` §4 DTOs).

The dataclasses are ``frozen=True, slots=True`` per AC-2 — DTOs cross
component boundaries and mutation-through-aliasing has bitten this
codebase before (R05 in the C5 risk register).
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING, Any
from uuid import UUID

if TYPE_CHECKING:
    import numpy.typing as npt

from gps_denied_onboard._types.geo import LatLonAlt

__all__ = [
    "EstimatorHealth",
    "EstimatorOutput",
    "IsamState",
    "PoseSourceLabel",
    "Quat",
]


@dataclass(frozen=True, slots=True)
class Quat:
    """Unit quaternion ``(w, x, y, z)``; scalar-first.

    Used by C4/C5 to carry body→world orientation without committing
    to a heavyweight SE(3) library at the DTO boundary.
    """

    w: float
    x: float
    y: float
    z: float


class PoseSourceLabel(Enum):
    """Canonical C5-emitted pose provenance label.

    The three values map 1:1 to the C5 source-label state machine
    (AZ-385): ``SATELLITE_ANCHORED`` requires the spoof-promotion
    gate to be open; ``VISUAL_PROPAGATED`` is the steady-state
    no-anchor path; ``DEAD_RECKONED`` is the IMU-only fallback.
    """

    SATELLITE_ANCHORED = "satellite_anchored"
    VISUAL_PROPAGATED = "visual_propagated"
    DEAD_RECKONED = "dead_reckoned"


class IsamState(Enum):
    """C5 iSAM2 lifecycle state surfaced via :class:`EstimatorHealth`."""

    INIT = "init"
    TRACKING = "tracking"
    DEGRADED = "degraded"
    LOST = "lost"


@dataclass(frozen=True, slots=True)
class EstimatorOutput:
    """C5 state-estimator output.

    Invariant 6 (the C5 contract) forbids emitting ``smoothed=True``
    values to the FC; C8 outbound adapters MUST raise on it. The
    composition root distinguishes the two paths by setting
    ``smoothed=True`` only on entries returned from
    ``smoothed_history(...)`` (history is for C13 / GCS observability,
    not flight control).
    """

    frame_id: UUID
    position_wgs84: LatLonAlt
    orientation_world_T_body: Quat
    velocity_world_mps: tuple[float, float, float]
    covariance_6x6: npt.NDArray[Any]
    source_label: PoseSourceLabel
    last_satellite_anchor_age_ms: int
    smoothed: bool
    emitted_at: int


@dataclass(frozen=True, slots=True)
class EstimatorHealth:
    """C5 iSAM2 health snapshot returned by ``health_snapshot()``.

    Consumed by C8 telemetry, C13 FDR records, and the spoof-promotion
    gate (AZ-385) for the ``cov_norm_growing_for_s`` / ``spoof_promotion_blocked``
    decisions.
    """

    isam2_state: IsamState
    keyframe_count: int
    cov_norm_growing_for_s: float
    spoof_promotion_blocked: bool
