from django.db import models


DEFAULT_SENSOR_KEY = "sensor-7-1"
DEFAULT_SENSOR_DATA_TYPE = "float"


class SensorPayloadMixin:
    """دسترسی سازگار به مقادیر سنسور از payload پویا."""

    sensor_payload: dict

    def _payload(self) -> dict:
        if isinstance(self.sensor_payload, dict):
            return self.sensor_payload
        return {}

    def get_sensor_block(self, sensor_key: str | None = None) -> dict:
        payload = self._payload()
        if sensor_key:
            block = payload.get(sensor_key, {})
            return block if isinstance(block, dict) else {}

        for _sensor_key, block in self.iter_sensor_blocks():
            return block
        return {}

    def iter_sensor_blocks(self):
        for sensor_key, block in self._payload().items():
            if isinstance(block, dict):
                yield sensor_key, block

    def get_metric(self, metric_name: str, sensor_key: str | None = None):
        block = self.get_sensor_block(sensor_key)
        if metric_name in block:
            return block.get(metric_name)

        for _candidate_key, candidate in self.iter_sensor_blocks():
            if metric_name in candidate:
                return candidate.get(metric_name)
        return None

    def get_sensor_keys(self) -> list[str]:
        return [sensor_key for sensor_key, _block in self.iter_sensor_blocks()]

    def get_all_metrics(self) -> dict[str, dict]:
        return {
            sensor_key: dict(block)
            for sensor_key, block in self.iter_sensor_blocks()
        }

    @property
    def soil_moisture(self):
        return self.get_metric("soil_moisture")

    @property
    def soil_temperature(self):
        return self.get_metric("soil_temperature")

    @property
    def soil_ph(self):
        return self.get_metric("soil_ph")

    @property
    def electrical_conductivity(self):
        return self.get_metric("electrical_conductivity")

    @property
    def nitrogen(self):
        return self.get_metric("nitrogen")

    @property
    def phosphorus(self):
        return self.get_metric("phosphorus")

    @property
    def potassium(self):
        return self.get_metric("potassium")


