"""
سرویس‌های هواشناسی — واکشی پیش‌بینی ۷ روزه و ذخیره در دیتابیس.
"""

import logging
from datetime import date, timedelta

from django.apps import apps
from django.db import transaction

from location_data.models import SoilLocation

from .adapters import DEFAULT_FORECAST_DAYS
from .models import WeatherForecast

logger = logging.getLogger(__name__)


def fetch_weather_from_api(latitude: float, longitude: float) -> dict | None:
    """
    واکشی پیش‌بینی هواشناسی از provider فعال.
    خروجی در قالب سازگار با Open-Meteo daily format برگردانده می‌شود.
    """
    adapter = apps.get_app_config("weather").get_weather_data_adapter()
    return adapter.fetch_forecast(
        latitude=latitude,
        longitude=longitude,
        days=DEFAULT_FORECAST_DAYS,
    )


def parse_weather_response(data: dict) -> list[dict]:
    """
    تبدیل پاسخ API به لیست dict برای ذخیره در WeatherForecast.
    فرمت ورودی: Open-Meteo daily format.
    """
    daily = data.get("daily", {})
    times = daily.get("time", [])
    forecasts = []

    for index, date_str in enumerate(times):
        forecasts.append(
            {
                "forecast_date": date_str,
                "temperature_max": _safe_index(daily.get("temperature_2m_max"), index),
                "temperature_min": _safe_index(daily.get("temperature_2m_min"), index),
                "temperature_mean": _safe_index(daily.get("temperature_2m_mean"), index),
                "precipitation": _safe_index(daily.get("precipitation_sum"), index),
                "precipitation_probability": _safe_index(
                    daily.get("precipitation_probability_max"),
                    index,
                ),
                "humidity_mean": _safe_index(
                    daily.get("relative_humidity_2m_mean"),
                    index,
                ),
                "wind_speed_max": _safe_index(daily.get("wind_speed_10m_max"), index),
                "et0": _safe_index(
                    daily.get("et0_fao_evapotranspiration"),
                    index,
                ),
                "weather_code": _safe_index(daily.get("weather_code"), index),
            }
        )
    return forecasts


def _safe_index(lst: list | None, index: int):
    """مقدار index را از لیست برمی‌گرداند یا None."""
    if lst is None or index >= len(lst):
        return None
    return lst[index]


def update_weather_for_location(location: SoilLocation) -> dict:
    """
    واکشی و ذخیره پیش‌بینی هواشناسی ۷ روزه برای یک SoilLocation.

    خروجی:
        {"status": "success"|"no_data"|"error", "location_id": int, ...}
    """
    lat = float(location.latitude)
    lon = float(location.longitude)

    try:
        data = fetch_weather_from_api(lat, lon)
    except Exception as exc:
        logger.error("Weather API error for location %s: %s", location.id, exc)
        return {
            "status": "error",
            "location_id": location.id,
            "error": str(exc),
        }

    if data is None:
        logger.info("Weather provider returned no data for location %s.", location.id)
        return {
            "status": "no_data",
            "location_id": location.id,
            "message": "Weather provider returned no data.",
        }

    forecasts = parse_weather_response(data)

    with transaction.atomic():
        for forecast in forecasts:
            WeatherForecast.objects.update_or_create(
                location=location,
                forecast_date=forecast.pop("forecast_date"),
                defaults=forecast,
            )

    return {
        "status": "success",
        "location_id": location.id,
        "days_updated": len(forecasts),
    }


def update_weather_for_all_locations() -> list[dict]:
    """
    واکشی پیش‌بینی هواشناسی برای تمام SoilLocation‌های موجود.
    """
    results = []
    for location in SoilLocation.objects.all():
        results.append(update_weather_for_location(location))
    return results


def get_forecast_for_location(
    location: SoilLocation,
    days: int = DEFAULT_FORECAST_DAYS,
) -> list[WeatherForecast]:
    """
    دریافت پیش‌بینی‌های ذخیره‌شده برای یک location (تا N روز آینده).
    """
    today = date.today()
    end_date = today + timedelta(days=days)
    return list(
        WeatherForecast.objects.filter(
            location=location,
            forecast_date__gte=today,
            forecast_date__lte=end_date,
        ).order_by("forecast_date")
    )


def should_irrigate_today(location: SoilLocation) -> dict:
    """
    بررسی ساده: آیا فردا باران می‌بارد؟
    اگر بارش فردا بیشتر از آستانه باشد → آبیاری لازم نیست.
    """
    tomorrow = date.today() + timedelta(days=1)
    forecast = WeatherForecast.objects.filter(
        location=location,
        forecast_date=tomorrow,
    ).first()

    if forecast is None:
        return {
            "needs_irrigation": None,
            "tomorrow_precipitation": None,
            "tomorrow_date": str(tomorrow),
            "reason": "داده پیش‌بینی فردا موجود نیست.",
        }

    rain_threshold_mm = 2.0
    if forecast.precipitation is not None and forecast.precipitation >= rain_threshold_mm:
        return {
            "needs_irrigation": False,
            "tomorrow_precipitation": forecast.precipitation,
            "tomorrow_date": str(tomorrow),
            "reason": (
                f"فردا {forecast.precipitation} mm بارش پیش‌بینی شده — "
                "نیاز به آبیاری نیست."
            ),
        }

    return {
        "needs_irrigation": True,
        "tomorrow_precipitation": forecast.precipitation,
        "tomorrow_date": str(tomorrow),
        "reason": "بارش فردا ناچیز یا صفر — آبیاری توصیه می‌شود.",
    }
