from datetime import date
from unittest.mock import Mock, patch

from django.test import TestCase

from location_data.models import AnalysisGridCell, AnalysisGridObservation, RemoteSensingRun, SoilLocation
from location_data.tasks import _upsert_grid_observations, run_remote_sensing_analysis


class RemoteSensingTaskDiagnosticsTests(TestCase):
    def setUp(self):
        self.boundary = {
            "type": "Polygon",
            "coordinates": [
                [
                    [51.3890, 35.6890],
                    [51.3900, 35.6890],
                    [51.3900, 35.6900],
                    [51.3890, 35.6900],
                    [51.3890, 35.6890],
                ]
            ],
        }
        self.location = SoilLocation.objects.create(
            latitude="35.689200",
            longitude="51.389000",
            farm_boundary=self.boundary,
        )
        self.run = RemoteSensingRun.objects.create(
            soil_location=self.location,
            block_code="",
            chunk_size_sqm=900,
            temporal_start=date(2026, 4, 9),
            temporal_end=date(2026, 5, 9),
            status=RemoteSensingRun.STATUS_PENDING,
            metadata={},
        )
        self.cell = AnalysisGridCell.objects.create(
            soil_location=self.location,
            block_code="",
            cell_code="cell-1",
            chunk_size_sqm=900,
            geometry=self.boundary,
            centroid_lat="35.689200",
            centroid_lon="51.389200",
        )

    def test_upsert_logs_and_stores_diagnostics_for_empty_observations(self):
        metric_payload = {
            "results": {},
            "metadata": {
                "backend": "openeo",
                "backend_url": "https://openeofed.dataspace.copernicus.eu",
                "collections_used": ["SENTINEL2_L2A"],
                "job_refs": {"ndvi": "job-1"},
                "failed_metrics": [],
                "payload_diagnostics": {
                    "ndvi": {
                        "returned_cell_count": 0,
                        "payload_keys_sample": [],
                        "available_features": ["mean"],
                    }
                },
            },
        }

        with self.assertLogs("location_data.tasks", level="WARNING") as captured:
            summary = _upsert_grid_observations(
                cells=[self.cell],
                run=self.run,
                temporal_start=date(2026, 4, 9),
                temporal_end=date(2026, 5, 9),
                metric_payload=metric_payload,
            )

        log_output = "\n".join(captured.output)
        self.assertIn("Persisting empty observation for cell=cell-1, run_id=", log_output)
        self.assertIn("No payload cells matched DB cell_codes for run_id=", log_output)
        self.assertIn("All persisted observations are empty for run_id=", log_output)

        self.assertEqual(summary["total_observation_count"], 1)
        self.assertEqual(summary["usable_observation_count"], 0)
        self.assertEqual(summary["fully_null_observation_count"], 1)
        self.assertEqual(summary["matched_cell_count"], 0)
        self.assertEqual(summary["payload_keys_sample"], [])
        self.assertEqual(summary["available_features"], ["mean"])

        observation = AnalysisGridObservation.objects.get(cell=self.cell)
        self.assertIsNone(observation.ndvi)
        self.assertIsNone(observation.ndwi)
        self.assertIsNone(observation.soil_vv)
        self.assertIsNone(observation.soil_vv_db)

        self.run.refresh_from_db()
        diagnostics = self.run.metadata["diagnostics"]["empty_observations"]
        self.assertEqual(diagnostics["job_ref"], {"ndvi": "job-1"})
        self.assertEqual(diagnostics["total_cells"], 1)
        self.assertEqual(diagnostics["matched_cells"], 0)
        self.assertEqual(diagnostics["payload_keys_sample"], [])
        self.assertEqual(diagnostics["available_features"], ["mean"])

    def test_run_remote_sensing_analysis_refetches_when_cached_observations_are_empty(self):
        AnalysisGridObservation.objects.create(
            cell=self.cell,
            run=self.run,
            temporal_start=date(2026, 4, 9),
            temporal_end=date(2026, 5, 9),
            metadata={},
        )
        subdivision_result = Mock(
            id=99,
            cluster_count=1,
            selected_features=["ndvi", "ndwi", "soil_vv_db"],
            metadata={"used_cell_count": 1, "skipped_cell_count": 0, "kmeans_params": {}},
            skipped_cell_codes=[],
        )
        remote_payload = {
            "results": {
                "cell-1": {
                    "ndvi": 0.52,
                    "ndwi": 0.21,
                    "soil_vv": 10.0,
                    "soil_vv_db": 10.0,
                }
            },
            "metadata": {
                "backend": "openeo",
                "backend_url": "https://openeofed.dataspace.copernicus.eu",
                "collections_used": ["SENTINEL2_L2A", "SENTINEL1_GRD"],
                "job_refs": {"ndvi": "job-1"},
                "failed_metrics": [],
                "payload_diagnostics": {
                    "ndvi": {
                        "returned_cell_count": 1,
                        "payload_keys_sample": ["0"],
                        "available_features": ["mean"],
                    }
                },
            },
        }

        with patch(
            "location_data.tasks.create_or_get_analysis_grid_cells",
            return_value={
                "created": False,
                "block_code": "",
                "total_count": 1,
                "created_count": 0,
                "chunk_size_sqm": 900,
                "existing_count": 1,
            },
        ), patch(
            "location_data.tasks.compute_remote_sensing_metrics",
            return_value=remote_payload,
        ) as compute_mock, patch(
            "location_data.tasks._ensure_subdivision_result",
            return_value=subdivision_result,
        ):
            summary = run_remote_sensing_analysis(
                soil_location_id=self.location.id,
                block_code="",
                temporal_start=date(2026, 4, 9),
                temporal_end=date(2026, 5, 9),
                run_id=self.run.id,
            )

        compute_mock.assert_called_once()
        self.assertEqual(summary["source"], "openeo")
        self.assertEqual(summary["processed_cell_count"], 1)

        observation = AnalysisGridObservation.objects.get(cell=self.cell)
        self.assertEqual(observation.ndvi, 0.52)
        self.assertEqual(observation.ndwi, 0.21)
        self.assertEqual(observation.soil_vv, 10.0)
        self.assertEqual(observation.soil_vv_db, 10.0)

        self.run.refresh_from_db()
        cached_details = self.run.metadata["stage_details"]["using_cached_observations"]
        self.assertEqual(cached_details["source"], "database")
        self.assertFalse(cached_details["usable"])
        self.assertTrue(cached_details["refetching"])
