from django.conf import settings
from django.db import models
from django.utils import timezone

from apps.catalogos.models import Linea, Oficina, Variedad
from utils.audit import AuditModel


ESTADO_IDENTIFICACION = "identificacion"
ESTADO_CAMION_REGISTRADO = "camion_registrado"
ESTADO_PESO_LLENO = "peso_lleno"
ESTADO_MUESTREO_PENDIENTE = "muestreo_pendiente"
ESTADO_MUESTREO_COMPLETADO = "muestreo_completado"
ESTADO_PESO_VACIO = "peso_vacio"
ESTADO_CERRADA = "cerrada"
ESTADO_RECHAZADA = "rechazada"
ESTADO_ESCALADA = "escalada"
ESTADO_BORRADOR = "borrador"
ESTADO_VALIDADA = "validada"
ESTADO_EDITADA = "editada"
ESTADO_EN_DISPUTA = "en_disputa"
ESTADO_CERRADA_EN_DISPUTA = "cerrada_en_disputa"
ESTADO_RECHAZADA_EN_DISPUTA = "rechazada_en_disputa"

ESTADO_CHOICES = [
    (ESTADO_IDENTIFICACION, "Identificación"),
    (ESTADO_CAMION_REGISTRADO, "Camión registrado"),
    (ESTADO_PESO_LLENO, "Peso lleno registrado"),
    (ESTADO_MUESTREO_PENDIENTE, "Muestreo en curso"),
    (ESTADO_MUESTREO_COMPLETADO, "Muestreo completado"),
    (ESTADO_PESO_VACIO, "Peso vacío registrado"),
    (ESTADO_CERRADA, "Cerrada"),
    (ESTADO_RECHAZADA, "Rechazada"),
    (ESTADO_ESCALADA, "Escalada"),
    (ESTADO_BORRADOR, "Borrador"),
    (ESTADO_VALIDADA, "Validada"),
    (ESTADO_EDITADA, "Editada"),
    (ESTADO_EN_DISPUTA, "En disputa"),
    (ESTADO_CERRADA_EN_DISPUTA, "Cerrada en disputa"),
    (ESTADO_RECHAZADA_EN_DISPUTA, "Rechazada en disputa"),
]


class Recepcion(AuditModel):
    ESTADO_IDENTIFICACION = ESTADO_IDENTIFICACION
    ESTADO_CAMION_REGISTRADO = ESTADO_CAMION_REGISTRADO
    ESTADO_PESO_LLENO = ESTADO_PESO_LLENO
    ESTADO_MUESTREO_PENDIENTE = ESTADO_MUESTREO_PENDIENTE
    ESTADO_MUESTREO_COMPLETADO = ESTADO_MUESTREO_COMPLETADO
    ESTADO_PESO_VACIO = ESTADO_PESO_VACIO
    ESTADO_CERRADA = ESTADO_CERRADA
    ESTADO_RECHAZADA = ESTADO_RECHAZADA
    ESTADO_ESCALADA = ESTADO_ESCALADA
    ESTADO_BORRADOR = ESTADO_BORRADOR
    ESTADO_VALIDADA = ESTADO_VALIDADA
    ESTADO_EDITADA = ESTADO_EDITADA
    ESTADO_EN_DISPUTA = ESTADO_EN_DISPUTA
    ESTADO_CERRADA_EN_DISPUTA = ESTADO_CERRADA_EN_DISPUTA
    ESTADO_RECHAZADA_EN_DISPUTA = ESTADO_RECHAZADA_EN_DISPUTA

    ESTADO_CHOICES = ESTADO_CHOICES

    boleta = models.OneToOneField(
        "boletas.Boleta",
        on_delete=models.PROTECT,
        related_name="recepcion",
    )

    flete = models.ForeignKey(
        "recepciones.Flete",
        on_delete=models.SET_NULL,
        related_name="recepciones",
        null=True,
        blank=True,
    )

    peso_neto_asignado = models.DecimalField(
        max_digits=12,
        decimal_places=3,
        null=True,
        blank=True,
        help_text="Peso neto asignado a esta boleta dentro de un flete compartido.",
    )

    bodega_destino = models.ForeignKey(
        "catalogos.Bodega",
        on_delete=models.PROTECT,
        related_name="recepciones_destino",
        null=True,
        blank=True,
    )

    oficina = models.ForeignKey(
        "catalogos.Oficina",
        on_delete=models.PROTECT,
        related_name="recepciones",
        null=True,
        blank=True,
    )

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

    observaciones = models.TextField(blank=True)
    fecha_cierre = models.DateTimeField(null=True, blank=True)
    fecha_validacion = models.DateTimeField(null=True, blank=True)
    fecha_edicion = models.DateTimeField(null=True, blank=True)

    firmas = models.JSONField(default=dict, blank=True)
    alerta = models.JSONField(default=dict, blank=True)

    rechazo_calidad = models.BooleanField(default=False)
    rechazos_calidad = models.JSONField(default=list, blank=True)
    calidad_calculada = models.BooleanField(default=False)

    class Meta:
        permissions = [
            ("create", "Puede crear recepciones"),
            ("edit", "Puede editar recepciones"),
            ("delete", "Puede eliminar recepciones"),
            ("process", "Puede procesar recepciones"),
            ("close", "Puede cerrar recepciones"),
            ("validate", "Puede validar recepciones"),
            ("reopen", "Puede reabrir recepciones validadas"),
            ("resolve_dispute", "Puede resolver disputas de recepciones"),
        ]

    def cerrar(self):
        self.estado = self.ESTADO_CERRADA
        self.fecha_cierre = timezone.now()

    def marcar_validada(self):
        self.estado = self.ESTADO_VALIDADA
        self.fecha_validacion = timezone.now()

    def marcar_editada(self):
        self.estado = self.ESTADO_EDITADA
        self.fecha_edicion = timezone.now()

    def reabrir_para_edicion(self):
        self.estado = self.ESTADO_BORRADOR
        self.fecha_cierre = None

    def save(self, *args, **kwargs):
        skip_log = getattr(self, "_skip_estado_log", False)
        estado_anterior = None
        if self.pk and not skip_log:
            try:
                estado_anterior = Recepcion.objects.values_list("estado", flat=True).get(pk=self.pk)
            except Recepcion.DoesNotExist:
                estado_anterior = None

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

        if estado_anterior is not None and estado_anterior != self.estado:
            RecepcionLog.objects.create(
                recepcion=self,
                usuario=getattr(self, "updated_by", None),
                accion=RecepcionLog.ACCION_CAMBIO_ESTADO,
                estado_anterior=estado_anterior,
                estado_nuevo=self.estado,
            )


