"""
BAN YARO — Wetter via Open-Meteo
- get_weather_summary(): Push-Job, prüft 5 deutsche Städte
- get_weather_for_location(): API-Endpoint, beliebiger Standort mit TTL-Cache
"""

import time
import logging
import httpx
from datetime import datetime

logger = logging.getLogger(__name__)

# WMO-Wettercodes → (Beschreibung, Phosphor-Icon-Name)
_WMO = {
    0:  ('Klar',                  'sun'),
    1:  ('Überwiegend klar',      'cloud-sun'),
    2:  ('Teilweise bewölkt',     'cloud-sun'),
    3:  ('Bedeckt',               'cloud'),
    45: ('Nebel',                 'cloud-fog'),
    48: ('Gefrierender Nebel',    'cloud-fog'),
    51: ('Leichter Nieselregen',  'cloud-rain'),
    53: ('Nieselregen',           'cloud-rain'),
    55: ('Starker Nieselregen',   'cloud-rain'),
    61: ('Leichter Regen',        'cloud-rain'),
    63: ('Regen',                 'cloud-rain'),
    65: ('Starker Regen',         'cloud-rain'),
    71: ('Leichter Schnee',       'snowflake'),
    73: ('Schnee',                'snowflake'),
    75: ('Starker Schnee',        'snowflake'),
    77: ('Schneekörner',          'snowflake'),
    80: ('Leichte Schauer',       'cloud-rain'),
    81: ('Schauer',               'cloud-rain'),
    82: ('Starke Schauer',        'cloud-rain'),
    85: ('Schneeschauer',         'snowflake'),
    86: ('Starke Schneeschauer',  'snowflake'),
    95: ('Gewitter',              'cloud-lightning'),
    96: ('Gewitter mit Hagel',    'cloud-lightning'),
    99: ('Schweres Gewitter',     'cloud-lightning'),
}

# TTL-Cache: (round(lat,1), round(lon,1)) → (timestamp, data)
_location_cache: dict = {}
_CACHE_TTL = 1800  # 30 Minuten


async def get_weather_for_location(lat: float, lon: float) -> dict:
    """Holt aktuelles Wetter für einen Standort. 30-min TTL-Cache."""
    key = (round(lat, 1), round(lon, 1))
    now = time.time()
    if key in _location_cache:
        ts, cached = _location_cache[key]
        if now - ts < _CACHE_TTL:
            return cached

    url = (
        "https://api.open-meteo.com/v1/forecast"
        f"?latitude={lat}&longitude={lon}"
        "&current=temperature_2m,apparent_temperature,weathercode,windspeed_10m,is_day"
        "&daily=precipitation_probability_max,uv_index_max"
        "&timezone=Europe%2FBerlin&forecast_days=1"
    )
    async with httpx.AsyncClient(timeout=8.0) as client:
        resp = await client.get(url)
        resp.raise_for_status()
        raw = resp.json()

    cur   = raw.get('current', {})
    daily = raw.get('daily', {})

    temp       = cur.get('temperature_2m')
    feels_like = cur.get('apparent_temperature')
    wcode      = cur.get('weathercode', 0)
    wind       = cur.get('windspeed_10m')
    is_day     = cur.get('is_day', 1)
    precip     = (daily.get('precipitation_probability_max') or [None])[0]
    uv         = (daily.get('uv_index_max') or [None])[0]

    desc, icon = _WMO.get(wcode, ('Unbekannt', 'cloud'))
    if wcode == 0 and not is_day:
        icon = 'moon'

    month = datetime.now().month
    zecken = None
    if temp is not None and temp > 7.0 and 3 <= month <= 10:
        zecken = 'hoch' if temp > 20 else ('mittel' if temp > 12 else 'niedrig')

    data = {
        'temp_c':        temp,
        'feels_like_c':  feels_like,
        'weathercode':   wcode,
        'desc':          desc,
        'icon':          icon,
        'wind_kmh':      wind,
        'precip_prob':   precip,
        'uv_index':      uv,
        'is_day':        bool(is_day),
        'zecken_warnung': zecken,
    }
    _location_cache[key] = (now, data)
    return data

