"""Загрузка и валидация конфигурации GPS-источников и регионов (ET-008)."""
import logging
import os

import yaml

logger = logging.getLogger(__name__)


# ET-011 / ADR-015: дефолтный whitelist для скачивания, если конфиг недоступен
# (например, в unit-тестах). Совпадает с production-выбором "только OSM".
DEFAULT_DOWNLOAD_ALLOWED_SOURCES = frozenset({"osm"})


def load_sources_config(path: str) -> list:
    """Загружает конфигурацию источников GPS-треков.

    Args:
        path: путь к YAML-файлу конфигурации источников

    Returns:
        list[dict] — список источников

    Raises:
        ValueError: при ошибках валидации
        FileNotFoundError: если файл не найден
    """
    with open(path, "r", encoding="utf-8") as f:
        data = yaml.safe_load(f)

    sources = data.get("sources", [])
    if not isinstance(sources, list):
        raise ValueError("sources must be a list")

    for src in sources:
        if not src.get("id"):
            raise ValueError(f"Source missing 'id': {src}")
        if not src.get("base_url"):
            raise ValueError(f"Source '{src['id']}' missing 'base_url'")

        # Enabled source must have license_adr
        if src.get("enabled", False):
            if not src.get("license_adr"):
                raise ValueError(
                    f"Enabled source '{src['id']}' must have 'license_adr'"
                )

    return sources


def load_regions_config(path: str) -> list:
    """Загружает конфигурацию регионов для сбора GPS-треков.

    Args:
        path: путь к YAML-файлу конфигурации регионов

    Returns:
        list[dict] — список регионов

    Raises:
        ValueError: при ошибках валидации
        FileNotFoundError: если файл не найден
    """
    with open(path, "r", encoding="utf-8") as f:
        data = yaml.safe_load(f)

    regions = data.get("regions", [])
    if not isinstance(regions, list):
        raise ValueError("regions must be a list")

    for reg in regions:
        if not reg.get("id"):
            raise ValueError(f"Region missing 'id': {reg}")

        bbox = reg.get("bbox")
        if not bbox or len(bbox) != 4:
            raise ValueError(f"Region '{reg['id']}' must have bbox with 4 values")

        west, south, east, north = bbox

        # Валидация диапазонов координат
        if not (-180 <= west <= 180):
            raise ValueError(f"Region '{reg['id']}' bbox west={west} out of range")
        if not (-180 <= east <= 180):
            raise ValueError(f"Region '{reg['id']}' bbox east={east} out of range")
        if not (-90 <= south <= 90):
            raise ValueError(f"Region '{reg['id']}' bbox south={south} out of range")
        if not (-90 <= north <= 90):
            raise ValueError(f"Region '{reg['id']}' bbox north={north} out of range")
        if west >= east:
            raise ValueError(
                f"Region '{reg['id']}' bbox: west must be < east"
            )
        if south >= north:
            raise ValueError(
                f"Region '{reg['id']}' bbox: south must be < north"
            )

    return regions


def load_download_allowed_sources(path: str | None) -> set[str]:
    """ET-011 / ADR-015: возвращает whitelist source_id с download_allowed=true.

    Семантика default-deny: если поле `download_allowed` отсутствует,
    источник **не** добавляется в whitelist.

    Args:
        path: путь к `config/gps_sources.yaml` либо None.

    Returns:
        set[str] — id источников, для которых разрешено скачивание.
        При path=None / отсутствии файла / ошибке парсинга — возвращает
        `DEFAULT_DOWNLOAD_ALLOWED_SOURCES` (`{"osm"}`) и логирует WARNING.
    """
    if not path:
        return set(DEFAULT_DOWNLOAD_ALLOWED_SOURCES)
    if not os.path.exists(path):
        logger.warning(
            "gps_sources config not found at %s; falling back to default "
            "download whitelist=%s",
            path,
            sorted(DEFAULT_DOWNLOAD_ALLOWED_SOURCES),
        )
        return set(DEFAULT_DOWNLOAD_ALLOWED_SOURCES)
    try:
        sources = load_sources_config(path)
    except (ValueError, OSError) as exc:
        logger.warning(
            "failed to load gps_sources config from %s (%s); falling back to "
            "default download whitelist=%s",
            path,
            exc,
            sorted(DEFAULT_DOWNLOAD_ALLOWED_SOURCES),
        )
        return set(DEFAULT_DOWNLOAD_ALLOWED_SOURCES)

    allowed: set[str] = set()
    for src in sources:
        # Дефолт `False` — default-deny (ADR-015 §C).
        if src.get("download_allowed") is True:
            allowed.add(src["id"])
    return allowed
