"""C12 ``FlightsApiClient`` Protocol + DTOs + enums (AZ-489).

Frozen by ``_docs/02_document/contracts/c12_operator_tooling/flights_api_client.md``
v1.0.0. The DTOs mirror ``suite/flights/Database/Entities/{Flight,Waypoint}.cs``;
adding a new field on the parent-suite C# side requires a new minor-version
bump here (FAC-INV-1: online + offline produce the same shape).
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Protocol, runtime_checkable
from uuid import UUID

from gps_denied_onboard._types.geo import BoundingBox, LatLonAlt

__all__ = [
    "FlightDto",
    "FlightsApiClient",
    "WaypointDto",
    "WaypointObjective",
    "WaypointSource",
]


class WaypointObjective(Enum):
    """Mission-planning intent attached to a single waypoint.

    Mirrors ``suite/flights/Database/Entities/WaypointObjective.cs``. Unknown
    values raise :class:`WaypointSchemaError` during parsing per FAC-INV-1.
    """

    TAKEOFF = "takeoff"
    WAYPOINT = "waypoint"
    LOITER = "loiter"
    LANDING = "landing"
    OTHER = "other"


class WaypointSource(Enum):
    """Origin of the waypoint per the parent-suite enum."""

    OPERATOR = "operator"
    IMPORT = "import"
    OTHER = "other"


@dataclass(frozen=True, slots=True)
class WaypointDto:
    """A single ordered waypoint inside a :class:`FlightDto`.

    ``ordinal`` is the strictly-ascending sort key inside the parent flight;
    parsing enforces a contiguous ``0..N-1`` sequence (FAC-INV-2).
    ``alt_m`` is the WGS84 ellipsoidal height in metres.
    """

    ordinal: int
    lat_deg: float
    lon_deg: float
    alt_m: float
    objective: WaypointObjective
    source: WaypointSource


@dataclass(frozen=True, slots=True)
class FlightDto:
    """An operator-planned mission resolved from the flights service or a file.

    ``waypoints`` is non-empty and ordered by ascending ``ordinal``
    (FAC-INV-2). ``waypoints[0]`` is the takeoff origin per ADR-010 — see
    :func:`takeoff_origin_from_flight`.
    """

    flight_id: UUID
    name: str
    waypoints: tuple[WaypointDto, ...]


@runtime_checkable
class FlightsApiClient(Protocol):
    """Read a :class:`FlightDto` from the parent-suite flights service or a file.

    Pure read; no side effects beyond structured logging. The caller (C12
    ``CacheBuildWorkflow``) decides which source to use based on CLI flags
    (``--flight-id`` vs ``--flight-file``).
    """

    def fetch_flight(
        self,
        *,
        flight_id: UUID,
        base_url: str,
        auth_token: str,
        timeout_s: float = 10.0,
    ) -> FlightDto:
        """Resolve via HTTPS against the parent-suite ``flights`` service.

        Raises:
            FlightsApiUnreachableError: HTTPS timeout / 5xx after the
                single allowed retry (FAC-INV-5).
            FlightsApiAuthError: HTTP 401 / 403 (never retried; never logs
                ``auth_token``).
            FlightNotFoundError: HTTP 404 — operator gave a wrong GUID.
            FlightsApiSchemaError: response body violates the DTO schema.
            WaypointSchemaError: a waypoint inside the response is malformed.
        """
        ...

    def load_flight_file(self, *, path: Path) -> FlightDto:
        """Resolve from a JSON export on disk (offline path).

        Returns a DTO with the SAME shape as :meth:`fetch_flight` (FAC-INV-1).
        """
        ...

    def bbox_from_waypoints(
        self,
        waypoints: tuple[WaypointDto, ...],
        *,
        buffer_m: float = 1000.0,
    ) -> BoundingBox:
        """Envelope ``waypoints`` lat/lon and inflate by ``buffer_m`` horizontal metres.

        FAC-INV-3: buffer is a horizontal-distance expansion via
        ``WgsConverter`` ENU round-trip, NOT a degree-space expansion.
        """
        ...

    def takeoff_origin_from_flight(self, flight: FlightDto) -> LatLonAlt:
        """Return ``waypoints[0]`` as a :class:`LatLonAlt` — no rounding (FAC-INV-4)."""
        ...