# Wichtige deutsche Städte als Stichprobe
CITIES = [
    {"name": "Berlin",    "lat": 52.52, "lon": 13.41},
    {"name": "München",   "lat": 48.14, "lon": 11.58},
    {"name": "Hamburg",   "lat": 53.55, "lon": 10.00},
    {"name": "Frankfurt", "lat": 50.11, "lon":  8.68},
    {"name": "Köln",      "lat": 50.94, "lon":  6.96},
]

# WMO-Wettercodes 95–99 = Gewitter
THUNDERSTORM_CODES = {95, 96, 99}


async def get_weather_summary() -> dict:
    """
    Holt Tagesprognose für mehrere deutsche Städte von Open-Meteo.
    Gibt zurück: {"max_temp_c": float, "thunderstorm": bool}
    """
    max_temp = -999.0
    thunderstorm = False

    async with httpx.AsyncClient(timeout=10.0) as client:
        for city in CITIES:
            url = (
                "https://api.open-meteo.com/v1/forecast"
                f"?latitude={city['lat']}&longitude={city['lon']}"
                "&daily=temperature_2m_max,precipitation_probability_max,weathercode"
                "&timezone=Europe%2FBerlin&forecast_days=1"
            )
            try:
                resp = await client.get(url)
                resp.raise_for_status()
                data = resp.json()

                daily = data.get("daily", {})
                temps = daily.get("temperature_2m_max", [None])
                precip_probs = daily.get("precipitation_probability_max", [None])
                weathercodes = daily.get("weathercode", [None])

                temp = temps[0] if temps else None
                precip_prob = precip_probs[0] if precip_probs else None
                weathercode = weathercodes[0] if weathercodes else None

                if temp is not None and temp > max_temp:
                    max_temp = temp

                if (
                    precip_prob is not None and precip_prob > 50
                    and weathercode is not None and int(weathercode) in THUNDERSTORM_CODES
                ):
                    thunderstorm = True
                    logger.info(f"Gewitter erkannt: {city['name']} (Code {weathercode}, {precip_prob}% Niederschlag)")

            except Exception as e:
                logger.warning(f"Wetter-Abruf für {city['name']} fehlgeschlagen: {e}")

    if max_temp == -999.0:
        max_temp = 0.0

    logger.info(f"Wetter-Zusammenfassung: max_temp={max_temp}°C, thunderstorm={thunderstorm}")
    return {"max_temp_c": max_temp, "thunderstorm": thunderstorm}


# ---------------------------------------------------------------------------
# 7-Tage-Vorhersage
# ---------------------------------------------------------------------------
import asyncio  # noqa: E402 — appended section

_forecast_cache: dict = {}
_FORECAST_TTL = 3600  # 1 Stunde

_WDAY_DE = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']


def _wind_dir(deg: float) -> str:
    dirs = ['N', 'NO', 'O', 'SO', 'S', 'SW', 'W', 'NW']
    idx = round(deg / 45) % 8
    return dirs[idx]


def _asphalt_temp(air_max: float, uv_max: float) -> tuple[float, str]:
    bonus = min(uv_max * 3.0, 30.0)
    asphalt = air_max + bonus
    if asphalt <= 30:
        warn = 'safe'
    elif asphalt <= 40:
        warn = 'warm'
    elif asphalt <= 55:
        warn = 'hot'
    else:
        warn = 'danger'
    return round(asphalt, 1), warn


def _pollen_lvl(val: float | None) -> dict:
    if val is None:
        return {'level': 0, 'label': 'keine'}
    if val < 5:
        return {'level': 1, 'label': 'niedrig'}
    if val < 25:
        return {'level': 2, 'label': 'mittel'}
    if val < 100:
        return {'level': 3, 'label': 'hoch'}
    return {'level': 4, 'label': 'sehr hoch'}


