from rest_framework import serializers

from .models import (
    AnalysisGridObservation,
    BlockSubdivision,
    RemoteSensingClusterBlock,
    RemoteSensingRun,
    RemoteSensingClusterAssignment,
    RemoteSensingSubdivisionResult,
    RemoteSensingSubdivisionOption,
    RemoteSensingSubdivisionOptionBlock,
    SoilLocation,
)
from .satellite_snapshot import build_location_block_satellite_snapshots


class SoilDataRequestSerializer(serializers.Serializer):
    """ورودی ثبت مزرعه و بلوک‌های تعریف‌شده توسط کشاورز."""

    class BlockInputSerializer(serializers.Serializer):
        block_code = serializers.CharField(max_length=64)
        boundary = serializers.JSONField()
        order = serializers.IntegerField(required=False, min_value=1)

    lon = serializers.DecimalField(max_digits=9, decimal_places=6, required=True)
    lat = serializers.DecimalField(max_digits=9, decimal_places=6, required=True)
    block_count = serializers.IntegerField(required=False, min_value=1, default=1)
    block_code = serializers.CharField(required=False, default="block-1", max_length=64)
    farm_boundary = serializers.JSONField(required=False)
    blocks = BlockInputSerializer(many=True, required=False)

    def validate(self, attrs):
        blocks = attrs.get("blocks") or []
        if self.context.get("require_farm_boundary") and not attrs.get("farm_boundary"):
            raise serializers.ValidationError(
                {"farm_boundary": ["مختصات گوشه‌های کل زمین باید ارسال شود."]}
            )
        if self.context.get("require_farm_boundary") and not blocks:
            raise serializers.ValidationError(
                {"blocks": ["مختصات بلوک‌های تعریف‌شده توسط کشاورز باید ارسال شود."]}
            )
        if blocks:
            attrs["block_count"] = len(blocks)
        return attrs


class SoilLocationResponseSerializer(serializers.ModelSerializer):
    """سریالایزر خروجی برای SoilLocation همراه با خلاصه سنجش‌ازدور."""

    lon = serializers.DecimalField(
        source="longitude",
        max_digits=9,
        decimal_places=6,
        read_only=True,
    )
    lat = serializers.DecimalField(
        source="latitude",
        max_digits=9,
        decimal_places=6,
        read_only=True,
    )
    input_block_count = serializers.IntegerField(read_only=True)
    farm_boundary = serializers.JSONField(read_only=True)
    block_layout = serializers.JSONField(read_only=True)
    block_subdivisions = serializers.SerializerMethodField()
    satellite_snapshots = serializers.SerializerMethodField()

    class Meta:
        model = SoilLocation
        fields = [
            "id",
            "lon",
            "lat",
            "input_block_count",
            "farm_boundary",
            "block_layout",
            "block_subdivisions",
            "satellite_snapshots",
        ]

    def get_block_subdivisions(self, obj):
        subdivisions = obj.block_subdivisions.all().order_by("block_code", "id")
        return BlockSubdivisionSerializer(subdivisions, many=True).data

    def get_satellite_snapshots(self, obj):
        return build_location_block_satellite_snapshots(obj)


class BlockSubdivisionSerializer(serializers.ModelSerializer):
    elbow_plot = serializers.ImageField(read_only=True)

    class Meta:
        model = BlockSubdivision
        fields = [
            "block_code",
            "chunk_size_sqm",
            "grid_points",
            "centroid_points",
            "grid_point_count",
            "centroid_count",
            "elbow_plot",
            "status",
            "metadata",
            "created_at",
            "updated_at",
        ]


class SoilDataTaskResponseSerializer(serializers.Serializer):
    """سریالایزر خروجی وقتی تسک در صف قرار گرفته (۲۰۲)."""

    source = serializers.CharField(default="task")
    task_id = serializers.UUIDField()
    lon = serializers.FloatField(source="longitude")
    lat = serializers.FloatField(source="latitude")
    status_url = serializers.CharField(required=False)


class NdviHealthRequestSerializer(serializers.Serializer):
    farm_uuid = serializers.UUIDField(required=True, help_text="شناسه یکتای مزرعه")


class NdviHealthDataItemSerializer(serializers.Serializer):
    title = serializers.CharField()
    value = serializers.JSONField()
    color = serializers.CharField()
    icon = serializers.CharField()


