from django.db import models
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.utils import timezone
from decimal import Decimal
from proveedores.models import Proveedor, ProveedorReferencia
from referencias.models import Referencia
from listas_configuracion.models import FormaPago, EmpaqueCompra
from django.contrib.auth import get_user_model
from fabricacion.models import PlantaProduccion , Maquina
from django.db.models.signals import post_delete
from django.dispatch import receiver
User = get_user_model()

class OrdenCompra(models.Model):
    ESTADOS = [
        ('borrador', 'Borrador'),
        ('pendiente', 'Pendiente de aprobación'),
        ('aprobada', 'Aprobada'),
        ('rechazada', 'Rechazada'),
        ('en_camino', 'En camino'),
        ('recibida', 'Recibida'),
        ('cancelada', 'Cancelada'),
    ]

    proveedor = models.ForeignKey(
        Proveedor,
        on_delete=models.PROTECT,
        related_name='ordenes_compra'
    )
    creada_por = models.ForeignKey(
        User,
        on_delete=models.PROTECT,
        related_name='ordenes_compra_creadas'
    )
    fecha_creacion = models.DateTimeField(auto_now_add=True)
    fecha_requerida = models.DateField(verbose_name="Fecha requerida de entrega")
    forma_pago = models.ForeignKey(
        FormaPago,
        on_delete=models.PROTECT,
        verbose_name="Forma de pago"
    )
    estado = models.CharField(
        max_length=20,
        choices=ESTADOS,
        default='borrador'
    )
    observaciones = models.TextField(blank=True)
    numero_orden = models.CharField(
        max_length=20,
        unique=True,
        editable=False,
        verbose_name="Número de orden"
    )

    class Meta:
        db_table = 'compras_ordencompra'  # 👈 Usar tabla original
        verbose_name = 'Orden de Compra'
        verbose_name_plural = 'Órdenes de Compra'
        ordering = ['-fecha_creacion']
        permissions = [
            ('aprobar_ordencompra', 'Puede aprobar órdenes de compra'),
            ('recibir_ordencompra', 'Puede marcar órdenes como recibidas'),
        ]

    def __str__(self):
        return f"OC-{self.numero_orden} | {self.proveedor} ({self.estado})"

    def save(self, *args, **kwargs):
        if not self.numero_orden:
            fecha = timezone.now().strftime('%Y%m%d')
            ultima_orden = OrdenCompra.objects.filter(
                numero_orden__startswith=f"OC-{fecha}"
            ).count()
            self.numero_orden = f"OC-{fecha}-{ultima_orden + 1:04d}"

        if self.estado == 'recibida' and self._state.adding is False:
            original = OrdenCompra.objects.get(pk=self.pk)
            if original.estado != 'recibida':
                for item in self.items.all():
                    ref = item.referencia
                    cantidad_existente = ref.stock
                    costo_existente = ref.precio_costo or 0

                    cantidad_nueva = item.cantidad
                    costo_nuevo = item.precio_unitario

                    nuevo_stock = cantidad_existente + cantidad_nueva

                    if nuevo_stock > 0:
                        nuevo_costo = (
                            (cantidad_existente * costo_existente) +
                            (cantidad_nueva * costo_nuevo)
                        ) / nuevo_stock
                    else:
                        nuevo_costo = costo_nuevo

                    ref.stock = nuevo_stock
                    ref.precio_costo = nuevo_costo
                    ref.save()

        super().save(*args, **kwargs)

    @property
    def total(self):
        return sum(item.subtotal for item in self.items.all())

    def clean(self):
        if self.estado in ['borrador', 'pendiente'] and self.fecha_requerida < timezone.now().date():
            raise ValidationError("La fecha requerida no puede ser en el pasado.")

    def aprobar(self, usuario):
        if self.estado != 'pendiente':
            raise ValidationError("Solo se pueden aprobar órdenes pendientes.")
        self.estado = 'aprobada'
        self.save()

    def marcar_como_recibida(self, usuario):
        if self.estado != 'en_camino':
            raise ValidationError("Solo se pueden recibir órdenes en camino.")
        self.estado = 'recibida'
        self.save()

