"""Open-Meteo weather API client.

Two endpoints:

  fetch_archive(session, lat, lon, start_date, end_date) -> dict
      ERA5 reanalysis data — consistent, scientifically validated.
      Available from 1940 to ~5 days ago (reanalysis processing lag).
      Use for historical backfill.

  fetch_recent(session, lat, lon, past_days) -> dict
      Forecast model blended with recent observations.
      Covers the last N days + today (fills the ERA5 lag window).
      Use for daily updates.

Both return the same structure:
  {
    "daily": {
      "time": ["2020-01-01", "2020-01-02", ...],
      "temperature_2m_max": [28.5, 27.1, ...],
      ...
    }
  }

No API key required. No rate limits for reasonable usage (~12 calls/day).
"""

import niquests

ARCHIVE_URL = "https://archive-api.open-meteo.com/v1/archive"
FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
HTTP_TIMEOUT_SECONDS = 60
MAX_RESPONSE_BYTES = 2_000_000  # multi-year response ~200 KB; 2 MB is generous

# Variables fetched for each location. All metric units.
# wind_speed_unit=ms ensures m/s (Open-Meteo default is km/h).
DAILY_VARIABLES = ",".join([
    "temperature_2m_max",
    "temperature_2m_min",
    "temperature_2m_mean",
    "precipitation_sum",
    "wind_speed_10m_max",
    "relative_humidity_2m_max",
    "cloud_cover_mean",
    "et0_fao_evapotranspiration",   # FAO Penman-Monteith ET — direct crop water demand signal
    "vapour_pressure_deficit_max",  # VPD >1.5 kPa = significant plant water stress
])


def _get(session: niquests.Session, url: str, params: dict) -> dict:
    """GET url, validate HTTP 200, return parsed JSON dict."""
    response = session.get(url, params=params, timeout=HTTP_TIMEOUT_SECONDS)

    assert response.status_code == 200, (
        f"Open-Meteo returned HTTP {response.status_code}: {response.text[:300]}"
    )
    assert len(response.content) <= MAX_RESPONSE_BYTES, (
        f"Open-Meteo response unexpectedly large: {len(response.content):,} bytes"
    )

    data = response.json()
    assert isinstance(data, dict), f"Expected dict, got {type(data)}"

    # Open-Meteo signals some errors as HTTP 200 with error=true in body
    if data.get("error"):
        raise ValueError(f"Open-Meteo API error: {data.get('reason', data)}")

    assert "daily" in data, f"Open-Meteo response missing 'daily' key: {list(data.keys())}"
    assert "time" in data["daily"], "Open-Meteo 'daily' missing 'time' array"

    return data


def fetch_archive(
    session: niquests.Session,
    lat: float,
    lon: float,
    start_date: str,
    end_date: str,
) -> dict:
    """Fetch ERA5 reanalysis daily data for a date range (YYYY-MM-DD strings)."""
    assert start_date and len(start_date) == 10, f"start_date must be YYYY-MM-DD, got {start_date!r}"
    assert end_date and len(end_date) == 10, f"end_date must be YYYY-MM-DD, got {end_date!r}"
    assert start_date <= end_date, f"start_date {start_date} must be <= end_date {end_date}"

    return _get(session, ARCHIVE_URL, {
        "latitude": lat,
        "longitude": lon,
        "start_date": start_date,
        "end_date": end_date,
        "daily": DAILY_VARIABLES,
        "wind_speed_unit": "ms",
        "timezone": "UTC",
    })


def fetch_recent(
    session: niquests.Session,
    lat: float,
    lon: float,
    past_days: int = 10,
) -> dict:
    """Fetch recent weather via Open-Meteo forecast model (fills ERA5 lag window).

    past_days=10 captures the ~5-day ERA5 lag plus buffer for missed daily runs.
    """
    assert 1 <= past_days <= 92, f"past_days must be 1-92, got {past_days}"

    return _get(session, FORECAST_URL, {
        "latitude": lat,
        "longitude": lon,
        "daily": DAILY_VARIABLES,
        "wind_speed_unit": "ms",
        "timezone": "UTC",
        "past_days": past_days,
        "forecast_days": 1,
    })
