# services/external/app/registry/city_registry.py
"""
City Registry - Configuration-driven multi-city support
"""

from dataclasses import dataclass
from typing import List, Optional, Dict, Any
from enum import Enum
import math


class Country(str, Enum):
    SPAIN = "ES"
    FRANCE = "FR"


class WeatherProvider(str, Enum):
    AEMET = "aemet"
    METEO_FRANCE = "meteo_france"
    OPEN_WEATHER = "open_weather"


class TrafficProvider(str, Enum):
    MADRID_OPENDATA = "madrid_opendata"
    VALENCIA_OPENDATA = "valencia_opendata"
    BARCELONA_OPENDATA = "barcelona_opendata"


@dataclass
class CityDefinition:
    """City configuration with data source specifications"""
    city_id: str
    name: str
    country: Country
    latitude: float
    longitude: float
    radius_km: float

    weather_provider: WeatherProvider
    weather_config: Dict[str, Any]
    traffic_provider: TrafficProvider
    traffic_config: Dict[str, Any]

    timezone: str
    population: int
    enabled: bool = True


class CityRegistry:
    """Central registry of supported cities"""

    CITIES: List[CityDefinition] = [
        CityDefinition(
            city_id="madrid",
            name="Madrid",
            country=Country.SPAIN,
            latitude=40.4168,
            longitude=-3.7038,
            radius_km=30.0,
            weather_provider=WeatherProvider.AEMET,
            weather_config={
                "station_ids": ["3195", "3129", "3197"],
                "municipality_code": "28079"
            },
            traffic_provider=TrafficProvider.MADRID_OPENDATA,
            traffic_config={
                "current_xml_url": "https://datos.madrid.es/egob/catalogo/...",
                "historical_base_url": "https://datos.madrid.es/...",
                "measurement_points_csv": "https://datos.madrid.es/..."
            },
            timezone="Europe/Madrid",
            population=3_200_000
        ),
        CityDefinition(
            city_id="valencia",
            name="Valencia",
            country=Country.SPAIN,
            latitude=39.4699,
            longitude=-0.3763,
            radius_km=25.0,
            weather_provider=WeatherProvider.AEMET,
            weather_config={
                "station_ids": ["8416"],
                "municipality_code": "46250"
            },
            traffic_provider=TrafficProvider.VALENCIA_OPENDATA,
            traffic_config={
                "api_endpoint": "https://valencia.opendatasoft.com/api/..."
            },
            timezone="Europe/Madrid",
            population=800_000,
            enabled=False
        ),
        CityDefinition(
            city_id="barcelona",
            name="Barcelona",
            country=Country.SPAIN,
            latitude=41.3851,
            longitude=2.1734,
            radius_km=30.0,
            weather_provider=WeatherProvider.AEMET,
            weather_config={
                "station_ids": ["0076"],
                "municipality_code": "08019"
            },
            traffic_provider=TrafficProvider.BARCELONA_OPENDATA,
            traffic_config={
                "api_endpoint": "https://opendata-ajuntament.barcelona.cat/..."
            },
            timezone="Europe/Madrid",
            population=1_600_000,
            enabled=False
        )
    ]

    @classmethod
    def get_enabled_cities(cls) -> List[CityDefinition]:
        """Get all enabled cities"""
        return [city for city in cls.CITIES if city.enabled]

    @classmethod
    def get_city(cls, city_id: str) -> Optional[CityDefinition]:
        """Get city by ID"""
        for city in cls.CITIES:
            if city.city_id == city_id:
                return city
        return None

    @classmethod
    def find_nearest_city(cls, latitude: float, longitude: float) -> Optional[CityDefinition]:
        """Find nearest enabled city to coordinates"""
        enabled_cities = cls.get_enabled_cities()
        if not enabled_cities:
            return None

        min_distance = float('inf')
        nearest_city = None

        for city in enabled_cities:
            distance = cls._haversine_distance(
                latitude, longitude,
                city.latitude, city.longitude
            )
            if distance <= city.radius_km and distance < min_distance:
                min_distance = distance
                nearest_city = city

        return nearest_city

    @staticmethod
    def _haversine_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
        """Calculate distance in km between two coordinates"""
        R = 6371

        dlat = math.radians(lat2 - lat1)
        dlon = math.radians(lon2 - lon1)

        a = (math.sin(dlat/2) ** 2 +
             math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
             math.sin(dlon/2) ** 2)

        c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
        return R * c
