from rest_framework import serializers

from apps.catalogos.models import (
    Bascula,
    Bodega,
    Campo,
    Castigo,
    Contrato,
    ContratoVariedad,
    Linea,
    Oficina,
    Parametro,
    ProductorOficina,
    Productor,
    Variedad,
    Zafra,
)
from apps.usuarios.permissions import is_admin_user
from utils.office import extract_office_id


class CommaDecimalField(serializers.DecimalField):
    def to_internal_value(self, data):
        if data is None or data == "":
            if self.allow_null:
                return None
            return super().to_internal_value(data)

        if isinstance(data, str):
            data = data.replace(",", "").strip()
        return super().to_internal_value(data)

    def to_representation(self, value):
        if value is None:
            return None
        # Format with thousands separator
        return "{:,.{prec}f}".format(value, prec=self.decimal_places)


class BodegaObjectField(serializers.PrimaryKeyRelatedField):
    def use_pk_only_optimization(self):
        return False

    def to_internal_value(self, data):
        if isinstance(data, dict):
            data = data.get("id")
        return super().to_internal_value(data)

    def to_representation(self, value):
        if value is None:
            return None
        nombre = getattr(value, "nombre", None)
        if nombre is None and getattr(value, "pk", None) is not None:
            try:
                nombre = Bodega.objects.only("id", "nombre").get(pk=value.pk).nombre
            except Bodega.DoesNotExist:
                nombre = None
        return {"id": value.pk, "nombre": nombre}


class ZafraSerializer(serializers.ModelSerializer):
    class Meta:
        model = Zafra
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Zafra()
        for k, v in attrs.items():
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "La zafra no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        instance.clean()
        return attrs


class OficinaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Oficina
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by")

        extra_kwargs = {
            "sap_contrasena": {"write_only": True},
        }

    def to_representation(self, instance):
        data = super().to_representation(instance)

        request = self.context.get("request")
        user = getattr(request, "user", None) if request is not None else None

        if not user or not getattr(user, "is_authenticated", False) or not is_admin_user(user):
            data.pop("sap_url", None)
            data.pop("sap_companydb", None)
            data.pop("sap_usuario", None)
            data.pop("sap_contrasena", None)

        return data


class VariedadSerializer(serializers.ModelSerializer):
    class Meta:
        model = Variedad
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Variedad()
        for k, v in attrs.items():
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "La variedad no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        return attrs


class CampoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Campo
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Campo()
        for k, v in attrs.items():
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "El campo no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        return attrs


class LineaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Linea
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Linea()
        for k, v in attrs.items():
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "La línea no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        return attrs


class ProductorSerializer(serializers.ModelSerializer):
    class ProductorOficinaSerializer(serializers.ModelSerializer):
        nombre = serializers.CharField(source="oficina.nombre", read_only=True)

        class Meta:
            model = ProductorOficina
            fields = ("oficina", "nombre", "codigo_sap")

    oficinas = ProductorOficinaSerializer(source="productor_oficinas", many=True, required=False)

    class Meta:
        model = Productor
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by")
        extra_kwargs = {"codigo_sica": {"validators": []}}

    def validate(self, attrs):
        if "codigo_sica" in attrs and not attrs["codigo_sica"]:
            attrs["codigo_sica"] = None

        codigo_sica = attrs.get("codigo_sica")
        if codigo_sica:
            qs = Productor.objects.filter(codigo_sica=codigo_sica)
            if self.instance:
                qs = qs.exclude(pk=self.instance.pk)
            if qs.exists():
                raise serializers.ValidationError(
                    {"codigo_sica": "Ya existe un productor con este código SICA."}
                )
        return attrs

    def _sync_oficinas(self, productor, productor_oficinas_data):
        request = self.context.get("request")
        user = getattr(request, "user", None) if request is not None else None

        ProductorOficina.objects.filter(productor=productor).delete()

        for item in productor_oficinas_data:
            ProductorOficina.objects.create(
                productor=productor,
                oficina=item["oficina"],
                codigo_sap=item.get("codigo_sap", "") or "",
                created_by=user,
                updated_by=user,
            )

    def create(self, validated_data):
        productor_oficinas_data = validated_data.pop("productor_oficinas", None)
        productor = super().create(validated_data)
        if productor_oficinas_data is not None:
            self._sync_oficinas(productor, productor_oficinas_data)
        return productor

    def update(self, instance, validated_data):
        productor_oficinas_data = validated_data.pop("productor_oficinas", None)
        productor = super().update(instance, validated_data)
        if productor_oficinas_data is not None:
            self._sync_oficinas(productor, productor_oficinas_data)
        return productor