class SensorData(SensorPayloadMixin, models.Model):
    """
    داده‌های مزرعه/سنسور برای مرکز زمین.
    مقادیر سنسورها به‌صورت JSON ذخیره می‌شوند تا بتوان چند نوع سنسور
    و پارامترهای دلخواه را در یک رکورد نگه داشت.
    نمونه:
    {
        "sensor-7-1": {
            "soil_moisture": 22.4,
            "soil_temperature": 18.1
        },
        "leaf-sensor": {
            "leaf_wetness": 11
        }
    }
    """

    farm_uuid = models.UUIDField(
        primary_key=True,
        editable=False,
        help_text="شناسه یکتای farm که از API دریافت می‌شود",
    )
    center_location = models.ForeignKey(
        "location_data.SoilLocation",
        on_delete=models.CASCADE,
        related_name="farm_data",
        db_column="center_location_id",
        help_text="مرکز زمین مرتبط از جدول location_data.SoilLocation",
    )
    weather_forecast = models.ForeignKey(
        "weather.WeatherForecast",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="farm_data_entries",
        db_column="weather_forecast_id",
        help_text="رکورد آب وهوای مرتبط با مرکز زمین",
    )
    sensor_payload = models.JSONField(
        default=dict,
        blank=True,
        help_text='اطلاعات سنسورها در فرمت {"sensor-7-1": {...}}',
    )
    plants = models.ManyToManyField(
        "plant.Plant",
        blank=True,
        db_table="farm_data_sensordata_plants",
        related_name="farm_data",
        help_text="مسیر legacy برای گیاهان farm. برای خواندن canonical از plant_assignments/plant_snapshots استفاده شود.",
    )
    irrigation_method = models.ForeignKey(
        "irrigation.IrrigationMethod",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="farm_data",
        db_column="irrigation_method_id",
        help_text="روش آبیاری انتخاب‌شده برای این farm",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "farm_data_sensordata"
        ordering = ["-updated_at"]
        verbose_name = "farm-data"
        verbose_name_plural = "farm-data"

    def __str__(self):
        return (
            f"SensorData({self.farm_uuid}, center_location={self.center_location_id}, "
            f"weather_forecast={self.weather_forecast_id})"
        )

    @property
    def location(self):
        return self.center_location

    @location.setter
    def location(self, value):
        self.center_location = value

    @property
    def location_id(self):
        return self.center_location_id

    @property
    def plant_snapshots(self):
        return [assignment.plant for assignment in self.plant_assignments.select_related("plant").order_by("position", "id")]


class Device(models.Model):
    """نسخه نرمال‌شده هر سنسور داخل farm_data_sensordata."""

    farm = models.ForeignKey(
        SensorData,
        on_delete=models.CASCADE,
        related_name="devices",
        db_column="farm_uuid",
    )
    location = models.ForeignKey(
        "location_data.SoilLocation",
        on_delete=models.CASCADE,
        related_name="devices",
        db_column="location_id",
        help_text="location مرتبط با این device",
    )
    sensor_name = models.CharField(
        max_length=64,
        db_index=True,
        help_text='نام سنسور مثل "sensor-7-1"',
    )
    payload = models.JSONField(
        default=dict,
        blank=True,
        help_text="payload همان سنسور",
    )
    cluster_uuid = models.UUIDField(
        null=True,
        blank=True,
        db_index=True,
        help_text="uuid کلاستر داخل location برای این سنسور",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "farm_data_device"
        ordering = ["sensor_name", "id"]
        constraints = [
            models.UniqueConstraint(
                fields=["farm", "sensor_name"],
                name="farm_data_unique_device_per_farm_sensor",
            )
        ]
        verbose_name = "device"
        verbose_name_plural = "devices"

    def __str__(self):
        return f"{self.farm_id}::{self.sensor_name}"


class PlantCatalogSnapshot(models.Model):
    """
    کپی خواندنی از کاتالوگ گیاه Backend برای مصرف ماژول‌های AI.
    """

    backend_plant_id = models.PositiveIntegerField(
        unique=True,
        db_index=True,
        help_text="شناسه گیاه در Backend/plants",
    )
    name = models.CharField(max_length=255, db_index=True)
    slug = models.SlugField(max_length=255, blank=True, default="")
    icon = models.CharField(max_length=255, blank=True, default="leaf")
    description = models.TextField(blank=True, default="")
    metadata = models.JSONField(default=dict, blank=True)
    light = models.CharField(max_length=255, blank=True, default="")
    watering = models.CharField(max_length=255, blank=True, default="")
    soil = models.CharField(max_length=255, blank=True, default="")
    temperature = models.CharField(max_length=255, blank=True, default="")
    growth_stage = models.CharField(max_length=255, blank=True, default="")
    growth_stages = models.JSONField(blank=True, default=list)
    planting_season = models.CharField(max_length=255, blank=True, default="")
    harvest_time = models.CharField(max_length=255, blank=True, default="")
    spacing = models.CharField(max_length=255, blank=True, default="")
    fertilizer = models.CharField(max_length=255, blank=True, default="")
    health_profile = models.JSONField(default=dict, blank=True)
    irrigation_profile = models.JSONField(default=dict, blank=True)
    growth_profile = models.JSONField(default=dict, blank=True)
    is_active = models.BooleanField(default=True)
    source_updated_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="updated_at رکورد canonical در Backend",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "farm_data_plantcatalogsnapshot"
        ordering = ["name", "backend_plant_id"]
        verbose_name = "plant catalog snapshot"
        verbose_name_plural = "plant catalog snapshots"

    def __str__(self):
        return f"{self.name} ({self.backend_plant_id})"


class FarmPlantAssignment(models.Model):
    """
    رابطه مزرعه با snapshot گیاه برای read-model هوش مصنوعی.
    """

    farm = models.ForeignKey(
        SensorData,
        on_delete=models.CASCADE,
        related_name="plant_assignments",
        db_column="farm_uuid",
    )
    plant = models.ForeignKey(
        PlantCatalogSnapshot,
        on_delete=models.CASCADE,
        related_name="farm_assignments",
    )
    position = models.PositiveIntegerField(default=0)
    stage = models.CharField(max_length=64, blank=True, default="")
    metadata = models.JSONField(default=dict, blank=True)
    assigned_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "farm_data_farmplantassignment"
        ordering = ["position", "id"]
        constraints = [
            models.UniqueConstraint(
                fields=["farm", "plant"],
                name="farm_data_unique_farm_plant_assignment",
            )
        ]
        verbose_name = "farm plant assignment"
        verbose_name_plural = "farm plant assignments"

    def __str__(self):
        return f"{self.farm_id} -> {self.plant_id}"


class SensorParameter(models.Model):
    """
    تعریف پارامترهای سنسور برای هر نوع سنسور.
    با این ساختار می‌توان برای sensor-7-1 یا هر سنسور جدید،
    پارامترهای اختصاصی تعریف کرد.
    """

    sensor_key = models.CharField(
        max_length=64,
        db_index=True,
        default=DEFAULT_SENSOR_KEY,
        help_text='کلید سنسور داخل JSON مثل "sensor-7-1" یا "leaf-sensor"',
    )
    code = models.CharField(
        max_length=64,
        db_index=True,
        help_text="کد پارامتر (مثلاً soil_moisture)",
    )
    name_fa = models.CharField(max_length=128, help_text="نام فارسی")
    unit = models.CharField(max_length=32, blank=True, help_text="واحد اندازه‌گیری")
    data_type = models.CharField(
        max_length=32,
        default=DEFAULT_SENSOR_DATA_TYPE,
        help_text="نوع داده پارامتر مثل float, int, string, bool",
    )
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text="اطلاعات تکمیلی پارامتر مثل بازه مجاز، توضیح یا تنظیمات UI",
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "farm_data_sensorparameter"
        ordering = ["sensor_key", "code"]
        constraints = [
            models.UniqueConstraint(
                fields=["sensor_key", "code"],
                name="sensor_parameter_unique_sensor_code",
            )
        ]
        verbose_name = "پارامتر سنسور"
        verbose_name_plural = "پارامترهای سنسور"

    def __str__(self):
        return f"{self.sensor_key}.{self.code} ({self.name_fa})"


class ParameterUpdateLog(models.Model):
    """
    لاگ آپدیت لیست پارامترها.
    """

    ACTION_ADDED = "added"
    ACTION_MODIFIED = "modified"
    ACTION_CHOICES = [
        (ACTION_ADDED, "اضافه شده"),
        (ACTION_MODIFIED, "ویرایش شده"),
    ]

    parameter = models.ForeignKey(
        SensorParameter,
        on_delete=models.CASCADE,
        related_name="update_logs",
    )
    action = models.CharField(max_length=16, choices=ACTION_CHOICES)
    payload = models.JSONField(
        default=dict,
        blank=True,
        help_text="خلاصه تغییرات پارامتر برای audit",
    )
    updated_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "farm_data_parameterupdatelog"
        ordering = ["-updated_at"]
        verbose_name = "لاگ آپدیت پارامتر"
        verbose_name_plural = "لاگ آپدیت پارامترها"

    def __str__(self):
        return f"{self.parameter.code} - {self.action} - {self.updated_at}"
