"""C8 `FcAdapter` + `GcsAdapter` Protocols (AZ-390 / E-C8).

Concrete strategies (linked at build time per ADR-002):
- AP: `PymavlinkArdupilotAdapter` (AZ-393 outbound, AZ-391 inbound, AZ-395 signing)
- iNav: `Msp2InavAdapter` (AZ-394 outbound, AZ-391 inbound)
- GCS: `QgcTelemetryAdapter` (AZ-397)

Replay extensions (`TlogReplayFcAdapter`, `JsonlReplaySink`) implement
the same Protocols and live under separate build flags (E-DEMO-REPLAY).

Public-API restriction: only `FcAdapter`, `GcsAdapter`, `ReplaySink`,
plus the contract DTOs in `_types/fc.py` and `_types/emitted.py`.
"""

from __future__ import annotations

from typing import Protocol, runtime_checkable

from gps_denied_onboard._types.emitted import EmittedExternalPosition
from gps_denied_onboard._types.fc import (
    FlightStateSignal,
    OperatorCommandCallback,
    PortConfig,
    Severity,
    Subscription,
    TelemetryCallback,
)
from gps_denied_onboard._types.state import EstimatorOutput

__all__ = ["FcAdapter", "GcsAdapter", "ReplaySink"]


@runtime_checkable
class FcAdapter(Protocol):
    """Per-FC flight-controller adapter (inbound telemetry + outbound emit).

    Invariants enforced by concrete implementations (see contract):
    - Single open per instance; re-open raises `FcOpenError`.
    - `close()` is idempotent.
    - `emit_external_position` rejects `output.smoothed == True`.
    - Outbound methods are called from one dedicated emit thread.
    - Inbound subscribe-callbacks fire on the decoder thread.
    """

    def open(self, port: PortConfig, signing_key: bytes | None) -> None: ...

    def close(self) -> None: ...

    def subscribe_telemetry(self, callback: TelemetryCallback) -> Subscription: ...

    def emit_external_position(self, output: EstimatorOutput) -> EmittedExternalPosition: ...

    def emit_status_text(self, msg: str, severity: Severity) -> None: ...

    def request_source_set_switch(self) -> None: ...

    def current_flight_state(self) -> FlightStateSignal: ...


@runtime_checkable
class GcsAdapter(Protocol):
    """Ground-control-station adapter (downsampled summary + operator commands)."""

    def open(self, port: PortConfig) -> None: ...

    def close(self) -> None: ...

    def emit_summary(self, output: EstimatorOutput) -> None: ...

    def subscribe_operator_commands(self, callback: OperatorCommandCallback) -> Subscription: ...

    def emit_status_text(self, msg: str, severity: Severity) -> None: ...


@runtime_checkable
class ReplaySink(Protocol):
    """Replay-mode estimate sink (e.g. JSONL writer).

    Lives in the same module so the replay binary's composition root
    can wire `JsonlReplaySink` alongside the production adapters.
    Excluded from `__init__.__all__` in production-only builds via the
    `BUILD_REPLAY_SINK_JSONL` flag.
    """

    def write(self, output: EstimatorOutput) -> None: ...
