from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone

from apps.catalogos.models import Campo, Contrato, ContratoVariedad, Linea, Oficina, Productor, Variedad, Zafra
from utils.audit import AuditModel


class FolioSecuencia(AuditModel):
    productor = models.ForeignKey(
        Productor, on_delete=models.PROTECT, related_name="folio_secuencias"
    )
    zafra = models.ForeignKey(
        Zafra, on_delete=models.PROTECT, related_name="folio_secuencias"
    )
    ultimo_folio = models.PositiveIntegerField(default=0)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["productor", "zafra"],
                name="uq_folio_secuencia_productor_zafra",
            ),
        ]

    def __str__(self):
        return f"{self.productor_id}-{self.zafra_id}: {self.ultimo_folio}"


class Boleta(AuditModel):
    ESTADO_GENERADA = "generada"
    ESTADO_IMPRESA = "impresa"
    ESTADO_ENVIADA = "enviada"
    ESTADO_ASIGNADA = "asignada"
    ESTADO_RECIBIDA = "recibida"
    ESTADO_CERRADA = "cerrada"
    ESTADO_RECHAZADA = "rechazada"

    ESTADO_CHOICES = (
        (ESTADO_GENERADA, "Generada"),
        (ESTADO_IMPRESA, "Impresa"),
        (ESTADO_ENVIADA, "Enviada"),
        (ESTADO_ASIGNADA, "Asignada"),
        (ESTADO_RECIBIDA, "Recibida"),
        (ESTADO_CERRADA, "Cerrada"),
        (ESTADO_RECHAZADA, "Rechazada"),
    )

    zafra = models.ForeignKey(Zafra, on_delete=models.PROTECT, related_name="boletas")
    oficina = models.ForeignKey(Oficina, on_delete=models.PROTECT, related_name="boletas", null=True, blank=True)
    productor = models.ForeignKey(Productor, on_delete=models.PROTECT, related_name="boletas")
    variedad = models.ForeignKey(Variedad, on_delete=models.PROTECT, related_name="boletas", null=True, blank=True)
    campo = models.ForeignKey(Campo, on_delete=models.PROTECT, related_name="boletas", null=True, blank=True)
    contrato = models.ForeignKey(Contrato, on_delete=models.PROTECT, related_name="boletas", null=True, blank=True)

    folio = models.CharField(max_length=60)
    fecha_emision = models.DateTimeField(default=timezone.now)

    estado = models.CharField(max_length=20, choices=ESTADO_CHOICES, default=ESTADO_GENERADA)

    qr_texto = models.TextField(blank=True)
    peso_estimado = models.DecimalField(max_digits=12, decimal_places=3, null=True, blank=True)

    origen_trigo = models.CharField(max_length=120, blank=True, default="")

    placas_camion = models.CharField(max_length=20, blank=True, default="")
    linea = models.ForeignKey(
        Linea,
        on_delete=models.PROTECT,
        related_name="boletas",
        null=True,
        blank=True,
    )
    linea_nombre = models.CharField(max_length=120, blank=True, default="")
    marca_unidad = models.CharField(max_length=120, blank=True, default="")
    modelo_unidad = models.CharField(max_length=120, blank=True, default="")
    chofer_nombre = models.CharField(max_length=100, blank=True, default="")
    chofer_telefono = models.CharField(max_length=20, blank=True, default="")
    chofer_firma_en_dispositivo = models.BooleanField(default=False)

    enviada_email = models.BooleanField(default=False)
    pdf_generado = models.FileField(upload_to="boletas_pdf/", null=True, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["zafra", "folio"], name="uq_boleta_folio_por_zafra"),
        ]
        permissions = [
            ("create", "Puede crear boletas"),
            ("generate", "Puede generar boletas"),
            ("edit", "Puede editar boletas"),
            ("delete", "Puede eliminar boletas"),
        ]

    def save(self, *args, **kwargs):
        if not self.variedad_id and self.contrato_id:
            variedades = list(
                ContratoVariedad.objects.filter(contrato_id=self.contrato_id)
                .values_list("variedad_id", flat=True)
                .order_by("id")[:2]
            )
            if len(variedades) == 1:
                self.variedad_id = variedades[0]
        if self.linea_id:
            self.linea_nombre = self.linea.nombre

        skip_log = getattr(self, "_skip_estado_log", False)
        estado_anterior = None
        if self.pk and not skip_log:
            try:
                estado_anterior = Boleta.objects.values_list("estado", flat=True).get(pk=self.pk)
            except Boleta.DoesNotExist:
                estado_anterior = None

        super().save(*args, **kwargs)

        if estado_anterior is not None and estado_anterior != self.estado:
            BoletaLog.objects.create(
                boleta=self,
                usuario=getattr(self, "updated_by", None),
                estado_anterior=estado_anterior,
                estado_nuevo=self.estado,
            )

    def clean(self):
        if self.contrato_id and self.zafra_id and self.contrato.zafra_id != self.zafra_id:
            raise ValidationError({"contrato": "El contrato no pertenece a la zafra indicada."})
        if self.contrato_id and self.productor_id and self.contrato.productor_id != self.productor_id:
            raise ValidationError({"productor": "El contrato no pertenece al productor indicado."})

        if (
            self.contrato_id
            and self.variedad_id
            and not self.contrato.variedades.filter(variedad_id=self.variedad_id).exists()
        ):
            raise ValidationError({"variedad": "La variedad no pertenece al contrato indicado."})

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

        if (
            self.oficina_id
            and self.zafra_id
            and getattr(self.zafra, "oficina_id", None) is not None
            and self.zafra.oficina_id != self.oficina_id
        ):
            raise ValidationError({"oficina": "La zafra no pertenece a la oficina indicada."})

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

    def __str__(self):
        return f"Boleta {self.folio}"


class BoletaLog(models.Model):
    ACCION_CAMBIO_ESTADO = "cambio_estado"

    ACCION_CHOICES = [
        (ACCION_CAMBIO_ESTADO, "Cambio de estado"),
    ]

    boleta = models.ForeignKey(
        Boleta,
        on_delete=models.CASCADE,
        related_name="logs",
    )
    usuario = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="boletas_logs",
    )
    accion = models.CharField(max_length=20, choices=ACCION_CHOICES, default=ACCION_CAMBIO_ESTADO)
    estado_anterior = models.CharField(max_length=20, blank=True)
    estado_nuevo = models.CharField(max_length=20)
    detalle = models.JSONField(default=dict, blank=True)
    fecha = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-id"]
