"""C4 PoseEstimator output DTOs + enums (AZ-355 / E-C4).

Aligned with the C4 contract at
``_docs/02_document/contracts/c4_pose/pose_estimator_protocol.md``
v1.0.0. The structured DTO (``UUID frame_id`` + ``LatLonAlt`` +
``Quat`` + ``np.ndarray covariance_6x6`` + enums + ``emitted_at``
monotonic_ns) is consumed by C5 (state estimator), C8 (FC outbound),
and C13 (FDR records). The C4 contract makes this the canonical form;
no component should re-invent the raw-4x4 ``pose_se3`` shape that
predated AZ-355.

``Quat`` is re-exported from :mod:`_types.state` so the C5 surface
(``EstimatorOutput.orientation_world_T_body``) and the C4 surface
(``PoseEstimate.orientation_world_T_body``) share one literal class
identity — ``isinstance`` checks across the boundary stay free.

``LatLonAlt`` is re-exported from :mod:`_types.geo` for the same
reason; the geo module remains the storage site so downstream
non-C4 callers (``WgsConverter`` + C6 tile-cache + C8 outbound)
don't pull in the C4 namespace.

``PoseSourceLabel`` is shared with C5 (one provenance enum, three
values: ``SATELLITE_ANCHORED`` / ``VISUAL_PROPAGATED`` /
``DEAD_RECKONED``).
"""

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
from gps_denied_onboard._types.state import PoseSourceLabel, Quat

__all__ = [
    "CovarianceMode",
    "LatLonAlt",
    "PoseEstimate",
    "PoseSourceLabel",
    "Quat",
]


class CovarianceMode(Enum):
    """Per-frame covariance-derivation mode tagged on every emitted ``PoseEstimate``.

    * ``MARGINALS`` — production-default; recovered via
      ``Marginals.marginalCovariance(pose_key)`` against C5's shared
      iSAM2 graph (ADR-003 substrate). The cost-accurate path.
    * ``JACOBIAN`` — D-CROSS-LATENCY-1 thermal-throttle fallback
      (ADR-006). Cheaper but ~5-10% less accurate; engaged per-frame
      when ``ThermalState.thermal_throttle_active == True``.

    AZ-355 owns the enum; AZ-358 (Marginals) + AZ-361 (Hybrid) own
    the producer paths.
    """

    MARGINALS = "marginals"
    JACOBIAN = "jacobian"


@dataclass(frozen=True, slots=True)
class PoseEstimate:
    """C4 single-frame pose estimate.

    Per the C4 contract (Invariant 5) ``covariance_6x6`` is always
    SPD. Per Invariant 6 the ``covariance_mode`` enum matches the
    path actually taken — never reports ``MARGINALS`` while the
    implementation ran the Jacobian path. Per Invariant 7 C4 emits
    ``source_label = SATELLITE_ANCHORED`` unconditionally on success;
    only C5 may downgrade to ``VISUAL_PROPAGATED`` or
    ``DEAD_RECKONED``.

    ``last_satellite_anchor_age_ms`` is provided BY C5 and passed
    through (Invariant 8) — the runtime root broadcasts the current
    value from C5; C4 caches it without computing it independently.

    ``emitted_at`` is a ``time.monotonic_ns()`` snapshot taken when
    the estimator produced the value; consumers MUST NOT compare it
    to wall-clock ``datetime`` values.
    """

    frame_id: UUID
    position_wgs84: LatLonAlt
    orientation_world_T_body: Quat
    covariance_6x6: npt.NDArray[Any]
    covariance_mode: CovarianceMode
    source_label: PoseSourceLabel
    last_satellite_anchor_age_ms: int
    emitted_at: int