class NdviHealthResponseSerializer(serializers.Serializer):
    ndviIndex = serializers.FloatField(allow_null=True, required=False)
    mean_ndvi = serializers.FloatField(allow_null=True)
    ndvi_map = serializers.JSONField()
    vegetation_health_class = serializers.CharField(allow_null=True)
    observation_date = serializers.CharField(allow_null=True)
    satellite_source = serializers.CharField(allow_null=True)
    healthData = NdviHealthDataItemSerializer(many=True)


class RemoteSensingFarmRequestSerializer(serializers.Serializer):
    farm_uuid = serializers.UUIDField(required=True, help_text="شناسه یکتای مزرعه")
    force_refresh = serializers.BooleanField(required=False, default=False)
    page = serializers.IntegerField(required=False, min_value=1, default=1)
    page_size = serializers.IntegerField(required=False, min_value=1, max_value=200, default=100)


class ClusterCropRecommendationRequestSerializer(serializers.Serializer):
    farm_uuid = serializers.UUIDField(required=True, help_text="شناسه یکتای مزرعه")


class RemoteSensingClusterBlockLiveRequestSerializer(serializers.Serializer):
    temporal_start = serializers.DateField(required=False)
    temporal_end = serializers.DateField(required=False)
    days = serializers.IntegerField(required=False, min_value=1, max_value=90, default=30)

    def validate(self, attrs):
        temporal_start = attrs.get("temporal_start")
        temporal_end = attrs.get("temporal_end")
        if bool(temporal_start) != bool(temporal_end):
            raise serializers.ValidationError(
                "برای بازه سفارشی باید هر دو فیلد temporal_start و temporal_end ارسال شوند."
            )
        if temporal_start and temporal_end and temporal_start > temporal_end:
            raise serializers.ValidationError(
                {"temporal_start": ["temporal_start نمی‌تواند بعد از temporal_end باشد."]}
            )
        return attrs


class RemoteSensingCellObservationSerializer(serializers.ModelSerializer):
    cell_code = serializers.CharField(source="cell.cell_code", read_only=True)
    block_code = serializers.CharField(source="cell.block_code", read_only=True)
    chunk_size_sqm = serializers.IntegerField(source="cell.chunk_size_sqm", read_only=True)
    centroid_lat = serializers.DecimalField(source="cell.centroid_lat", max_digits=9, decimal_places=6, read_only=True)
    centroid_lon = serializers.DecimalField(source="cell.centroid_lon", max_digits=9, decimal_places=6, read_only=True)
    geometry = serializers.JSONField(source="cell.geometry", read_only=True)

    class Meta:
        model = AnalysisGridObservation
        fields = [
            "cell_code",
            "block_code",
            "chunk_size_sqm",
            "centroid_lat",
            "centroid_lon",
            "geometry",
            "temporal_start",
            "temporal_end",
            "ndvi",
            "ndwi",
            "soil_vv",
            "soil_vv_db",
            "metadata",
        ]


class RemoteSensingSummarySerializer(serializers.Serializer):
    cell_count = serializers.IntegerField()
    ndvi_mean = serializers.FloatField(allow_null=True)
    ndwi_mean = serializers.FloatField(allow_null=True)
    soil_vv_db_mean = serializers.FloatField(allow_null=True)


class RemoteSensingClusterBlockLiveMetricsSerializer(serializers.Serializer):
    ndvi = serializers.FloatField(allow_null=True)
    ndwi = serializers.FloatField(allow_null=True)
    soil_vv = serializers.FloatField(allow_null=True)
    soil_vv_db = serializers.FloatField(allow_null=True)


class RemoteSensingRunSerializer(serializers.ModelSerializer):
    status_label = serializers.SerializerMethodField()
    pipeline_status = serializers.SerializerMethodField()
    stage = serializers.SerializerMethodField()
    selected_features = serializers.SerializerMethodField()
    requested_cluster_count = serializers.SerializerMethodField()

    def get_status_label(self, obj):
        metadata_status = (obj.metadata or {}).get("status_label")
        return metadata_status or obj.normalized_status

    def get_pipeline_status(self, obj):
        metadata_status = (obj.metadata or {}).get("status_label")
        return metadata_status or obj.normalized_status

    def get_stage(self, obj):
        return (obj.metadata or {}).get("stage")

    def get_selected_features(self, obj):
        return (obj.metadata or {}).get("selected_features", [])

    def get_requested_cluster_count(self, obj):
        return (obj.metadata or {}).get("requested_cluster_count")

    class Meta:
        model = RemoteSensingRun
        fields = [
            "id",
            "block_code",
            "chunk_size_sqm",
            "temporal_start",
            "temporal_end",
            "status",
            "status_label",
            "pipeline_status",
            "stage",
            "selected_features",
            "requested_cluster_count",
            "metadata",
            "error_message",
            "started_at",
            "finished_at",
            "created_at",
            "updated_at",
        ]


