import pytest
from unittest.mock import AsyncMock, patch, MagicMock


def test_parse_open_meteo_response():
    """parse_forecast() should extract daily data into a clean list."""
    from fabledassistant.services.weather import parse_forecast
    raw = {
        "daily": {
            "time": ["2026-03-11", "2026-03-12"],
            "temperature_2m_max": [15.0, 12.0],
            "temperature_2m_min": [8.0, 6.0],
            "precipitation_sum": [0.0, 3.2],
            "weathercode": [0, 61],
            "windspeed_10m_max": [10.0, 25.0],
        }
    }
    days = parse_forecast(raw)
    assert len(days) == 2
    assert days[0]["date"] == "2026-03-11"
    assert days[0]["temp_max"] == 15.0
    assert days[0]["temp_min"] == 8.0
    assert days[0]["precip_mm"] == 0.0
    assert days[0]["weathercode"] == 0
    assert days[1]["date"] == "2026-03-12"
    assert days[1]["precip_mm"] == 3.2


def test_describe_weathercode():
    """describe_weathercode() should return human-readable strings."""
    from fabledassistant.services.weather import describe_weathercode
    assert describe_weathercode(0) == "Clear sky"
    assert describe_weathercode(61) == "Rain: slight"
    assert describe_weathercode(95) == "Thunderstorm"
    assert "Unknown" in describe_weathercode(999)


def test_detect_forecast_changes_no_change():
    """detect_changes() should return empty list when forecasts are identical."""
    from fabledassistant.services.weather import detect_changes
    day = {"date": "2026-03-12", "temp_max": 12.0, "temp_min": 6.0,
           "precip_mm": 3.2, "weathercode": 61}
    assert detect_changes([day], [day]) == []


def test_detect_forecast_changes_rain_added():
    """detect_changes() should flag when precipitation appears."""
    from fabledassistant.services.weather import detect_changes
    old = [{"date": "2026-03-12", "temp_max": 14.0, "temp_min": 8.0,
            "precip_mm": 0.0, "weathercode": 2}]
    new = [{"date": "2026-03-12", "temp_max": 12.0, "temp_min": 7.0,
            "precip_mm": 4.5, "weathercode": 61}]
    changes = detect_changes(old, new)
    assert len(changes) == 1
    assert "2026-03-12" in changes[0]
    assert "rain" in changes[0].lower() or "precip" in changes[0].lower()


def test_parse_weather_card_data_returns_none_when_stale():
    """Should return None when cache is older than 24 hours."""
    from datetime import datetime, timezone, timedelta
    from fabledassistant.services.weather import parse_weather_card_data

    cache = MagicMock()
    cache.fetched_at = datetime.now(timezone.utc) - timedelta(hours=25)
    cache.forecast_json = {}
    assert parse_weather_card_data(cache) is None


def test_parse_weather_card_data_returns_card_schema():
    """Should return the correct metadata.weather schema for fresh cache."""
    from datetime import datetime, timezone, timedelta, date
    from fabledassistant.services.weather import parse_weather_card_data

    today = date.today().isoformat()
    yesterday = (date.today() - timedelta(days=1)).isoformat()
    tomorrow = (date.today() + timedelta(days=1)).isoformat()

    cache = MagicMock()
    cache.fetched_at = datetime.now(timezone.utc)
    cache.location_label = "Berlin, DE"
    cache.forecast_json = {
        "current_weather": {"temperature": 12.0, "weathercode": 2},
        "daily": {
            "time": [yesterday, today, tomorrow],
            "temperature_2m_max": [14.0, 16.0, 18.0],
            "temperature_2m_min": [9.0, 8.0, 10.0],
            "precipitation_sum": [0.0, 0.0, 1.5],
            "weathercode": [1, 2, 61],
            "windspeed_10m_max": [10.0, 12.0, 8.0],
        }
    }

    card = parse_weather_card_data(cache)
    assert card is not None
    assert card["current_temp"] == 12
    assert card["condition"] == "Partly cloudy"
    assert card["today_high"] == 16
    assert card["today_low"] == 8
    assert card["yesterday_high"] == 14
    assert card["yesterday_low"] == 9
    assert len(card["forecast"]) >= 1
    assert card["location"] == "Berlin, DE"
    assert card["precip_summary"] is None


def test_summarize_precip_dry():
    from fabledassistant.services.weather import _summarize_precip
    assert _summarize_precip([(h, 10) for h in range(24)]) is None


def test_summarize_precip_all_day():
    from fabledassistant.services.weather import _summarize_precip
    result = _summarize_precip([(h, 60) for h in range(24)])
    assert result is not None
    assert "all day" in result.lower()


def test_summarize_precip_window():
    from fabledassistant.services.weather import _summarize_precip
    hourly = [(h, 5) for h in range(24)]
    for h in range(14, 18):
        hourly[h] = (h, 65)
    result = _summarize_precip(hourly)
    assert result is not None
    assert "2 PM" in result
    assert "65%" in result


def test_parse_weather_card_includes_hourly_precip_summary():
    from datetime import datetime, timezone, timedelta, date
    from fabledassistant.services.weather import parse_weather_card_data

    today = date.today().isoformat()
    yesterday = (date.today() - timedelta(days=1)).isoformat()
    tomorrow = (date.today() + timedelta(days=1)).isoformat()

    hourly_times = [f"{today}T{h:02d}:00" for h in range(24)]
    hourly_probs = [5] * 24
    for h in range(14, 18):
        hourly_probs[h] = 70

    cache = MagicMock()
    cache.fetched_at = datetime.now(timezone.utc)
    cache.location_label = "Berlin, DE"
    cache.forecast_json = {
        "current_weather": {"temperature": 12.0, "weathercode": 61},
        "daily": {
            "time": [yesterday, today, tomorrow],
            "temperature_2m_max": [14.0, 16.0, 18.0],
            "temperature_2m_min": [9.0, 8.0, 10.0],
            "precipitation_sum": [0.0, 5.0, 1.5],
            "precipitation_probability_max": [0, 70, 30],
            "weathercode": [1, 61, 61],
            "windspeed_10m_max": [10.0, 12.0, 8.0],
        },
        "hourly": {
            "time": hourly_times,
            "precipitation_probability": hourly_probs,
        },
    }

    card = parse_weather_card_data(cache)
    assert card is not None
    assert card["precip_summary"] is not None
    assert "2 PM" in card["precip_summary"]