class ItemOrdenCompra(models.Model):
    orden = models.ForeignKey(
        OrdenCompra,
        on_delete=models.CASCADE,
        related_name='items'
    )
    referencia = models.ForeignKey(
        Referencia,
        on_delete=models.PROTECT,
        related_name='items_orden_compra'
    )
    cantidad = models.DecimalField(
        max_digits=12,
        decimal_places=4,
        validators=[MinValueValidator(0.0001)]
    )
    precio_unitario = models.DecimalField(
        max_digits=12,
        decimal_places=4,
        validators=[MinValueValidator(0)]
    )
    empaque = models.ForeignKey(
        EmpaqueCompra,
        on_delete=models.PROTECT,
        verbose_name="Empaque de compra"
    )
    proveedor_referencia = models.ForeignKey(
        ProveedorReferencia,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        verbose_name="Relación Proveedor-Referencia"
    )
    lead_time = models.PositiveIntegerField(
        verbose_name="Tiempo estimado de entrega (días)",
        null=True,
        blank=True
    )
    certificado = models.BooleanField(
        default=False,
        verbose_name="Certificado por proveedor"
    )

    class Meta:
        db_table = 'compras_itemordencompra'
        verbose_name = 'Ítem de Orden de Compra'
        verbose_name_plural = 'Ítems de Órdenes de Compra'
        constraints = [
            models.UniqueConstraint(
                fields=['orden', 'referencia'],
                name='unique_item_orden_referencia'
            )
        ]

    @property
    def subtotal(self):
        if self.cantidad is None or self.precio_unitario is None:
            return 0
        return self.cantidad * self.precio_unitario

    def save(self, *args, **kwargs):
        if self.lead_time is None and self.proveedor_referencia and self.proveedor_referencia.lead_time:
            self.lead_time = self.proveedor_referencia.lead_time
        super().save(*args, **kwargs)

    def __str__(self):
        precio = self.precio_unitario if self.precio_unitario is not None else "No definido"
        return f"{self.cantidad or 0} x {self.referencia.sku} (${precio}/u)"

    def clean(self):
        if not ProveedorReferencia.objects.filter(
            proveedor=self.orden.proveedor,
            referencia=self.referencia
        ).exists():
            raise ValidationError("Este proveedor no suministra la referencia seleccionada.")



class AjusteInventarioFisico(models.Model):
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    fecha = models.DateField(default=timezone.now)
    cantidad_antes = models.DecimalField(max_digits=12, decimal_places=4)
    cantidad_despues = models.DecimalField(max_digits=12, decimal_places=4)
    unidades_entrada = models.DecimalField(max_digits=12, decimal_places=4, default=0)
    unidades_salida = models.DecimalField(max_digits=12, decimal_places=4, default=0)
    valor_total = models.DecimalField(max_digits=14, decimal_places=2, default=0)

    def save(self, *args, **kwargs):
        if not self.pk:
            self.cantidad_antes = self.referencia.stock  # 👈 solo si es nuevo
        self.unidades_entrada = max(self.cantidad_despues - self.cantidad_antes, 0)
        self.unidades_salida = max(self.cantidad_antes - self.cantidad_despues, 0)
        self.valor_total = self.cantidad_despues * self.referencia.precio_costo
        self.referencia.stock = self.cantidad_despues
        self.referencia.save()
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Ajuste inventario {self.referencia.sku} - {self.fecha}"

class MuestraComercial(models.Model):
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    fecha = models.DateField(default=timezone.now)
    cantidad_entregada = models.DecimalField(max_digits=12, decimal_places=4)
    observaciones = models.TextField(blank=True)
    entregado_por = models.ForeignKey(User, on_delete=models.PROTECT)

    stock_antes = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    stock_despues = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    costo_unitario = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    valor_total_salida = models.DecimalField(max_digits=14, decimal_places=2, editable=False)

    class Meta:
        verbose_name = 'Muestra Comercial'
        verbose_name_plural = 'Muestras Comerciales'
        ordering = ['-fecha']

    def save(self, *args, **kwargs):
        if not self.pk:
            self.stock_antes = self.referencia.stock
            self.costo_unitario = self.referencia.precio_costo
            self.valor_total_salida = self.costo_unitario * self.cantidad_entregada

            self.stock_despues = self.stock_antes - self.cantidad_entregada
            self.referencia.stock = self.stock_despues
            self.referencia.save(update_fields=['stock'])

        super().save(*args, **kwargs)

    def __str__(self):
        return f"Muestra de {self.referencia.sku} - {self.fecha}"

