"""C5 ``StateEstimator`` error hierarchy — AZ-381.

Every C5-emitted exception inherits :class:`StateEstimatorError`
(AC-10) so callers can write a single ``except`` against the whole
component surface. Composition-root failures use
:class:`StateEstimatorConfigError`; runtime failures split into
``Degraded`` (recoverable; emit a degraded estimate + log) vs
``Fatal`` (unrecoverable; trigger the AC-5.2 IMU-only fallback path
in C8).
"""

from __future__ import annotations

__all__ = [
    "EstimatorAlreadyStartedError",
    "EstimatorDegradedError",
    "EstimatorFatalError",
    "StateEstimatorConfigError",
    "StateEstimatorError",
]


class StateEstimatorError(Exception):
    """Base class for every C5-emitted exception (AC-10)."""


class EstimatorDegradedError(StateEstimatorError):
    """Recoverable runtime degradation.

    Examples: out-of-order ``add_*`` call (Invariant 2), failed factor
    add against the graph (R05 mitigation surfaces via this), poor
    convergence detected post-update. The estimator continues to
    produce outputs but the next ``current_estimate()`` may carry a
    degraded ``EstimatorHealth.isam2_state``.
    """


class EstimatorFatalError(StateEstimatorError):
    """Unrecoverable numerical failure.

    Raised when iSAM2 / Marginals / the smoother enter a state from
    which the run cannot continue: non-SPD posterior covariance after
    update, NaN propagation, GTSAM exception bubbling. Triggers the
    AC-5.2 path in C8 (IMU-only fallback) and the source-label state
    machine transitions to ``DEAD_RECKONED``.
    """


class StateEstimatorConfigError(StateEstimatorError):
    """Composition-time configuration error.

    Raised by :func:`build_state_estimator` when the requested
    strategy is not registered (per ADR-002 build flag gating), when
    the config schema fails validation, or when the runtime root
    cannot wire the iSAM2 graph handle into C4.

    AZ-490: also raised by :meth:`StateEstimator.set_takeoff_origin`
    when the supplied ``LatLonAlt`` is outside WGS-84 bounds, when
    either sigma is non-positive / non-finite, or when the entrypoint
    is called twice with conflicting arguments before the first
    measurement.
    """


class EstimatorAlreadyStartedError(StateEstimatorConfigError):
    """``set_takeoff_origin`` called after the estimator left the INIT state.

    AZ-490 / Contract Invariant 11a: the operator-origin entrypoint is
    valid only before the first ``add_*`` call. Once any factor has
    been added (i.e. the smoother is past INIT), seeding a new prior
    would silently corrupt the running estimate. ``IS-A`` of
    :class:`StateEstimatorConfigError` so existing
    ``except StateEstimatorConfigError`` callers also catch this.
    """
