from decimal import Decimal

from django.db import transaction
from django.urls import reverse
from rest_framework import serializers

from apps.boletas.models import Boleta
from apps.catalogos.models import Bodega, Variedad
from apps.recepciones.models import Recepcion
from apps.traspasos.models import AjusteTraspaso, FotoTraspaso, ItemTraspaso, Traspaso, TraspasoLog


class BodegaLiteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bodega
        fields = ["id", "codigo", "nombre"]


class VariedadLiteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Variedad
        fields = ["id", "nombre"]


class ItemTraspasoSerializer(serializers.ModelSerializer):
    variedad = VariedadLiteSerializer(read_only=True)

    class Meta:
        model = ItemTraspaso
        fields = [
            "id",
            "variedad",
            "boleta_referencia",
            "kilos_programados",
            "kilos_reales",
            "created_at",
        ]
        read_only_fields = ["id", "created_at"]


class FotoTraspasoSerializer(serializers.ModelSerializer):
    imagen_url = serializers.SerializerMethodField()

    class Meta:
        model = FotoTraspaso
        fields = ["id", "tipo", "imagen_url", "created_at"]
        read_only_fields = ["id", "created_at"]

    def get_imagen_url(self, obj):
        request = self.context.get("request")
        url = reverse("traspaso-foto-download", kwargs={"pk": obj.pk})
        if request:
            return request.build_absolute_uri(url)
        return url


class TraspasoLogSerializer(serializers.ModelSerializer):
    class Meta:
        model = TraspasoLog
        fields = ["id", "usuario", "accion", "detalle", "fecha"]
        read_only_fields = ["id", "fecha"]


class AjusteTraspasoSerializer(serializers.ModelSerializer):
    class Meta:
        model = AjusteTraspaso
        fields = ["id", "kilos_ajustados", "motivo", "aprobado_por", "created_at"]
        read_only_fields = ["id", "aprobado_por", "created_at"]


class TraspasoSerializer(serializers.ModelSerializer):
    origen = BodegaLiteSerializer(read_only=True)
    destino = BodegaLiteSerializer(read_only=True)
    items = ItemTraspasoSerializer(many=True, read_only=True)
    fotos = FotoTraspasoSerializer(many=True, read_only=True)
    logs = TraspasoLogSerializer(many=True, read_only=True)
    ajuste = AjusteTraspasoSerializer(read_only=True)

    class Meta:
        model = Traspaso
        fields = [
            "id",
            "folio",
            "origen",
            "destino",
            "fecha_programada",
            "estado",
            "peso_salida",
            "peso_llegada",
            "diferencia",
            "firma_conductor_salida",
            "firma_conductor_llegada",
            "creado_por",
            "actualizado_por",
            "created_at",
            "updated_at",
            "items",
            "fotos",
            "logs",
            "ajuste",
        ]
        read_only_fields = [
            "id",
            "folio",
            "estado",
            "diferencia",
            "creado_por",
            "actualizado_por",
            "created_at",
            "updated_at",
        ]


class ItemTraspasoCreateSerializer(serializers.Serializer):
    variedad_id = serializers.IntegerField()
    boleta_referencia_id = serializers.IntegerField(required=False, allow_null=True)
    kilos_programados = serializers.DecimalField(max_digits=10, decimal_places=2)

    def validate_kilos_programados(self, v):
        if v is None or v <= 0:
            raise serializers.ValidationError("kilos_programados debe ser > 0")
        return v