async def get_forecast(lat: float, lon: float) -> dict:
    """7-Tage-Wettervorhersage inkl. Pollen, Asphalttemperatur, Zecken. 1h TTL-Cache."""
    key = (round(lat, 2), round(lon, 2))
    now = time.time()
    if key in _forecast_cache:
        ts, cached = _forecast_cache[key]
        if now - ts < _FORECAST_TTL:
            return cached

    forecast_url = (
        "https://api.open-meteo.com/v1/forecast"
        f"?latitude={lat}&longitude={lon}"
        "&daily=temperature_2m_max,temperature_2m_min,apparent_temperature_max,"
        "apparent_temperature_min,precipitation_probability_max,precipitation_sum,"
        "weathercode,windspeed_10m_max,winddirection_10m_dominant,uv_index_max,"
        "sunrise,sunset"
        "&timezone=auto&forecast_days=7"
    )
    pollen_url = (
        "https://air-quality-api.open-meteo.com/v1/air-quality"
        f"?latitude={lat}&longitude={lon}"
        "&hourly=alder_pollen,birch_pollen,grass_pollen,mugwort_pollen,ragweed_pollen"
        "&timezone=auto&forecast_days=7"
    )

    async with httpx.AsyncClient(timeout=10.0) as client:
        forecast_task = client.get(forecast_url)
        pollen_task   = client.get(pollen_url)
        forecast_resp, pollen_resp = await asyncio.gather(
            forecast_task, pollen_task, return_exceptions=True
        )

    # --- Forecast (required) ---
    if isinstance(forecast_resp, Exception):
        raise forecast_resp
    forecast_resp.raise_for_status()
    raw = forecast_resp.json()

    daily    = raw.get('daily', {})
    timezone = raw.get('timezone', 'auto')

    dates       = daily.get('time', [])
    temp_max    = daily.get('temperature_2m_max', [])
    temp_min    = daily.get('temperature_2m_min', [])
    feels_max   = daily.get('apparent_temperature_max', [])
    feels_min   = daily.get('apparent_temperature_min', [])
    precip_prob = daily.get('precipitation_probability_max', [])
    precip_sum  = daily.get('precipitation_sum', [])
    wcodes      = daily.get('weathercode', [])
    wind_kmh    = daily.get('windspeed_10m_max', [])
    wind_deg    = daily.get('winddirection_10m_dominant', [])
    uv_index    = daily.get('uv_index_max', [])
    sunrises    = daily.get('sunrise', [])
    sunsets     = daily.get('sunset', [])

    # --- Pollen (optional) ---
    pollen_daily: dict | None = None
    if not isinstance(pollen_resp, Exception):
        try:
            pollen_resp.raise_for_status()
            praw    = pollen_resp.json()
            hourly  = praw.get('hourly', {})
            htimes  = hourly.get('time', [])
            # aggregate hourly → daily max per type
            pollen_types = {
                'erle':    hourly.get('alder_pollen', []),
                'birke':   hourly.get('birch_pollen', []),
                'graeser': hourly.get('grass_pollen', []),
                'beifuss': hourly.get('mugwort_pollen', []),
                'ambrosia': hourly.get('ragweed_pollen', []),
            }
            # build date → max mapping per type
            pollen_daily = {ptype: {} for ptype in pollen_types}
            for i, ts_str in enumerate(htimes):
                day_str = ts_str[:10]  # 'YYYY-MM-DD'
                for ptype, vals in pollen_types.items():
                    v = vals[i] if i < len(vals) else None
                    if v is not None:
                        prev = pollen_daily[ptype].get(day_str)
                        pollen_daily[ptype][day_str] = max(prev, v) if prev is not None else v
        except Exception as e:
            logger.warning(f"Pollen-Abruf fehlgeschlagen: {e}")
            pollen_daily = None

    # --- Assemble days ---
    days = []
    for i, date_str in enumerate(dates):
        wcode     = int(wcodes[i]) if i < len(wcodes) and wcodes[i] is not None else 0
        desc, icon = _WMO.get(wcode, ('Unbekannt', 'cloud'))

        t_max  = temp_max[i]  if i < len(temp_max)  else None
        t_min  = temp_min[i]  if i < len(temp_min)  else None
        f_max  = feels_max[i] if i < len(feels_max) else None
        f_min  = feels_min[i] if i < len(feels_min) else None
        pp     = precip_prob[i] if i < len(precip_prob) else None
        ps     = precip_sum[i]  if i < len(precip_sum)  else None
        wk     = wind_kmh[i]   if i < len(wind_kmh)   else None
        wd_deg = wind_deg[i]   if i < len(wind_deg)   else None
        uv     = uv_index[i]   if i < len(uv_index)   else None

        # Sunrise / Sunset → HH:MM only (format: "2025-05-02T06:12")
        sunrise_raw = sunrises[i] if i < len(sunrises) else None
        sunset_raw  = sunsets[i]  if i < len(sunsets)  else None
        sunrise_hm  = sunrise_raw[11:16] if sunrise_raw and len(sunrise_raw) >= 16 else sunrise_raw
        sunset_hm   = sunset_raw[11:16]  if sunset_raw  and len(sunset_raw)  >= 16 else sunset_raw

        # Weekday
        try:
            dt_obj = datetime.strptime(date_str, '%Y-%m-%d')
            wday   = _WDAY_DE[dt_obj.weekday()]
        except Exception:
            wday = ''

        # Asphalt
        asphalt_t, asphalt_w = _asphalt_temp(t_max or 0.0, uv or 0.0)

        # Zecken
        month  = datetime.strptime(date_str, '%Y-%m-%d').month
        zecken = None
        if t_max is not None and t_max > 7.0 and 3 <= month <= 10:
            zecken = 'hoch' if t_max > 20 else ('mittel' if t_max > 12 else 'niedrig')

        # Pollen
        if pollen_daily is not None:
            pollen_out = {
                pt: _pollen_lvl(pollen_daily[pt].get(date_str))
                for pt in ('erle', 'birke', 'graeser', 'beifuss', 'ambrosia')
            }
        else:
            pollen_out = None

        days.append({
            'date':         date_str,
            'wday':         wday,
            'weathercode':  wcode,
            'desc':         desc,
            'icon':         icon,
            'temp_max':     t_max,
            'temp_min':     t_min,
            'feels_max':    f_max,
            'feels_min':    f_min,
            'precip_prob':  pp,
            'precip_sum':   ps,
            'wind_kmh':     wk,
            'wind_dir':     _wind_dir(wd_deg) if wd_deg is not None else None,
            'wind_dir_deg': wd_deg,
            'uv_index':     uv,
            'sunrise':      sunrise_hm,
            'sunset':       sunset_hm,
            'asphalt_temp': asphalt_t,
            'asphalt_warn': asphalt_w,
            'pollen':       pollen_out,
            'zecken':       zecken,
            'thunderstorm': wcode in {95, 96, 99},
            'paw_cold':     wcode in {71, 73, 75, 77, 85, 86} or (t_min is not None and t_min < 0),
        })

    result = {'timezone': timezone, 'days': days}
    _forecast_cache[key] = (now, result)
    _log_forecast(round(lat, 1), round(lon, 1), days)
    return result