class RemoteSensingClusterAssignmentSerializer(serializers.ModelSerializer):
    cell_code = serializers.CharField(source="cell.cell_code", read_only=True)
    centroid_lat = serializers.DecimalField(source="cell.centroid_lat", max_digits=9, decimal_places=6, read_only=True)
    centroid_lon = serializers.DecimalField(source="cell.centroid_lon", max_digits=9, decimal_places=6, read_only=True)

    class Meta:
        model = RemoteSensingClusterAssignment
        fields = [
            "cell_code",
            "cluster_label",
            "centroid_lat",
            "centroid_lon",
            "raw_feature_values",
            "scaled_feature_values",
        ]


class RemoteSensingClusterBlockSerializer(serializers.ModelSerializer):
    class Meta:
        model = RemoteSensingClusterBlock
        fields = [
            "uuid",
            "sub_block_code",
            "cluster_label",
            "chunk_size_sqm",
            "centroid_lat",
            "centroid_lon",
            "center_cell_code",
            "center_cell_lat",
            "center_cell_lon",
            "cell_count",
            "cell_codes",
            "geometry",
            "metadata",
            "created_at",
            "updated_at",
        ]


class RemoteSensingSubdivisionOptionBlockSerializer(serializers.ModelSerializer):
    class Meta:
        model = RemoteSensingSubdivisionOptionBlock
        fields = [
            "cluster_label",
            "sub_block_code",
            "chunk_size_sqm",
            "centroid_lat",
            "centroid_lon",
            "center_cell_code",
            "center_cell_lat",
            "center_cell_lon",
            "cell_count",
            "cell_codes",
            "geometry",
            "metadata",
        ]


class RemoteSensingSubdivisionOptionSerializer(serializers.ModelSerializer):
    cluster_blocks = RemoteSensingSubdivisionOptionBlockSerializer(many=True, read_only=True)

    class Meta:
        model = RemoteSensingSubdivisionOption
        fields = [
            "id",
            "requested_k",
            "effective_cluster_count",
            "is_active",
            "is_recommended",
            "selection_source",
            "metadata",
            "cluster_blocks",
            "created_at",
            "updated_at",
        ]


class RemoteSensingSubdivisionOptionActivateSerializer(serializers.Serializer):
    requested_k = serializers.IntegerField(min_value=1)


class RemoteSensingSubdivisionResultSerializer(serializers.ModelSerializer):
    assignments = serializers.SerializerMethodField()
    cluster_blocks = RemoteSensingClusterBlockSerializer(many=True, read_only=True)
    available_k_options = serializers.SerializerMethodField()

    def get_assignments(self, obj):
        assignments = self.context.get("paginated_assignments")
        if assignments is None:
            assignments = obj.assignments.all().order_by("cluster_label", "cell__cell_code")
        return RemoteSensingClusterAssignmentSerializer(assignments, many=True).data

    def get_available_k_options(self, obj):
        options = obj.options.all().order_by("requested_k")
        return RemoteSensingSubdivisionOptionSerializer(options, many=True).data

    class Meta:
        model = RemoteSensingSubdivisionResult
        fields = [
            "id",
            "block_code",
            "chunk_size_sqm",
            "temporal_start",
            "temporal_end",
            "cluster_count",
            "selected_features",
            "skipped_cell_codes",
            "metadata",
            "available_k_options",
            "cluster_blocks",
            "assignments",
            "created_at",
            "updated_at",
        ]


class RemoteSensingResponseSerializer(serializers.Serializer):
    status = serializers.CharField()
    source = serializers.CharField()
    location = SoilLocationResponseSerializer()
    block_code = serializers.CharField(allow_blank=True)
    chunk_size_sqm = serializers.IntegerField(allow_null=True)
    temporal_extent = serializers.JSONField()
    summary = RemoteSensingSummarySerializer()
    cells = RemoteSensingCellObservationSerializer(many=True)
    run = RemoteSensingRunSerializer(allow_null=True)
    subdivision_result = RemoteSensingSubdivisionResultSerializer(allow_null=True)
    pagination = serializers.JSONField(required=False)



