# live_data.py — four free sources of live signals for the Disaster Lab. No API keys.
#
#   from live_data import rain_days, river_days, quakes, events
#
# Every function asks one website and gives back plain Python: lists of (date, value)
# or lists of dictionaries. The notebook scores them with the same functions it uses
# for the sample data.
#
#   rain_days()   Open-Meteo weather: daily rain, the past days and a forecast
#   river_days()  Open-Meteo flood API: river flow from the GloFAS model, in m3/s
#   quakes()      USGS: earthquakes in the last hours, anywhere in the world
#   events()      NASA EONET: floods, storms and quakes that are open or recent
#
#   pip install requests

import datetime

import requests

CHIANG_MAI = (18.79, 98.98)          # latitude, longitude
PING_GLOFAS = (18.70, 99.02)         # a GloFAS grid cell on the Ping below the city
HEADERS = {"User-Agent": "krueng-disaster-lab/1.0 (educational)"}


def get_json(url, params):
    """Ask a website for JSON. Raises an error if the website says no."""
    reply = requests.get(url, params=params, headers=HEADERS, timeout=30)
    reply.raise_for_status()
    return reply.json()


def rain_days(place=CHIANG_MAI, past=7, future=7):
    """Daily rain in mm as (date, mm) pairs: past days measured, future days forecast."""
    lat, lon = place
    daily = get_json("https://api.open-meteo.com/v1/forecast",
                     {"latitude": lat, "longitude": lon, "daily": "precipitation_sum",
                      "past_days": past, "forecast_days": future,
                      "timezone": "Asia/Bangkok"})["daily"]
    return list(zip(daily["time"], daily["precipitation_sum"]))


def rain_history(place, start, end):
    """Daily rain in mm as (date, mm) pairs for past dates (ERA5 reanalysis)."""
    lat, lon = place
    daily = get_json("https://archive-api.open-meteo.com/v1/archive",
                     {"latitude": lat, "longitude": lon, "start_date": start,
                      "end_date": end, "daily": "precipitation_sum",
                      "timezone": "Asia/Bangkok"})["daily"]
    return list(zip(daily["time"], daily["precipitation_sum"]))


def river_days(place=PING_GLOFAS, past=30, future=7):
    """River flow in m3/s as (date, flow) pairs from the GloFAS model, not a gauge."""
    lat, lon = place
    daily = get_json("https://flood-api.open-meteo.com/v1/flood",
                     {"latitude": lat, "longitude": lon, "daily": "river_discharge",
                      "past_days": past, "forecast_days": future})["daily"]
    return list(zip(daily["time"], daily["river_discharge"]))


def quakes(hours=24, min_magnitude=4.5):
    """Earthquakes from the USGS in the last hours, biggest first.
    Each one is a dictionary: time, magnitude, place, lat, lon, depth_km."""
    now = datetime.datetime.now(datetime.timezone.utc)
    since = now - datetime.timedelta(hours=hours)
    found = get_json("https://earthquake.usgs.gov/fdsnws/event/1/query",
                     {"format": "geojson", "starttime": since.strftime("%Y-%m-%dT%H:%M"),
                      "minmagnitude": min_magnitude, "orderby": "magnitude"})
    result = []
    for feature in found["features"]:
        facts = feature["properties"]
        lon, lat, depth = feature["geometry"]["coordinates"]
        seconds = facts["time"] / 1000
        when = datetime.datetime.fromtimestamp(seconds, datetime.timezone.utc)
        result.append({"time": when.strftime("%Y-%m-%d %H:%M UTC"),
                       "magnitude": facts["mag"], "place": facts["place"],
                       "lat": lat, "lon": lon, "depth_km": depth})
    return result


def events(days=30, kinds="floods,earthquakes,severeStorms,landslides"):
    """Natural events from NASA EONET, newest first.
    Each one is a dictionary: title, kind, date, lat, lon."""
    found = get_json("https://eonet.gsfc.nasa.gov/api/v3/events",
                     {"status": "all", "days": days, "category": kinds, "limit": 500})
    result = []
    for event in found["events"]:
        shape = event["geometry"][-1]
        lat, lon = centre(shape)
        result.append({"title": event["title"], "kind": event["categories"][0]["title"],
                       "date": shape["date"][:10], "lat": lat, "lon": lon})
    return result


def centre(shape):
    """(lat, lon) of an EONET shape: the point itself, or the middle of a polygon's ring.
    EONET writes its points as [lon, lat] but its flood polygons as [lat, lon]. Checked on
    1 October 2026 against a flood in Tak and a fire in Brazil."""
    if shape["type"] == "Point":
        lon, lat = shape["coordinates"]
        return lat, lon
    ring = shape["coordinates"][0]
    lats = [point[0] for point in ring]
    lons = [point[1] for point in ring]
    return sum(lats) / len(lats), sum(lons) / len(lons)