class NombreObjectField(serializers.PrimaryKeyRelatedField):
    def use_pk_only_optimization(self):
        return False

    def to_internal_value(self, data):
        if isinstance(data, dict):
            data = data.get("id")
        return super().to_internal_value(data)

    def to_representation(self, value):
        if value is None:
            return None
        return {"id": value.pk, "nombre": getattr(value, "nombre", None)}


class ContratoVariedadSerializer(serializers.ModelSerializer):
    variedad = NombreObjectField(queryset=Variedad.objects.all())
    peso_teorico = CommaDecimalField(max_digits=12, decimal_places=3, required=False, allow_null=True)

    class Meta:
        model = ContratoVariedad
        fields = ["id", "variedad", "peso_teorico", "created_at", "updated_at"]
        read_only_fields = ["id", "created_at", "updated_at"]


class ContratoSerializer(serializers.ModelSerializer):
    productor = NombreObjectField(queryset=Productor.objects.all())
    zafra = NombreObjectField(queryset=Zafra.objects.all())
    variedades = ContratoVariedadSerializer(many=True)

    class Meta:
        model = Contrato
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Contrato()
        for k, v in attrs.items():
            if k == "variedades":
                continue
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "El contrato no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        variedades_data = attrs.get("variedades", [])
        if not variedades_data:
            raise serializers.ValidationError({"variedades": "Debe incluir al menos una variedad."})

        variedad_ids = set()
        for item in variedades_data:
            variedad = item.get("variedad")
            if variedad is None:
                continue
            if variedad.id in variedad_ids:
                raise serializers.ValidationError({"variedades": "Las variedades deben ser únicas por contrato."})
            variedad_ids.add(variedad.id)
            if (
                office_id is not None
                and variedad.oficina_id is not None
                and variedad.oficina_id != office_id
            ):
                raise serializers.ValidationError(
                    {"variedades": f"La variedad {variedad.nombre} no pertenece a la oficina indicada."}
                )

        instance.clean()
        return attrs

    def _sync_variedades(self, contrato, variedades_data):
        request = self.context.get("request")
        user = getattr(request, "user", None) if request is not None else None

        ContratoVariedad.objects.filter(contrato=contrato).delete()
        for item in variedades_data:
            ContratoVariedad.objects.create(
                contrato=contrato,
                variedad=item["variedad"],
                peso_teorico=item.get("peso_teorico"),
                created_by=user,
                updated_by=user,
            )

    def create(self, validated_data):
        variedades_data = validated_data.pop("variedades", [])
        contrato = super().create(validated_data)
        self._sync_variedades(contrato, variedades_data)
        return contrato

    def update(self, instance, validated_data):
        variedades_data = validated_data.pop("variedades", [])
        contrato = super().update(instance, validated_data)
        self._sync_variedades(contrato, variedades_data)
        return contrato


class BodegaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bodega
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Bodega()
        for k, v in attrs.items():
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "La bodega no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        return attrs


class BasculaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bascula
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by", "oficina")

    def validate(self, attrs):
        instance = self.instance or Bascula()
        for k, v in attrs.items():
            setattr(instance, k, v)

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        if office_id is not None:
            if instance.oficina_id is not None and instance.oficina_id != office_id:
                raise serializers.ValidationError({"oficina": "La báscula no pertenece a la oficina indicada."})
            instance.oficina_id = office_id

        return attrs


class ParametroSerializer(serializers.ModelSerializer):
    castigos = serializers.SerializerMethodField()

    class Meta:
        model = Parametro
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by")

    def get_castigos(self, obj):
        if self.context.get("include_castigos"):
            return CastigoSerializer(obj.castigos.all(), many=True).data
        return None

    def validate(self, attrs):
        instance = self.instance or Parametro()
        for k, v in attrs.items():
            setattr(instance, k, v)
        instance.clean()
        return attrs


class CastigoSerializer(serializers.ModelSerializer):
    parametro = NombreObjectField(queryset=Parametro.objects.all())

    class Meta:
        model = Castigo
        fields = "__all__"
        read_only_fields = ("created_at", "updated_at", "created_by", "updated_by")

    def validate(self, attrs):
        instance = self.instance or Castigo()
        for k, v in attrs.items():
            setattr(instance, k, v)
        instance.clean()
        return attrs