class Flete(AuditModel):
    oficina = models.ForeignKey(
        Oficina,
        on_delete=models.PROTECT,
        related_name="fletes",
        null=True,
        blank=True,
    )

    variedad = models.ForeignKey(
        Variedad,
        on_delete=models.PROTECT,
        related_name="fletes",
        null=True,
        blank=True,
        help_text="Todas las boletas del flete deben corresponder a la misma variedad.",
    )

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

    placas_camion = models.CharField(max_length=20, blank=True, default="")
    linea = models.ForeignKey(
        Linea,
        on_delete=models.PROTECT,
        related_name="fletes",
        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)
    origen_trigo = models.CharField(max_length=120, blank=True, default="")

    observaciones = models.TextField(blank=True)
    fecha_cierre = models.DateTimeField(null=True, blank=True)
    fecha_validacion = models.DateTimeField(null=True, blank=True)
    fecha_edicion = models.DateTimeField(null=True, blank=True)

    firmas = models.JSONField(default=dict, blank=True)
    alerta = models.JSONField(default=dict, blank=True)

    def cerrar(self):
        self.estado = ESTADO_CERRADA
        self.fecha_cierre = timezone.now()

    def marcar_validada(self):
        self.estado = ESTADO_VALIDADA
        self.fecha_validacion = timezone.now()

    def marcar_editada(self):
        self.estado = ESTADO_EDITADA
        self.fecha_edicion = timezone.now()

    def reabrir_para_edicion(self):
        self.estado = ESTADO_BORRADOR
        self.fecha_cierre = None


class PesoFlete(models.Model):
    TIPO_LLENO = "lleno"
    TIPO_VACIO = "vacio"

    TIPO_CHOICES = [
        (TIPO_LLENO, "Lleno"),
        (TIPO_VACIO, "Vacío"),
    ]

    flete = models.ForeignKey(Flete, on_delete=models.CASCADE, related_name="pesos")
    tipo = models.CharField(max_length=10, choices=TIPO_CHOICES)
    valor = models.DecimalField(max_digits=10, decimal_places=2)
    es_automatico = models.BooleanField(default=True)
    motivo_manual = models.TextField(blank=True)
    bascula = models.ForeignKey(
        "catalogos.Bascula",
        on_delete=models.SET_NULL,
        related_name="pesos_flete",
        null=True,
        blank=True,
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["flete", "tipo"], name="uq_peso_por_flete_tipo"),
        ]


class PesoRecepcion(models.Model):
    TIPO_LLENO = "lleno"
    TIPO_VACIO = "vacio"

    TIPO_CHOICES = [
        (TIPO_LLENO, "Lleno"),
        (TIPO_VACIO, "Vacío"),
    ]

    recepcion = models.ForeignKey(Recepcion, on_delete=models.CASCADE, related_name="pesos")
    tipo = models.CharField(max_length=10, choices=TIPO_CHOICES)
    valor = models.DecimalField(max_digits=10, decimal_places=2)
    es_automatico = models.BooleanField(default=True)
    motivo_manual = models.TextField(blank=True)
    bascula = models.ForeignKey(
        "catalogos.Bascula",
        on_delete=models.SET_NULL,
        related_name="pesos",
        null=True,
        blank=True,
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["recepcion", "tipo"], name="uq_peso_por_recepcion_tipo"),
        ]


