"""Protocol surfaces for the satellite_matcher component (ARCH-05).

Two Protocols live here per PATTERNS.md §3:

  * ``SatelliteTileLoader`` — mirrors the concrete ``SatelliteDataManager``
    public surface from ``core/satellite.py`` (no existing ABC).
  * ``MetricRefiner`` — mirrors ``IMetricRefinement`` from ``core/metric.py``.

Adapters move here in Plan 06 (satellite_matcher).
"""
from __future__ import annotations

from typing import List, Optional, Protocol, Tuple, runtime_checkable

import numpy as np

from gps_denied.schemas import GPSPoint
from gps_denied.schemas.metric import AlignmentResult, ChunkAlignmentResult
from gps_denied.schemas.satellite import TileBounds, TileCoords


@runtime_checkable
class SatelliteTileLoader(Protocol):
    """Local satellite-tile reader / mosaic builder.

    Mirrors the public surface of ``SatelliteDataManager`` (no ABC today).
    """

    def load_local_tile(self, tile_coords: TileCoords) -> np.ndarray | None: ...

    def select_tiles_for_eskf_position(
        self, gps: GPSPoint, sigma_h_m: float, zoom: int
    ) -> list[TileCoords]: ...

    def assemble_mosaic(
        self,
        tile_list: list[tuple[TileCoords, np.ndarray]],
        target_size: int = 512,
    ) -> tuple[np.ndarray, TileBounds] | None: ...

    def fetch_tiles_for_position(
        self, gps: GPSPoint, sigma_h_m: float, zoom: int
    ) -> tuple[np.ndarray, TileBounds] | None: ...

    def get_cached_tile(
        self, flight_id: str, tile_coords: TileCoords
    ) -> np.ndarray | None: ...

    def cache_tile(
        self, flight_id: str, tile_coords: TileCoords, tile_data: np.ndarray
    ) -> bool: ...

    def compute_tile_coords(self, lat: float, lon: float, zoom: int) -> TileCoords: ...

    def compute_tile_bounds(self, tile_coords: TileCoords) -> TileBounds: ...

    def clear_flight_cache(self, flight_id: str) -> bool: ...

    def expand_search_grid(
        self, center: TileCoords, current_size: int, new_size: int
    ) -> list[TileCoords]: ...

    def get_tile_grid(self, center: TileCoords, grid_size: int) -> list[TileCoords]: ...


@runtime_checkable
class MetricRefiner(Protocol):
    """LiteSAM-style satellite alignment surface (mirrors IMetricRefinement)."""

    def align_to_satellite(
        self,
        uav_image: np.ndarray,
        satellite_tile: np.ndarray,
        tile_bounds: TileBounds,
    ) -> Optional[AlignmentResult]: ...

    def compute_homography(
        self, uav_image: np.ndarray, satellite_tile: np.ndarray
    ) -> Optional[np.ndarray]: ...

    def extract_gps_from_alignment(
        self,
        homography: np.ndarray,
        tile_bounds: TileBounds,
        image_center: Tuple[int, int],
    ) -> GPSPoint: ...

    def compute_match_confidence(self, alignment: AlignmentResult) -> float: ...

    def align_chunk_to_satellite(
        self,
        chunk_images: List[np.ndarray],
        satellite_tile: np.ndarray,
        tile_bounds: TileBounds,
    ) -> Optional[ChunkAlignmentResult]: ...

    def match_chunk_homography(
        self, chunk_images: List[np.ndarray], satellite_tile: np.ndarray
    ) -> Optional[np.ndarray]: ...


# Backwards-compat alias for the only ABC that previously existed.
IMetricRefinement = MetricRefiner