class RecepcionProduccion(models.Model):
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    planta = models.ForeignKey(PlantaProduccion, on_delete=models.PROTECT)
    maquina = models.ForeignKey(Maquina, on_delete=models.PROTECT)
    fecha = models.DateField(default=timezone.now)
    cantidad_antes = models.DecimalField(max_digits=12, decimal_places=4)
    cantidad_producida = models.DecimalField(max_digits=12, decimal_places=4)
    cantidad_despues = models.DecimalField(
        max_digits=12, decimal_places=4, blank=True, null=True
    )
    costo_unitario_produccion = models.DecimalField(max_digits=12, decimal_places=4)
    nuevo_costo_ponderado = models.DecimalField(
        max_digits=12, decimal_places=4, blank=True, null=True
    )
    valor_total = models.DecimalField(
        max_digits=14, decimal_places=2, blank=True, null=True
    )

    class Meta:
        db_table = 'mov_recepcion_produccion_recepcionproduccion'
        verbose_name = 'Recepción de Producción'
        verbose_name_plural = 'Recepciones de Producción'
        ordering = ['-fecha']

    def save(self, *args, **kwargs):
        # cálculo de cantidades
        self.cantidad_despues = self.cantidad_antes + self.cantidad_producida

        # valores anteriores y nuevos
        costo_existente = self.referencia.precio_costo or Decimal('0')
        total_existente = self.cantidad_antes * costo_existente
        total_nuevo     = self.cantidad_producida * self.costo_unitario_produccion

        # valor total de inventario tras producción
        self.valor_total = total_existente + total_nuevo

        # costo ponderado simple: (valor antiguo + valor producido) / unidades totales
        self.nuevo_costo_ponderado = (
            self.valor_total / self.cantidad_despues
            if self.cantidad_despues
            else Decimal('0')
        )

        # actualizamos la referencia
        self.referencia.stock = self.cantidad_despues
        self.referencia.precio_costo = self.nuevo_costo_ponderado
        self.referencia.save(update_fields=['stock', 'precio_costo'])

        super().save(*args, **kwargs)
class MovimientoVenta(models.Model):
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    cantidad = models.DecimalField(
        max_digits=12, decimal_places=4,
        validators=[MinValueValidator(Decimal('0.0001'))]
    )
    precio_unitario_venta = models.DecimalField(max_digits=12, decimal_places=2)
    fecha = models.DateField(default=timezone.now)
    creado_por = models.ForeignKey(User, on_delete=models.PROTECT)

    class Meta:
        verbose_name = "Venta"
        verbose_name_plural = "Ventas"
        ordering = ['-fecha']

    def save(self, *args, **kwargs):
        # Resta del stock
        self.referencia.stock -= self.cantidad
        self.referencia.save(update_fields=['stock'])
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Venta de {self.cantidad} de {self.referencia.sku} a ${self.precio_unitario_venta}"

class DevolucionCompra(models.Model):
    item = models.ForeignKey(
        ItemOrdenCompra,
        on_delete=models.PROTECT,
        related_name='devoluciones_compra',
        verbose_name='Ítem de orden origen'
    )
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    fecha = models.DateField(default=timezone.now)
    cantidad_antes = models.DecimalField(max_digits=12, decimal_places=4)
    cantidad_devuelta = models.DecimalField(max_digits=12, decimal_places=4)
    cantidad_despues = models.DecimalField(max_digits=12, decimal_places=4)
    precio_unitario_devuelto = models.DecimalField(max_digits=12, decimal_places=4)
    nuevo_precio_ponderado = models.DecimalField(max_digits=12, decimal_places=4, blank=True, null=True)
    valor_total = models.DecimalField(max_digits=14, decimal_places=2, blank=True, null=True)

    def save(self, *args, **kwargs):
        self.cantidad_despues = self.cantidad_antes - self.cantidad_devuelta
        valor_inventario = self.cantidad_antes * self.referencia.precio_costo
        valor_devolucion = self.cantidad_devuelta * self.precio_unitario_devuelto
        nuevo_valor = valor_inventario - valor_devolucion
        self.nuevo_precio_ponderado = nuevo_valor / self.cantidad_despues if self.cantidad_despues > 0 else 0
        self.valor_total = nuevo_valor
        self.referencia.stock = self.cantidad_despues
        self.referencia.precio_costo = self.nuevo_precio_ponderado
        self.referencia.save(update_fields=['stock', 'precio_costo'])
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Devolución {self.referencia.sku} - {self.fecha}"


class DevolucionMuestraComercial(models.Model):
    muestra = models.ForeignKey(
        MuestraComercial,
        on_delete=models.PROTECT,
        related_name='devoluciones_muestra',
        verbose_name='Muestra origen'
    )
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    cantidad = models.DecimalField(max_digits=12, decimal_places=4)
    fecha = models.DateField(default=timezone.now)
    usuario = models.ForeignKey(User, on_delete=models.PROTECT)
    comentario = models.TextField(blank=True, null=True)

    def save(self, *args, **kwargs):
        self.referencia.stock += self.cantidad
        self.referencia.save(update_fields=['stock'])
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Devolución de {self.cantidad} de {self.referencia.sku} el {self.fecha}"


