from __future__ import annotations

import uuid
from unittest.mock import patch

from django.test import override_settings

from farm_data.models import ParameterUpdateLog, SensorData, SensorParameter
from integration_tests.base import IntegrationAPITestCase
from plant.models import Plant


@override_settings(ROOT_URLCONF="config.urls")
class FarmManagementJourneyTests(IntegrationAPITestCase):
    def test_full_management_journey_persists_farm_related_records(self) -> None:
        primary_method = self.create_irrigation_method_via_api("Drip Prime")
        backup_method = self.create_irrigation_method_via_api(
            "Sprinkler Backup",
            category="pressure",
            water_efficiency_percent=78.0,
        )

        irrigation_list_response = self.client.get("/api/irrigation/")
        self.assertEqual(irrigation_list_response.status_code, 200)
        self.assertGreaterEqual(len(irrigation_list_response.json()["data"]), 2)
        irrigation_detail_response = self.client.get(f"/api/irrigation/{primary_method['id']}/")
        self.assertEqual(irrigation_detail_response.status_code, 200)
        self.assertEqual(irrigation_detail_response.json()["data"]["name"], "Drip Prime")

        moisture_parameter = self.create_sensor_parameter_via_api()
        self.assertEqual(moisture_parameter["action"], ParameterUpdateLog.ACTION_ADDED)
        self.assertTrue(
            SensorParameter.objects.filter(sensor_key="sensor-7-1", code="soil_moisture").exists()
        )

        moisture_parameter_update = self.create_sensor_parameter_via_api(
            metadata={"min": 5, "max": 85, "ui": "gauge"},
        )
        self.assertEqual(moisture_parameter_update["action"], ParameterUpdateLog.ACTION_MODIFIED)
        self.assertEqual(
            ParameterUpdateLog.objects.filter(parameter__code="soil_moisture").count(),
            2,
        )

        tomato = self.create_plant_via_api("Tomato")
        cucumber = self.create_plant_via_api("Cucumber", watering="daily")
        removable_plant = self.create_plant_via_api("Remove Plant")

        plants_list_response = self.client.get("/api/plants/")
        self.assertEqual(plants_list_response.status_code, 200)
        returned_names = {item["name"] for item in plants_list_response.json()["data"]}
        self.assertTrue({"Tomato", "Cucumber", "Remove Plant"}.issubset(returned_names))

        plant_catalog = self.create_plant_via_api(
            "Pepper",
            growth_stage="",
            icon="sprout",
        )
        Plant.objects.filter(pk=plant_catalog["id"]).update(growth_stage="", icon="")
        plant_names_response = self.client.get("/api/plants/names/")
        self.assertEqual(plant_names_response.status_code, 200)
        plant_names_payload = {
            item["name"]: item for item in plant_names_response.json()["data"]
        }
        self.assertEqual(plant_names_payload["Pepper"]["icon"], "leaf")
        self.assertEqual(
            plant_names_payload["Pepper"]["growth_stages"],
            ["initial", "vegetative", "flowering", "fruiting", "maturity"],
        )
        pepper = Plant.objects.get(pk=plant_catalog["id"])
        self.assertEqual(
            pepper.growth_stage,
            "initial, vegetative, flowering, fruiting, maturity",
        )
        self.assertEqual(pepper.icon, "leaf")

        plant_patch_response = self.client.patch(
            f"/api/plants/{tomato['id']}/",
            data={"growth_stage": "flowering", "watering": "daily"},
            format="json",
        )
        self.assertEqual(plant_patch_response.status_code, 200)
        self.assertEqual(Plant.objects.get(pk=tomato["id"]).growth_stage, "flowering")

        plant_put_response = self.client.put(
            f"/api/plants/{cucumber['id']}/",
            data={
                "name": "Cucumber",
                "light": "full sun",
                "watering": "every day",
                "soil": "sandy loam",
                "temperature": "18-30C",
                "growth_stage": "fruiting",
                "planting_season": "spring",
                "harvest_time": "70 days",
                "spacing": "40 cm",
                "fertilizer": "potassium rich",
            },
            format="json",
        )
        self.assertEqual(plant_put_response.status_code, 200)

        with patch(
            "plant.views.fetch_plant_info_from_api",
            return_value={
                "name": "Tomato",
                "light": "full sun",
                "watering": "daily",
                "soil": "loamy",
                "temperature": "20-28C",
                "growth_stage": "flowering",
                "planting_season": "spring",
                "harvest_time": "90 days",
                "spacing": "50 cm",
                "fertilizer": "balanced NPK",
            },
        ):
            plant_fetch_response = self.client.post(
                "/api/plants/fetch-info/",
                data={"name": "Tomato"},
                format="json",
            )
        self.assertEqual(plant_fetch_response.status_code, 200)
        self.assertEqual(plant_fetch_response.json()["data"]["name"], "Tomato")

        plant_delete_response = self.client.delete(f"/api/plants/{removable_plant['id']}/")
        self.assertEqual(plant_delete_response.status_code, 200)
        self.assertFalse(Plant.objects.filter(pk=removable_plant["id"]).exists())

        farm_uuid = uuid.uuid4()
        created_farm = self.upsert_farm_via_api(
            farm_uuid=farm_uuid,
            plant_ids=[tomato["id"], cucumber["id"]],
            irrigation_method_id=primary_method["id"],
            sensor_payload={
                "sensor-7-1": {
                    "soil_moisture": 41.2,
                    "soil_temperature": 23.4,
                    "soil_ph": 6.8,
                    "electrical_conductivity": 1.1,
                    "nitrogen": 17.0,
                    "phosphorus": 12.5,
                    "potassium": 21.0,
                }
            },
        )
        self.assertEqual(created_farm["farm_uuid"], str(farm_uuid))
        farm_record = SensorData.objects.get(farm_uuid=farm_uuid)
        self.assertCountEqual(
            list(farm_record.plants.values_list("id", flat=True)),
            [tomato["id"], cucumber["id"]],
        )
        self.assertEqual(farm_record.irrigation_method_id, primary_method["id"])
        self.assertEqual(farm_record.center_location_id, self.primary_location.id)

        updated_farm = self.upsert_farm_via_api(
            farm_uuid=farm_uuid,
            plant_ids=[tomato["id"]],
            irrigation_method_id=backup_method["id"],
            sensor_payload={
                "sensor-7-1": {
                    "nitrogen": 19.5,
                    "soil_moisture": 44.0,
                },
                "leaf-sensor": {
                    "leaf_wetness": 11.0,
                    "leaf_temperature": 21.3,
                },
            },
        )
        self.assertEqual(updated_farm["irrigation_method_id"], backup_method["id"])

        farm_record.refresh_from_db()
        self.assertEqual(farm_record.irrigation_method_id, backup_method["id"])
        self.assertCountEqual(list(farm_record.plants.values_list("id", flat=True)), [tomato["id"]])
        self.assertEqual(farm_record.sensor_payload["sensor-7-1"]["soil_temperature"], 23.4)
        self.assertEqual(farm_record.sensor_payload["sensor-7-1"]["soil_moisture"], 44.0)
        self.assertEqual(farm_record.sensor_payload["sensor-7-1"]["nitrogen"], 19.5)
        self.assertEqual(farm_record.sensor_payload["leaf-sensor"]["leaf_wetness"], 11.0)
        self.assertTrue(
            SensorParameter.objects.filter(sensor_key="leaf-sensor", code="leaf_wetness").exists()
        )

        farm_detail_response = self.client.get(f"/api/farm-data/{farm_uuid}/detail/")
        self.assertEqual(farm_detail_response.status_code, 200)
        farm_detail = farm_detail_response.json()["data"]
        self.assertEqual(farm_detail["center_location"]["id"], self.primary_location.id)
        self.assertEqual(farm_detail["irrigation_method_id"], backup_method["id"])
        self.assertEqual(farm_detail["plant_ids"], [tomato["id"]])
        self.assertEqual(farm_detail["plants"][0]["name"], "Tomato")
        self.assertEqual(
            farm_detail["sensor_payload"]["leaf-sensor"]["leaf_temperature"],
            21.3,
        )
        self.assertCountEqual(
            [item["code"] for item in farm_detail["sensor_schema"]["leaf-sensor"]],
            ["leaf_temperature", "leaf_wetness"],
        )
