from rest_framework import serializers

from irrigation.models import IrrigationMethod
from irrigation.serializers import IrrigationMethodSerializer
from weather.models import WeatherForecast

from .models import (
    DEFAULT_SENSOR_DATA_TYPE,
    DEFAULT_SENSOR_KEY,
    FarmPlantAssignment,
    PlantCatalogSnapshot,
    SensorData,
)


class SensorDataUpdateSerializer(serializers.Serializer):
    """ورودی آپدیت داده سنسور در ساختار JSON."""

    farm_uuid = serializers.UUIDField(required=True)
    farm_boundary = serializers.JSONField(required=True)
    block_count = serializers.IntegerField(required=False, min_value=1, default=1)
    sensor_key = serializers.CharField(required=False, default=DEFAULT_SENSOR_KEY)
    sensor_payload = serializers.JSONField(required=False)
    plant_ids = serializers.ListField(
        child=serializers.IntegerField(),
        required=False,
        help_text="لیست شناسه گیاهان canonical در Backend/plants",
    )
    irrigation_method_id = serializers.IntegerField(
        required=False,
        allow_null=True,
        help_text="شناسه روش آبیاری مرتبط",
    )

    def to_internal_value(self, data):
        if not isinstance(data, dict):
            raise serializers.ValidationError("بدنه درخواست باید JSON object باشد.")

        payload = dict(data)
        known_fields = {
            "farm_uuid",
            "farm_boundary",
            "block_count",
            "sensor_key",
            "sensor_payload",
            "plant_ids",
            "irrigation_method_id",
        }
        flat_metrics = {
            key: value
            for key, value in payload.items()
            if key not in known_fields
        }

        if flat_metrics:
            sensor_key = payload.get("sensor_key", DEFAULT_SENSOR_KEY)
            nested_payload = payload.get("sensor_payload") or {}
            if nested_payload and not isinstance(nested_payload, dict):
                raise serializers.ValidationError(
                    {"sensor_payload": "sensor_payload باید object باشد."}
                )
            merged_payload = dict(nested_payload)
            current_sensor_payload = merged_payload.get(sensor_key, {})
            if current_sensor_payload and not isinstance(current_sensor_payload, dict):
                raise serializers.ValidationError(
                    {"sensor_payload": f"مقدار {sensor_key} باید object باشد."}
                )
            merged_sensor_payload = dict(current_sensor_payload)
            merged_sensor_payload.update(flat_metrics)
            merged_payload[sensor_key] = merged_sensor_payload
            payload["sensor_payload"] = merged_payload

            for key in flat_metrics:
                payload.pop(key, None)

        return super().to_internal_value(payload)

    def validate_sensor_payload(self, value):
        if not isinstance(value, dict):
            raise serializers.ValidationError("sensor_payload باید object باشد.")
        for sensor_key, sensor_values in value.items():
            if not isinstance(sensor_values, dict):
                raise serializers.ValidationError(
                    f"مقدار سنسور {sensor_key} باید object باشد."
                )
        return value

    def validate_irrigation_method_id(self, value):
        if value is None:
            return value
        if not IrrigationMethod.objects.filter(pk=value).exists():
            raise serializers.ValidationError("روش آبیاری معتبر نیست.")
        return value

    def validate(self, attrs):
        if (
            "sensor_payload" not in attrs
            and "plant_ids" not in attrs
            and "irrigation_method_id" not in attrs
        ):
            raise serializers.ValidationError(
                "حداقل یکی از sensor_payload یا plant_ids یا irrigation_method_id باید ارسال شود."
            )
        return attrs


class SensorDataResponseSerializer(serializers.ModelSerializer):
    """سریالایزر خروجی برای SensorData."""

    plant_ids = serializers.SerializerMethodField()
    irrigation_method_id = serializers.IntegerField(
        source="irrigation_method.id",
        read_only=True,
        allow_null=True,
    )

    def get_plant_ids(self, obj):
        return [plant.backend_plant_id for plant in obj.plant_snapshots]

    class Meta:
        model = SensorData
        fields = [
            "farm_uuid",
            "center_location_id",
            "weather_forecast_id",
            "sensor_payload",
            "plant_ids",
            "irrigation_method_id",
            "created_at",
            "updated_at",
        ]


class SensorParameterSerializer(serializers.Serializer):
    """سریالایزر ورودی برای تعریف پارامترهای سنسورهای مختلف."""

    sensor_key = serializers.CharField(max_length=64, required=False, default=DEFAULT_SENSOR_KEY)
    code = serializers.CharField(max_length=64)
    name_fa = serializers.CharField(max_length=128)
    unit = serializers.CharField(max_length=32, required=False, allow_blank=True)
    data_type = serializers.CharField(
        max_length=32,
        required=False,
        default=DEFAULT_SENSOR_DATA_TYPE,
    )
    metadata = serializers.JSONField(required=False, default=dict)


class FarmCenterLocationSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    lat = serializers.DecimalField(max_digits=9, decimal_places=6)
    lon = serializers.DecimalField(max_digits=9, decimal_places=6)
    farm_boundary = serializers.JSONField()
    input_block_count = serializers.IntegerField()
    block_layout = serializers.JSONField()


class WeatherForecastDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = WeatherForecast
        fields = [
            "id",
            "forecast_date",
            "temperature_min",
            "temperature_max",
            "temperature_mean",
            "precipitation",
            "precipitation_probability",
            "humidity_mean",
            "wind_speed_max",
            "et0",
            "weather_code",
        ]


class FarmSoilPayloadSerializer(serializers.Serializer):
    resolved_metrics = serializers.JSONField()
    metric_sources = serializers.JSONField()
    satellite_snapshots = serializers.JSONField()


class PlantCatalogSnapshotSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(source="backend_plant_id", read_only=True)

    class Meta:
        model = PlantCatalogSnapshot
        fields = [
            "id",
            "backend_plant_id",
            "name",
            "slug",
            "icon",
            "description",
            "metadata",
            "light",
            "watering",
            "soil",
            "temperature",
            "growth_stage",
            "growth_stages",
            "planting_season",
            "harvest_time",
            "spacing",
            "fertilizer",
            "health_profile",
            "irrigation_profile",
            "growth_profile",
            "is_active",
            "source_updated_at",
            "updated_at",
        ]


class FarmPlantAssignmentSerializer(serializers.ModelSerializer):
    plant_id = serializers.IntegerField(source="plant.backend_plant_id", read_only=True)
    plant = PlantCatalogSnapshotSerializer(read_only=True)

    class Meta:
        model = FarmPlantAssignment
        fields = [
            "plant_id",
            "position",
            "stage",
            "metadata",
            "assigned_at",
            "updated_at",
            "plant",
        ]


class FarmDetailSerializer(serializers.Serializer):
    center_location = FarmCenterLocationSerializer()
    weather = WeatherForecastDetailSerializer(allow_null=True)
    sensor_payload = serializers.JSONField()
    sensor_schema = serializers.JSONField()
    soil = FarmSoilPayloadSerializer()
    plant_ids = serializers.ListField(child=serializers.IntegerField())
    plants = PlantCatalogSnapshotSerializer(many=True)
    plant_assignments = FarmPlantAssignmentSerializer(many=True)
    irrigation_method_id = serializers.IntegerField(allow_null=True)
    irrigation_method = IrrigationMethodSerializer(allow_null=True)
    created_at = serializers.DateTimeField()
    updated_at = serializers.DateTimeField()