class DevolucionProduccion(models.Model):
    recepcion = models.ForeignKey(
        RecepcionProduccion,
        on_delete=models.PROTECT,
        related_name='devoluciones_produccion',
        verbose_name='Recepción origen'
    )
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    cantidad_devuelta = models.DecimalField(max_digits=12, decimal_places=4)
    costo_unitario = models.DecimalField(max_digits=12, decimal_places=4)
    fecha = models.DateField(default=timezone.now)
    observaciones = models.TextField(blank=True)

    stock_antes = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    stock_despues = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    precio_costo_antes = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    nuevo_precio_costo = models.DecimalField(max_digits=12, decimal_places=4, editable=False)
    valor_inventario = models.DecimalField(max_digits=14, decimal_places=2, editable=False)

    def save(self, *args, **kwargs):
        self.stock_antes = self.referencia.stock
        self.precio_costo_antes = self.referencia.precio_costo or 0
        restante = self.stock_antes - self.cantidad_devuelta
        if restante > 0:
            total_existente = self.stock_antes * self.precio_costo_antes
            total_salida = self.cantidad_devuelta * self.costo_unitario
            self.valor_inventario = total_existente - total_salida
            self.nuevo_precio_costo = self.valor_inventario / restante
        else:
            self.valor_inventario = 0
            self.nuevo_precio_costo = self.precio_costo_antes
        self.stock_despues = restante
        self.referencia.stock = restante
        self.referencia.precio_costo = self.nuevo_precio_costo
        self.referencia.save(update_fields=['stock', 'precio_costo'])
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Devolución {self.cantidad_devuelta} unidades de {self.referencia.sku}"


class MovimientoDevolucionVenta(models.Model):
    venta = models.ForeignKey(
        MovimientoVenta,
        on_delete=models.PROTECT,
        related_name='devoluciones_venta',
        verbose_name='Venta origen'
    )
    referencia = models.ForeignKey(Referencia, on_delete=models.PROTECT)
    cantidad = models.DecimalField(max_digits=12, decimal_places=4)
    precio_venta_unitario = models.DecimalField(max_digits=12, decimal_places=2)
    fecha = models.DateField(default=timezone.now)
    observaciones = models.TextField(blank=True)

    class Meta:
        verbose_name = 'Devolución de Venta'
        verbose_name_plural = 'Devoluciones de Venta'
        ordering = ['-fecha']

    def save(self, *args, **kwargs):
        if not self.pk:
            self.referencia.stock += self.cantidad
            self.referencia.save(update_fields=['stock'])
            from siip.utils import calcular_precio_venta_ponderado
            inicio = self.fecha.replace(day=1)
            pond = calcular_precio_venta_ponderado(self.referencia, inicio, self.fecha)
            print(f"💡 Nuevo precio venta ponderado ({inicio}→{self.fecha}): {pond}")
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Devolución de {self.cantidad} de {self.referencia.sku} el {self.fecha}"
class MovimientoPerdidaProceso(models.Model):
    referencia = models.ForeignKey(
        Referencia,
        on_delete=models.PROTECT,
        related_name='perdidas_en_proceso'
    )
    cantidad = models.DecimalField(
        max_digits=10, decimal_places=2,
        help_text="Cantidad de unidades dañadas"
    )
    fecha = models.DateTimeField(
        default=timezone.now,
        help_text="Fecha y hora de la pérdida"
    )
    motivo = models.CharField(
        max_length=200,
        blank=True,
        help_text="Descripción breve del motivo de la pérdida"
    )
    observaciones = models.TextField(
        blank=True,
        help_text="Comentarios adicionales"
    )

    def save(self, *args, **kwargs):
        # Ajuste del stock según delta de cantidad
        if self.pk:
            prev = MovimientoPerdidaProceso.objects.get(pk=self.pk)
            delta = self.cantidad - prev.cantidad
        else:
            delta = self.cantidad
        super().save(*args, **kwargs)
        self.referencia.stock -= delta
        self.referencia.save(update_fields=['stock'])

    def delete(self, *args, **kwargs):
        # Al eliminar, reintegrar stock completo
        self.referencia.stock += self.cantidad
        self.referencia.save(update_fields=['stock'])
        super().delete(*args, **kwargs)