"""Weather data providers with pluggable backend support.

Each provider fetches current weather data and returns a standardized
WeatherData result.  Only Open-Meteo is supported in v1 (free, no API key).
"""

import time
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Dict, Type

import httpx

from wled_controller.utils import get_logger

logger = get_logger(__name__)

_HTTP_TIMEOUT = 5.0


@dataclass(frozen=True)
class WeatherData:
    """Immutable weather observation."""

    code: int  # WMO weather code (0-99)
    temperature: float  # Celsius
    wind_speed: float  # km/h
    cloud_cover: int  # 0-100 %
    fetched_at: float  # time.monotonic() timestamp


# Default fallback when no data has been fetched yet
DEFAULT_WEATHER = WeatherData(code=2, temperature=20.0, wind_speed=5.0, cloud_cover=50, fetched_at=0.0)


class WeatherProvider(ABC):
    """Abstract weather data provider."""

    @abstractmethod
    def fetch(self, latitude: float, longitude: float) -> WeatherData:
        """Fetch current weather for the given location.

        Must not raise — returns DEFAULT_WEATHER on failure.
        """


class OpenMeteoProvider(WeatherProvider):
    """Open-Meteo API provider (free, no API key required)."""

    _URL = "https://api.open-meteo.com/v1/forecast"

    def __init__(self) -> None:
        self._client = httpx.Client(timeout=_HTTP_TIMEOUT)

    def fetch(self, latitude: float, longitude: float) -> WeatherData:
        try:
            resp = self._client.get(
                self._URL,
                params={
                    "latitude": latitude,
                    "longitude": longitude,
                    "current": "temperature_2m,weather_code,wind_speed_10m,cloud_cover",
                },
            )
            resp.raise_for_status()
            data = resp.json()
            current = data["current"]
            return WeatherData(
                code=int(current["weather_code"]),
                temperature=float(current["temperature_2m"]),
                wind_speed=float(current.get("wind_speed_10m", 0.0)),
                cloud_cover=int(current.get("cloud_cover", 50)),
                fetched_at=time.monotonic(),
            )
        except Exception as e:
            logger.warning(f"Open-Meteo fetch failed: {e}")
            return DEFAULT_WEATHER


PROVIDER_REGISTRY: Dict[str, Type[WeatherProvider]] = {
    "open_meteo": OpenMeteoProvider,
}


def create_provider(provider_name: str, provider_config: dict) -> WeatherProvider:
    """Create a provider instance from registry."""
    cls = PROVIDER_REGISTRY.get(provider_name)
    if cls is None:
        raise ValueError(f"Unknown weather provider: {provider_name}")
    return cls()


# WMO Weather interpretation codes (WMO 4677)
WMO_CONDITION_NAMES: Dict[int, str] = {
    0: "Clear sky",
    1: "Mainly clear",
    2: "Partly cloudy",
    3: "Overcast",
    45: "Fog",
    48: "Depositing rime fog",
    51: "Light drizzle",
    53: "Moderate drizzle",
    55: "Dense drizzle",
    56: "Light freezing drizzle",
    57: "Dense freezing drizzle",
    61: "Slight rain",
    63: "Moderate rain",
    65: "Heavy rain",
    66: "Light freezing rain",
    67: "Heavy freezing rain",
    71: "Slight snowfall",
    73: "Moderate snowfall",
    75: "Heavy snowfall",
    77: "Snow grains",
    80: "Slight rain showers",
    81: "Moderate rain showers",
    82: "Violent rain showers",
    85: "Slight snow showers",
    86: "Heavy snow showers",
    95: "Thunderstorm",
    96: "Thunderstorm with slight hail",
    99: "Thunderstorm with heavy hail",
}
