from rest_framework import serializers

from apps.boletas.models import Boleta
from apps.catalogos.models import Campo, Contrato, Linea, Productor, Variedad, Zafra
from apps.catalogos.serializers import ContratoSerializer, ZafraSerializer
from utils.office import extract_office_id


class VariedadObjectField(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 = Variedad.objects.only("id", "nombre").get(pk=value.pk).nombre
            except Variedad.DoesNotExist:
                nombre = None
        return {"id": value.pk, "nombre": nombre}


class CampoObjectField(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 = Campo.objects.only("id", "nombre").get(pk=value.pk).nombre
            except Campo.DoesNotExist:
                nombre = None
        return {"id": value.pk, "nombre": nombre}


class LineaObjectField(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 = Linea.objects.only("id", "nombre").get(pk=value.pk).nombre
            except Linea.DoesNotExist:
                nombre = None
        return {"id": value.pk, "nombre": nombre}


class ProductorObjectField(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 = Productor.objects.only("id", "nombre").get(pk=value.pk).nombre
            except Productor.DoesNotExist:
                nombre = None
        return {"id": value.pk, "nombre": nombre}


class ZafraObjectField(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 ZafraSerializer(value, context=self.context).data


class ContratoObjectField(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 ContratoSerializer(value, context=self.context).data


class BoletaSerializer(serializers.ModelSerializer):
    contrato = ContratoObjectField(queryset=Contrato.objects.all(), required=False, allow_null=True)
    zafra = ZafraObjectField(queryset=Zafra.objects.all(), required=False, allow_null=True)
    variedad = VariedadObjectField(queryset=Variedad.objects.all(), required=False, allow_null=True)
    campo = CampoObjectField(queryset=Campo.objects.all(), required=False, allow_null=True)
    linea = LineaObjectField(queryset=Linea.objects.all(), required=False, allow_null=True)
    productor = ProductorObjectField(queryset=Productor.objects.all(), required=False, allow_null=True)

    class Meta:
        model = Boleta
        fields = "__all__"
        read_only_fields = (
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
            "oficina",
            "enviada_email",
            "pdf_generado",
            "folio",
        )
        extra_kwargs = {}

    def validate(self, attrs):
        instance = self.instance or Boleta()
        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

        contrato = getattr(instance, "contrato", None)
        if contrato is not None:
            current_zafra = getattr(instance, "zafra", None)
            if current_zafra is not None and contrato.zafra_id != current_zafra.id:
                raise serializers.ValidationError({"zafra": "La zafra no coincide con la del contrato."})

            current_productor = getattr(instance, "productor", None)
            if current_productor is not None and contrato.productor_id != current_productor.id:
                raise serializers.ValidationError({"productor": "El productor no coincide con el del contrato."})

            contrato_oficina_id = getattr(contrato, "oficina_id", None)
            if office_id is not None and contrato_oficina_id is not None and contrato_oficina_id != office_id:
                raise serializers.ValidationError({"contrato": "El contrato no pertenece a la oficina indicada."})

            if current_productor is None:
                attrs["productor"] = contrato.productor
                instance.productor = contrato.productor
            if current_zafra is None:
                attrs["zafra"] = contrato.zafra
                instance.zafra = contrato.zafra

        if contrato is None:
            zafra = getattr(instance, "zafra", None)
            productor = getattr(instance, "productor", None)

            if zafra is not None and productor is not None:
                qs = Contrato.objects.filter(zafra=zafra, productor=productor, activo=True)
                if office_id is not None:
                    qs = qs.filter(oficina_id=office_id)

                contratos = list(qs.order_by("id")[:2])
                if len(contratos) == 1:
                    attrs["contrato"] = contratos[0]
                    instance.contrato = contratos[0]
                    contrato = contratos[0]
                elif len(contratos) > 1:
                    raise serializers.ValidationError(
                        {
                            "contrato": "Hay más de un contrato activo para el productor en la zafra indicada. Envía 'contrato' explícitamente o usa zafra/productor sin contrato."
                        }
                    )
        variedad = getattr(instance, "variedad", None)
        if contrato is not None:
            contrato_variedad_ids = list(
                contrato.variedades.values_list("variedad_id", flat=True).order_by("id")
            )
            if variedad is None and len(contrato_variedad_ids) == 1:
                variedad = Variedad.objects.get(pk=contrato_variedad_ids[0])
                attrs["variedad"] = variedad
                instance.variedad = variedad

            if variedad is not None and variedad.id not in contrato_variedad_ids:
                raise serializers.ValidationError(
                    {"variedad": "La variedad no pertenece al contrato indicado."}
                )

        if variedad is not None:
            oficina_id = getattr(instance, "oficina_id", None) or office_id
            variedad_oficina_id = getattr(variedad, "oficina_id", None)
            if oficina_id is not None and variedad_oficina_id is not None and variedad_oficina_id != oficina_id:
                raise serializers.ValidationError({"variedad": "La variedad no pertenece a la oficina indicada."})

        campo = getattr(instance, "campo", None)
        if campo is not None:
            oficina_id = getattr(instance, "oficina_id", None) or office_id
            campo_oficina_id = getattr(campo, "oficina_id", None)
            if oficina_id is not None and campo_oficina_id is not None and campo_oficina_id != oficina_id:
                raise serializers.ValidationError({"campo": "El campo no pertenece a la oficina indicada."})

        linea = getattr(instance, "linea", None)
        if linea is not None:
            oficina_id = getattr(instance, "oficina_id", None) or office_id
            linea_oficina_id = getattr(linea, "oficina_id", None)
            if oficina_id is not None and linea_oficina_id is not None and linea_oficina_id != oficina_id:
                raise serializers.ValidationError({"linea": "La línea no pertenece a la oficina indicada."})

        return attrs


class GenerarBoletasRequestSerializer(serializers.Serializer):
    zafra_id = serializers.IntegerField()
    productor_ids = serializers.ListField(
        child=serializers.IntegerField(), required=False, allow_empty=False
    )
    contrato_ids = serializers.ListField(
        child=serializers.IntegerField(), required=False, allow_empty=False
    )
    linea_id = serializers.IntegerField(required=False, allow_null=True)

    cantidad_boletas_por_productor = serializers.IntegerField(min_value=1, max_value=500, default=1)

    generar_qr = serializers.BooleanField(default=True)
    enviar_email = serializers.BooleanField(default=False)
    imprimir_pdf = serializers.BooleanField(default=False)

    def validate_linea_id(self, v):
        if v in (None, ""):
            return None
        try:
            linea_id = int(v)
        except (TypeError, ValueError) as e:
            raise serializers.ValidationError("linea_id inválido.") from e

        try:
            linea = Linea.objects.get(pk=linea_id)
        except Linea.DoesNotExist as e:
            raise serializers.ValidationError("Línea no encontrada.") from e

        request = self.context.get("request")
        office_id = extract_office_id(request) if request is not None else None
        linea_oficina_id = getattr(linea, "oficina_id", None)
        if office_id is not None and linea_oficina_id is not None and int(linea_oficina_id) != int(office_id):
            raise serializers.ValidationError("La línea no pertenece a la oficina indicada.")
        return linea_id

    def validate(self, attrs):
        if not attrs.get("productor_ids") and not attrs.get("contrato_ids"):
            raise serializers.ValidationError(
                "Debes enviar 'productor_ids' o 'contrato_ids' para generar boletas."
            )
        if attrs.get("productor_ids") and attrs.get("contrato_ids"):
            raise serializers.ValidationError(
                "Envía solo uno de 'productor_ids' o 'contrato_ids', no ambos."
            )
        return attrs


class ImprimirBoletasMasivoRequestSerializer(serializers.Serializer):
    boleta_ids = serializers.ListField(child=serializers.IntegerField(), allow_empty=False)
    regenerate = serializers.BooleanField(required=False, default=False)
    save = serializers.BooleanField(required=False, default=False)


class DescargarBoletasRangoSerializer(serializers.Serializer):
    fecha_inicio = serializers.DateField()
    fecha_fin = serializers.DateField()

    def validate(self, attrs):
        if attrs["fecha_inicio"] > attrs["fecha_fin"]:
            raise serializers.ValidationError(
                {"fecha_inicio": "La fecha de inicio no puede ser mayor a la fecha de fin."}
            )
        return attrs


class EstadoHistorialSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    tipo = serializers.SerializerMethodField()
    fecha = serializers.DateTimeField()
    usuario = serializers.SerializerMethodField()
    accion = serializers.CharField()
    estado_anterior = serializers.CharField(allow_blank=True)
    estado_nuevo = serializers.CharField(allow_blank=True)
    detalle = serializers.JSONField()

    def get_tipo(self, obj):
        if hasattr(obj, "boleta_id"):
            return "boleta"
        return "recepcion"

    def get_usuario(self, obj):
        user = getattr(obj, "usuario", None)
        if user:
            return {"id": user.id, "username": user.username}
        return None