class TraspasoCreateSerializer(serializers.Serializer):
    origen_id = serializers.IntegerField()
    destino_id = serializers.IntegerField()
    fecha_programada = serializers.DateTimeField()
    items = ItemTraspasoCreateSerializer(many=True)

    def validate(self, attrs):
        if attrs.get("origen_id") == attrs.get("destino_id"):
            raise serializers.ValidationError({"destino_id": "origen y destino no pueden ser iguales."})
        items = attrs.get("items") or []
        if not items:
            raise serializers.ValidationError({"items": "Debe incluir al menos un item."})
        return attrs

    def create(self, validated_data):
        origen_id = validated_data["origen_id"]
        destino_id = validated_data["destino_id"]
        fecha_programada = validated_data["fecha_programada"]
        items_data = validated_data["items"]

        try:
            origen = Bodega.objects.get(pk=origen_id)
        except Bodega.DoesNotExist as e:
            raise serializers.ValidationError({"origen_id": "Bodega origen no encontrada."}) from e

        try:
            destino = Bodega.objects.get(pk=destino_id)
        except Bodega.DoesNotExist as e:
            raise serializers.ValidationError({"destino_id": "Bodega destino no encontrada."}) from e

        variedad_ids = {int(it["variedad_id"]) for it in items_data}
        existentes = set(Variedad.objects.filter(id__in=variedad_ids).values_list("id", flat=True))
        faltantes = sorted(list(variedad_ids - existentes))
        if faltantes:
            raise serializers.ValidationError({"items": {"variedad_id": f"Variedad no encontrada: {faltantes}."}})

        for it in items_data:
            boleta_id = it.get("boleta_referencia_id")
            if boleta_id:
                try:
                    boleta = Boleta.objects.select_related("recepcion", "recepcion__bodega_destino").get(pk=boleta_id)
                except Boleta.DoesNotExist as e:
                    raise serializers.ValidationError({"boleta_referencia_id": "Boleta no encontrada."}) from e

                if getattr(boleta, "estado", None) not in {Boleta.ESTADO_RECIBIDA, Boleta.ESTADO_CERRADA}:
                    raise serializers.ValidationError({"boleta_referencia_id": "La boleta debe estar recibida."})

                recepcion = getattr(boleta, "recepcion", None)
                if not recepcion or getattr(recepcion, "estado", None) != Recepcion.ESTADO_CERRADA:
                    raise serializers.ValidationError(
                        {"boleta_referencia_id": "La boleta debe tener recepción cerrada."}
                    )

                if getattr(recepcion, "bodega_destino_id", None) is None:
                    raise serializers.ValidationError(
                        {"boleta_referencia_id": "La recepción no tiene bodega destino asignada."}
                    )
                if int(recepcion.bodega_destino_id) != int(origen_id):
                    raise serializers.ValidationError(
                        {"boleta_referencia_id": "La boleta no pertenece a la bodega origen indicada."}
                    )

        user = self.context.get("user")
        with transaction.atomic():
            traspaso = Traspaso.objects.create(
                origen=origen,
                destino=destino,
                fecha_programada=fecha_programada,
                estado=Traspaso.ESTADO_PENDIENTE_SALIDA,
                creado_por=user,
                actualizado_por=user,
            )

            for it in items_data:
                ItemTraspaso.objects.create(
                    traspaso=traspaso,
                    variedad_id=it["variedad_id"],
                    boleta_referencia_id=it.get("boleta_referencia_id"),
                    kilos_programados=Decimal(str(it["kilos_programados"])),
                )

        return traspaso


class RegistrarSalidaSerializer(serializers.Serializer):
    peso_salida = serializers.DecimalField(max_digits=10, decimal_places=2)
    es_automatico = serializers.BooleanField(required=False, default=True)
    motivo_manual = serializers.CharField(required=False, allow_blank=True, default="")
    firma_conductor = serializers.CharField(required=False, allow_blank=True, default="")

    def validate(self, attrs):
        if attrs.get("peso_salida") is None or attrs["peso_salida"] <= 0:
            raise serializers.ValidationError({"peso_salida": "peso_salida debe ser > 0."})
        if not attrs.get("es_automatico") and not attrs.get("motivo_manual"):
            raise serializers.ValidationError(
                {"motivo_manual": "motivo_manual es requerido cuando es_automatico=false."}
            )
        return attrs


class RegistrarSalidaRequestSerializer(RegistrarSalidaSerializer):
    fotos = serializers.ListField(child=serializers.ImageField(), required=False, allow_empty=True)


class RegistrarLlegadaSerializer(serializers.Serializer):
    peso_llegada = serializers.DecimalField(max_digits=10, decimal_places=2)
    firma_conductor = serializers.CharField(required=False, allow_blank=True, default="")

    def validate(self, attrs):
        if attrs.get("peso_llegada") is None or attrs["peso_llegada"] <= 0:
            raise serializers.ValidationError({"peso_llegada": "peso_llegada debe ser > 0."})
        return attrs


class RegistrarLlegadaRequestSerializer(RegistrarLlegadaSerializer):
    fotos = serializers.ListField(child=serializers.ImageField(), required=False, allow_empty=True)


class AjustarTraspasoSerializer(serializers.Serializer):
    kilos_ajustados = serializers.DecimalField(max_digits=10, decimal_places=2)
    motivo = serializers.CharField()

    def validate(self, attrs):
        if attrs.get("kilos_ajustados") is None or attrs["kilos_ajustados"] <= 0:
            raise serializers.ValidationError({"kilos_ajustados": "kilos_ajustados debe ser > 0."})
        motivo = str(attrs.get("motivo", "")).strip()
        if not motivo:
            raise serializers.ValidationError({"motivo": "motivo es requerido."})
        attrs["motivo"] = motivo
        return attrs


class DisputaTraspasoSerializer(serializers.Serializer):
    motivo = serializers.CharField()

    def validate(self, attrs):
        motivo = str(attrs.get("motivo", "")).strip()
        if not motivo:
            raise serializers.ValidationError({"motivo": "motivo es requerido."})
        attrs["motivo"] = motivo
        return attrs
