"""Application configuration loaded from environment / YAML."""

from __future__ import annotations

from pathlib import Path

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class DatabaseConfig(BaseSettings):
    """Database connection settings."""

    model_config = SettingsConfigDict(env_prefix="DB_")

    url: str = Field(
        default="sqlite+aiosqlite:///./flight_data.db",
        description="SQLAlchemy async connection URL",
    )
    echo: bool = False


class APIConfig(BaseSettings):
    """API server settings."""

    model_config = SettingsConfigDict(env_prefix="API_")

    host: str = "127.0.0.1"
    port: int = 8000
    reload: bool = True
    cors_origins: list[str] = Field(default_factory=lambda: ["*"])


class TileProviderConfig(BaseSettings):
    """Satellite tile provider settings (Google Maps by default)."""

    model_config = SettingsConfigDict(env_prefix="TILES_")

    provider: str = "google"
    api_key: str = ""
    cache_dir: Path = Path("tile_cache")
    zoom_level: int = 18
    max_concurrent_requests: int = 4


class ModelPaths(BaseSettings):
    """Paths to ML model weights."""

    model_config = SettingsConfigDict(env_prefix="MODEL_")

    weights_dir: Path = Path("weights")
    superpoint_path: str = "superpoint.pt"
    lightglue_path: str = "lightglue.pt"
    litesam_path: str = "litesam.pt"


class OperationalArea(BaseSettings):
    """Default operational boundaries."""

    model_config = SettingsConfigDict(env_prefix="AREA_")

    name: str = "Eastern Ukraine"
    min_lat: float = 45.0
    max_lat: float = 52.0
    min_lon: float = 22.0
    max_lon: float = 40.0


class RecoveryConfig(BaseSettings):
    """Failure recovery and progressive search settings."""

    model_config = SettingsConfigDict(env_prefix="RECOVERY_")

    search_grid_sizes: list[int] = Field(default_factory=lambda: [1, 4, 9, 16, 25])
    min_chunk_frames: int = 5
    max_chunk_frames: int = 20
    user_input_threshold_tiles: int = 25
    confidence_threshold_good: float = 0.7
    confidence_threshold_degraded: float = 0.5
    min_inlier_count_good: int = 50
    min_inlier_count_tracking: int = 20


class RotationConfig(BaseSettings):
    """Image rotation and heading tracking settings."""

    model_config = SettingsConfigDict(env_prefix="ROTATION_")

    step_degrees: float = 30.0
    litesam_max_tolerance: float = 45.0
    sharp_turn_threshold: float = 45.0
    heading_history_size: int = 10
    confidence_threshold: float = 0.7

    @property
    def rotation_iterations(self) -> int:
        return int(360 / self.step_degrees)


class AuthConfig(BaseSettings):
    """JWT authentication settings."""

    model_config = SettingsConfigDict(env_prefix="AUTH_")

    enabled: bool = False  # False для dev/SITL, True для production
    secret_key: str = "dev-secret-change-in-production"
    algorithm: str = "HS256"
    ssl_certfile: str = ""   # шлях до TLS cert (порожній = без TLS)
    ssl_keyfile: str = ""    # шлях до TLS key


class MAVLinkConfig(BaseSettings):
    """MAVLink I/O bridge settings."""

    model_config = SettingsConfigDict(env_prefix="MAVLINK_")

    connection: str = Field(
        default="udp:127.0.0.1:14550",
        description="pymavlink connection string (serial:/dev/ttyTHS1:57600 or tcp:host:port)",
    )
    output_hz: float = 5.0
    telemetry_hz: float = 1.0


class SatelliteConfig(BaseSettings):
    """Pre-loaded satellite tile directory settings."""

    model_config = SettingsConfigDict(env_prefix="SATELLITE_")

    tile_dir: str = ".satellite_tiles"
    zoom_level: int = 18


class ESKFSettings(BaseSettings):
    """ESKF tuning parameters (overridable via env vars)."""

    model_config = SettingsConfigDict(env_prefix="ESKF_")

    accel_noise_density: float = 6.86e-4
    gyro_noise_density: float = 5.24e-5
    accel_random_walk: float = 2.0e-3
    gyro_random_walk: float = 8.73e-7
    vo_position_noise: float = 0.3
    sat_noise_min: float = 5.0
    sat_noise_max: float = 20.0
    satellite_max_age: float = 30.0
    covariance_high_threshold: float = 400.0
    init_pos_var: float = 100.0
    init_vel_var: float = 1.0
    init_att_var: float = 0.01
    init_accel_bias_var: float = 1e-4
    init_gyro_bias_var: float = 1e-6


class AppSettings(BaseSettings):
    """Root settings — aggregates all sub-configs."""

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        env_nested_delimiter="__",
        extra="ignore",
    )

    db: DatabaseConfig = Field(default_factory=DatabaseConfig)
    api: APIConfig = Field(default_factory=APIConfig)
    tiles: TileProviderConfig = Field(default_factory=TileProviderConfig)
    models: ModelPaths = Field(default_factory=ModelPaths)
    area: OperationalArea = Field(default_factory=OperationalArea)
    recovery: RecoveryConfig = Field(default_factory=RecoveryConfig)
    rotation: RotationConfig = Field(default_factory=RotationConfig)
    auth: AuthConfig = Field(default_factory=AuthConfig)
    mavlink: MAVLinkConfig = Field(default_factory=MAVLinkConfig)
    satellite: SatelliteConfig = Field(default_factory=SatelliteConfig)
    eskf: ESKFSettings = Field(default_factory=ESKFSettings)


_settings: AppSettings | None = None


def get_settings() -> AppSettings:
    """Cached singleton for application settings."""
    global _settings
    if _settings is None:
        _settings = AppSettings()
    return _settings