class RemoteSensingRunStatusResponseSerializer(serializers.Serializer):
    status = serializers.CharField()
    source = serializers.CharField()
    run = RemoteSensingRunSerializer()
    task_id = serializers.UUIDField(allow_null=True, required=False)
    task = serializers.JSONField(required=False)
    location = SoilLocationResponseSerializer(required=False)
    block_code = serializers.CharField(allow_blank=True, required=False)
    chunk_size_sqm = serializers.IntegerField(allow_null=True, required=False)
    temporal_extent = serializers.JSONField(required=False)
    summary = RemoteSensingSummarySerializer(required=False)
    cells = RemoteSensingCellObservationSerializer(many=True, required=False)
    subdivision_result = RemoteSensingSubdivisionResultSerializer(allow_null=True, required=False)
    pagination = serializers.JSONField(required=False)


class RemoteSensingRunResultResponseSerializer(serializers.Serializer):
    status = serializers.CharField()
    source = serializers.CharField()
    location = SoilLocationResponseSerializer()
    block_code = serializers.CharField(allow_blank=True)
    chunk_size_sqm = serializers.IntegerField(allow_null=True)
    temporal_extent = serializers.JSONField()
    summary = RemoteSensingSummarySerializer()
    cells = RemoteSensingCellObservationSerializer(many=True)
    run = RemoteSensingRunSerializer()
    subdivision_result = RemoteSensingSubdivisionResultSerializer(allow_null=True)
    pagination = serializers.JSONField(required=False)


class RemoteSensingClusterBlockLiveResponseSerializer(serializers.Serializer):
    status = serializers.CharField()
    source = serializers.CharField()
    cluster_block = RemoteSensingClusterBlockSerializer()
    temporal_extent = serializers.JSONField()
    selected_features = serializers.ListField(
        child=serializers.CharField(),
        allow_empty=False,
    )
    summary = RemoteSensingSummarySerializer()
    metrics = RemoteSensingClusterBlockLiveMetricsSerializer()
    metadata = serializers.JSONField()


class ClusterCropRegisteredPlantSerializer(serializers.Serializer):
    plant_id = serializers.IntegerField()
    plant_name = serializers.CharField()
    position = serializers.IntegerField(allow_null=True)
    stage = serializers.CharField(allow_blank=True)


class ClusterCropCandidateSerializer(serializers.Serializer):
    plant_id = serializers.IntegerField(allow_null=True)
    plant_name = serializers.CharField()
    position = serializers.IntegerField(allow_null=True)
    stage = serializers.CharField(allow_blank=True)
    score = serializers.FloatField()
    predicted_yield = serializers.FloatField(allow_null=True)
    predicted_yield_tons = serializers.FloatField(allow_null=True)
    biomass = serializers.FloatField(allow_null=True)
    max_lai = serializers.FloatField(allow_null=True)
    simulation_engine = serializers.CharField(allow_null=True)
    simulation_model_name = serializers.CharField(allow_null=True)
    simulation_warning = serializers.CharField(allow_null=True, allow_blank=True)
    supporting_metrics = serializers.JSONField()


class ClusterCropRecommendationClusterSerializer(serializers.Serializer):
    block_code = serializers.CharField(allow_blank=True)
    cluster_uuid = serializers.CharField(allow_null=True, allow_blank=True)
    sub_block_code = serializers.CharField()
    cluster_label = serializers.IntegerField(allow_null=True)
    temporal_extent = serializers.JSONField(allow_null=True)
    cluster_block = RemoteSensingClusterBlockSerializer(allow_null=True)
    satellite_metrics = serializers.JSONField()
    sensor_metrics = serializers.JSONField()
    resolved_metrics = serializers.JSONField()
    candidate_plants = ClusterCropCandidateSerializer(many=True)
    suggested_plant = ClusterCropCandidateSerializer(allow_null=True)
    source_metadata = serializers.JSONField()


class ClusterCropRecommendationResponseSerializer(serializers.Serializer):
    farm_uuid = serializers.CharField()
    location_id = serializers.IntegerField()
    evaluated_plant_count = serializers.IntegerField()
    cluster_count = serializers.IntegerField()
    registered_plants = ClusterCropRegisteredPlantSerializer(many=True)
    clusters = ClusterCropRecommendationClusterSerializer(many=True)
    source_metadata = serializers.JSONField()


class RemoteSensingSubdivisionOptionListResponseSerializer(serializers.Serializer):
    result_id = serializers.IntegerField()
    active_requested_k = serializers.IntegerField(allow_null=True)
    recommended_requested_k = serializers.IntegerField(allow_null=True)
    options = RemoteSensingSubdivisionOptionSerializer(many=True)


class RemoteSensingSubdivisionOptionActivateResponseSerializer(serializers.Serializer):
    result_id = serializers.IntegerField()
    activated_requested_k = serializers.IntegerField()
    subdivision_result = RemoteSensingSubdivisionResultSerializer()