def _log_forecast(lat_r: float, lon_r: float, days: list) -> None:
    """Speichert jeden Forecast-Tag in weather_log (INSERT OR IGNORE — kein Überschreiben)."""
    if not days:
        return
    try:
        import json
        from database import db
        with db() as conn:
            for d in days:
                pollen = d.get('pollen') or {}
                conn.execute("""
                    INSERT OR IGNORE INTO weather_log
                        (date, lat_r, lon_r,
                         temp_max, temp_min, feels_max,
                         precip_prob, precip_sum,
                         wind_kmh, wind_dir, uv_index,
                         weathercode, weatherdesc,
                         sunrise, sunset,
                         asphalt_temp, asphalt_warn, zecken,
                         pollen_erle, pollen_birke, pollen_graeser,
                         pollen_beifuss, pollen_ambrosia,
                         forecast_json)
                    VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
                """, (
                    d['date'], lat_r, lon_r,
                    d.get('temp_max'), d.get('temp_min'), d.get('feels_max'),
                    d.get('precip_prob'), d.get('precip_sum'),
                    d.get('wind_kmh'), d.get('wind_dir'), d.get('uv_index'),
                    d.get('weathercode'), d.get('desc'),
                    d.get('sunrise'), d.get('sunset'),
                    d.get('asphalt_temp'), d.get('asphalt_warn'), d.get('zecken'),
                    pollen.get('erle',    {}).get('level'),
                    pollen.get('birke',   {}).get('level'),
                    pollen.get('graeser', {}).get('level'),
                    pollen.get('beifuss', {}).get('level'),
                    pollen.get('ambrosia',{}).get('level'),
                    json.dumps(d, ensure_ascii=False),
                ))
    except Exception as e:
        logger.warning(f"weather_log insert fehlgeschlagen: {e}")