def foto_upload_path(instance, filename):
    rid = instance.recepcion_id or "unknown"
    return f"recepciones/{rid}/{instance.tipo}_{filename}"


class FotoRecepcion(models.Model):
    TIPO_CHOICES = [
        ("frontal", "Frontal"),
        ("lateral", "Lateral"),
        ("cargamento", "Cargamento"),
        ("boleta_fisica", "Boleta física"),
        ("otro", "Otro"),
    ]

    recepcion = models.ForeignKey(Recepcion, on_delete=models.CASCADE, related_name="fotos")
    tipo = models.CharField(max_length=20, choices=TIPO_CHOICES)
    imagen = models.ImageField(upload_to=foto_upload_path)
    created_at = models.DateTimeField(auto_now_add=True)


class Muestreo(models.Model):
    TIPO_CHOICES = [
        ("aleatorio", "Aleatorio"),
        ("estratificado", "Estratificado"),
        ("manual", "Manual"),
    ]
    ESTADO_CHOICES = [
        ("pendiente", "Pendiente"),
        ("tomado", "Muestra tomada"),
        ("en_laboratorio", "En laboratorio"),
        ("completado", "Completado"),
    ]

    recepcion = models.OneToOneField(
        Recepcion,
        on_delete=models.CASCADE,
        related_name="muestreo",
        null=True,
        blank=True,
    )
    flete = models.OneToOneField(
        Flete,
        on_delete=models.CASCADE,
        related_name="muestreo",
        null=True,
        blank=True,
    )
    oficina = models.ForeignKey(
        "catalogos.Oficina",
        on_delete=models.PROTECT,
        related_name="muestreos",
        null=True,
        blank=True,
    )
    tipo_muestreo = models.CharField(max_length=20, choices=TIPO_CHOICES, blank=True, null=True)
    tecnico_asignado = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="recepciones_muestreos",
        limit_choices_to={"groups__name": "Técnicos"},
    )
    estado = models.CharField(max_length=20, choices=ESTADO_CHOICES, default="pendiente")
    fecha_asignacion = models.DateTimeField(auto_now_add=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        self._sincronizar_estado_flete()

    def _sincronizar_estado_flete(self):
        """
        Cuando un muestreo ligado a un flete pasa a 'completado', sincroniza
        el estado del flete y de todas sus recepciones a MUESTREO_COMPLETADO.
        """
        if self.estado != "completado":
            return
        flete_id = getattr(self, "flete_id", None)
        if not flete_id:
            return
        flete = getattr(self, "flete", None)
        if flete is None:
            return
        if flete.estado == ESTADO_MUESTREO_PENDIENTE:
            flete.estado = ESTADO_MUESTREO_COMPLETADO
            flete.save(update_fields=["estado", "updated_at"])
        flete.recepciones.filter(
            estado=ESTADO_MUESTREO_PENDIENTE
        ).update(estado=ESTADO_MUESTREO_COMPLETADO)


class Deduccion(models.Model):
    TIPO_CHOICES = [
        ("porcentaje", "Porcentaje"),
        ("fijo", "Fijo"),
    ]

    recepcion = models.ForeignKey(Recepcion, on_delete=models.CASCADE, related_name="deducciones")
    parametro = models.ForeignKey("catalogos.Parametro", on_delete=models.PROTECT)
    kg_por_tonelada = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    valor_deducido = models.DecimalField(max_digits=10, decimal_places=2)
    tipo_deduccion = models.CharField(max_length=10, choices=TIPO_CHOICES)
    created_at = models.DateTimeField(auto_now_add=True)


class Bonificacion(models.Model):
    recepcion = models.ForeignKey(Recepcion, on_delete=models.CASCADE, related_name="bonificaciones")
    parametro = models.ForeignKey("catalogos.Parametro", on_delete=models.PROTECT)
    monto_por_tonelada = models.DecimalField(max_digits=12, decimal_places=2)
    monto_total = models.DecimalField(max_digits=12, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)


class RecepcionLog(models.Model):
    ACCION_VALIDADA = "validada"
    ACCION_EDITADA = "editada"
    ACCION_REABIERTA = "reabierta"
    ACCION_CAMBIO_ESTADO = "cambio_estado"

    ACCION_CHOICES = [
        (ACCION_VALIDADA, "Validada"),
        (ACCION_EDITADA, "Editada"),
        (ACCION_REABIERTA, "Reabierta"),
        (ACCION_CAMBIO_ESTADO, "Cambio de estado"),
    ]

    recepcion = models.ForeignKey(
        Recepcion,
        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="recepciones_logs",
    )
    accion = models.CharField(max_length=20, choices=ACCION_CHOICES)
    estado_anterior = models.CharField(max_length=20, blank=True)
    estado_nuevo = models.CharField(max_length=20, blank=True)
    detalle = models.JSONField(default=dict, blank=True)
    fecha = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-id"]
