from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import patch

from django.test import TestCase, override_settings
from rest_framework.test import APIClient

from rag.failure_contract import RAGServiceError


@override_settings(ROOT_URLCONF="weather.urls")
class FarmWeatherApiTests(TestCase):
    def setUp(self):
        self.client = APIClient()

    @patch("weather.views.apps.get_app_config")
    def test_farm_weather_api_returns_payload(self, mock_get_app_config):
        mock_service = SimpleNamespace(
            get_farm_weather_card=lambda **_kwargs: {
                "condition": "صاف",
                "temperature": 28.0,
                "unit": "°C",
                "humidity": 42.0,
                "windSpeed": 15.0,
                "windUnit": "km/h",
                "chartData": {
                    "labels": ["2026-04-01", "2026-04-02"],
                    "series": [[28.0, 29.0]],
                },
                "source_metadata": {
                    "weather": {"source": "center_location_forecast", "policy": "center_location_latest_forecast"},
                    "agronomic_metrics": {"source": "build_ai_farm_snapshot", "policy": "cluster_block_farm_aggregated", "available": True},
                },
            }
        )
        mock_get_app_config.return_value = SimpleNamespace(
            get_farm_weather_service=lambda: mock_service
        )

        response = self.client.post(
            "/farm-card/",
            data={"farm_uuid": "550e8400-e29b-41d4-a716-446655440000"},
            format="json",
        )

        self.assertEqual(response.status_code, 200)
        payload = response.json()["data"]
        self.assertEqual(payload["condition"], "صاف")
        self.assertEqual(payload["chartData"]["labels"][0], "2026-04-01")
        self.assertEqual(payload["source_metadata"]["weather"]["policy"], "center_location_latest_forecast")

    @patch("weather.views.apps.get_app_config")
    def test_farm_weather_api_returns_404_for_missing_farm(self, mock_get_app_config):
        mock_service = SimpleNamespace(
            get_farm_weather_card=lambda **_kwargs: (_ for _ in ()).throw(ValueError("Farm not found."))
        )
        mock_get_app_config.return_value = SimpleNamespace(
            get_farm_weather_service=lambda: mock_service
        )

        response = self.client.post(
            "/farm-card/",
            data={"farm_uuid": "550e8400-e29b-41d4-a716-446655440000"},
            format="json",
        )

        self.assertEqual(response.status_code, 404)
        self.assertEqual(response.json()["msg"], "Farm not found.")


@override_settings(ROOT_URLCONF="weather.urls")
class WaterNeedPredictionApiTests(TestCase):
    def setUp(self):
        self.client = APIClient()

    @patch("weather.views.apps.get_app_config")
    def test_water_need_api_returns_payload(self, mock_get_app_config):
        mock_service = SimpleNamespace(
            get_water_need_prediction=lambda **_kwargs: {
                "farm_uuid": "550e8400-e29b-41d4-a716-446655440000",
                "totalNext7Days": 24.6,
                "unit": "mm",
                "categories": ["روز 1", "روز 2"],
                "series": [{"name": "نیاز آبی تعدیل‌شده", "data": [3.2, 4.1]}],
                "dailyBreakdown": [
                    {"forecast_date": "2026-04-01", "gross_irrigation_mm": 3.2},
                    {"forecast_date": "2026-04-02", "gross_irrigation_mm": 4.1},
                ],
                "insight": {
                    "summary": "جمع نياز آبي هفته آينده حدود 24.6 ميلي متر است.",
                    "irrigation_outlook": "نياز آبي در حال افزايش است.",
                    "recommended_action": "آبياري صبح زود تنظيم شود.",
                    "risk_note": "در صورت بارش موثر برنامه بازبيني شود.",
                    "confidence": 0.82,
                },
                "raw_response": "{\"summary\":\"ok\"}",
            }
        )
        mock_get_app_config.return_value = SimpleNamespace(
            get_water_need_service=lambda: mock_service
        )

        response = self.client.post(
            "/water-need-prediction/",
            data={"farm_uuid": "550e8400-e29b-41d4-a716-446655440000"},
            format="json",
        )

        self.assertEqual(response.status_code, 200)
        payload = response.json()["data"]
        self.assertEqual(payload["farm_uuid"], "550e8400-e29b-41d4-a716-446655440000")
        self.assertEqual(payload["insight"]["confidence"], 0.82)
        self.assertEqual(payload["source_metadata"]["agronomic_metrics"]["policy"], "cluster_block_farm_aggregated")
        self.assertEqual(payload["source_metadata"]["weather"]["policy"], "center_location_latest_forecast")

    @patch("weather.views.apps.get_app_config")
    def test_water_need_api_returns_404_for_missing_farm(self, mock_get_app_config):
        mock_service = SimpleNamespace(
            get_water_need_prediction=lambda **_kwargs: (_ for _ in ()).throw(ValueError("Farm not found."))
        )
        mock_get_app_config.return_value = SimpleNamespace(
            get_water_need_service=lambda: mock_service
        )

        response = self.client.post(
            "/water-need-prediction/",
            data={"farm_uuid": "550e8400-e29b-41d4-a716-446655440000"},
            format="json",
        )

        self.assertEqual(response.status_code, 404)
        self.assertEqual(response.json()["msg"], "Farm not found.")

    @patch("weather.views.apps.get_app_config")
    def test_water_need_api_returns_structured_failure_for_invalid_llm_json(self, mock_get_app_config):
        mock_service = SimpleNamespace(
            get_water_need_prediction=lambda **_kwargs: (_ for _ in ()).throw(
                RAGServiceError(
                    error_code="invalid_json",
                    message="Water need prediction LLM response was not valid JSON.",
                    source="llm",
                    retriable=True,
                    http_status=502,
                )
            )
        )
        mock_get_app_config.return_value = SimpleNamespace(
            get_water_need_service=lambda: mock_service
        )

        response = self.client.post(
            "/water-need-prediction/",
            data={"farm_uuid": "550e8400-e29b-41d4-a716-446655440000"},
            format="json",
        )

        self.assertEqual(response.status_code, 502)
        self.assertEqual(response.json()["data"]["error_code"], "invalid_json")


@override_settings(ROOT_URLCONF="soile.urls")
class SpatialPolicySmokeTests(TestCase):
    pass
