"""Weather provider registry -- the app/widgets/ "dispatch registry over
pluggable implementations" pattern applied to weather data sources
instead of widget types. Open-Meteo (app/weather/open_meteo.py, no API
key, worldwide) and NWS (app/weather/nws.py, no API key, US-only) are the
two providers wired up now; Environment Canada is a documented next step
(docs/widgets.md), not included yet -- its free API is built around
station/grid lookups (the MSC GeoMet OGC service), not simple lat/lon
REST like these two, and would have meaningfully expanded this pass.

geocode_city stays Open-Meteo-backed regardless of which provider is
chosen to actually fetch forecasts -- it's just free-text-name-to-lat/lon
resolution, done once when a location is added, and Open-Meteo's
geocoder covers the whole world where NWS's own data plainly doesn't.

Every provider module exposes the same four functions:
    geocode_city(name) -> {"label", "latitude", "longitude"}   (open_meteo only, see above)
    fetch_current(lat, lon, units) -> {"temp", "category"}
    fetch_hourly(lat, lon, units, hours) -> [{"time", "temp", "category"}, ...]
    fetch_daily(lat, lon, units, days) -> {"YYYY-MM-DD": {"high", "low", "category"}, ...}
"category" is always one of the shared set (clear/partly_cloudy/cloudy/
fog/rain/snow/thunderstorm) that app/weather_render.py's icon-drawing
knows how to draw -- callers never need to know which provider supplied
an entry.
"""

from __future__ import annotations


class WeatherFetchError(Exception):
    """Geocoding or forecast fetch failed -- network, no match, an
    unexpected response shape, or (NWS) a location outside its US
    coverage. Raised loudly; callers (the dialog's location-set/preview
    endpoints, get_or_refresh_*_for_widget) decide what to do."""


from . import nws, open_meteo  # noqa: E402 -- after WeatherFetchError, which both submodules import

# Re-exported for existing call sites (routers/common.py, routers/
# api_widgets.py, calendar_render.py) -- all Open-Meteo-only and
# untouched by the provider abstraction below.
from .open_meteo import (  # noqa: E402,F401
    CHECK_INTERVAL_S,
    fetch_daily_forecast,
    geocode_city,
    weather_category,
)

PROVIDERS = {"open_meteo": open_meteo, "nws": nws}
PROVIDER_LABELS = {"open_meteo": "Open-Meteo", "nws": "National Weather Service (US)"}


def fetch_current(provider: str, latitude: float, longitude: float, units: str) -> dict:
    return PROVIDERS[provider].fetch_current(latitude, longitude, units)


def fetch_hourly(provider: str, latitude: float, longitude: float, units: str, hours: int = 48) -> list[dict]:
    return PROVIDERS[provider].fetch_hourly(latitude, longitude, units, hours)


def fetch_daily(provider: str, latitude: float, longitude: float, units: str, days: int) -> dict[str, dict]:
    return PROVIDERS[provider].fetch_daily(latitude, longitude, units, days)
