# devolucion_venta/utils.py
import logging
from movimientos.models import MovimientoVenta , MovimientoDevolucionVenta
from decimal import Decimal


from lista_de_materiales.models import ListaMateriales  
from movimientos.models import ItemOrdenCompra

from referencias.models import Referencia  # Asegúrate de que la ruta sea correcta

def obtener_ultimo_precio_compra(ref: Referencia) -> Decimal:
    """Fallback: devuelve el precio_unitario de la última compra, o 0."""
    ultima = (
        ItemOrdenCompra.objects
        .filter(referencia=ref)
        .order_by('-orden__fecha_requerida')
        .first()
    )
    if ultima:
        print(f"    ↳ Fallback precio compra de {ref.sku}: {ultima.precio_unitario}")
        return ultima.precio_unitario
    print(f"    ↳ Sin compras previas para {ref.sku} ⇒ 0")
    return Decimal('0')

def costo_desde_bom(ref: Referencia) -> Decimal:
    """
    Para ref tipo PT/PP recorre su BOM 'A' y suma:
      cantidad_total * costo_sub.
    Donde costo_sub = sub.precio_costo (si >0) o recursión.
    Si no hay BOM, usa precio_costo o fallback a última compra.
    """
    print(f"\n⟳ costo_desde_bom: inicio SKU={ref.sku} (id={ref.pk})")
    try:
        bom = ListaMateriales.objects.get(referencia=ref, estado='A')
        print(f"  ↳ Encontrado BOM id={bom.pk} para {ref.sku}")
    except ListaMateriales.DoesNotExist:
        base = ref.precio_costo or obtener_ultimo_precio_compra(ref)
        print(f"  ↳ Sin BOM: uso base={base} para {ref.sku}")
        return base

    total = Decimal('0')
    for comp in bom.componentes.select_related('referencia').all():
        sub = comp.referencia
        qty = comp.cantidad_total
        print(f"   • Componente {sub.sku}: qty={qty}")

        # Si ya tiene precio de costo positivo, lo usamos:
        if sub.precio_costo and sub.precio_costo > 0:
            costo_sub = sub.precio_costo
            print(f"       → usa sub.precio_costo={costo_sub}")
        else:
            # si no, recursión:
            print(f"       → precio_costo de {sub.sku} vacío o cero, recursión...")
            costo_sub = costo_desde_bom(sub)

        contrib = qty * costo_sub
        print(f"       → contribución: {qty} × {costo_sub} = {contrib}")
        total += contrib

    print(f" ← FIN {ref.sku}: total BOM cost = {total}\n")
    return total



# siip/utils.py
from decimal import Decimal

def calcular_precio_venta_ponderado(
    referencia,
    fecha_inicio,
    fecha_fin,
    return_history: bool = False
    ):
    """
    Calcula el precio de venta ponderado para una referencia entre fecha_inicio y fecha_fin,
    incluyendo tanto ventas como devoluciones de venta.

    Si return_history=True, devuelve una tupla:
        (precio_ponderado: Decimal,
         total_unidades: Decimal,
         total_valor: Decimal)
    En caso contrario, devuelve sólo precio_ponderado (para compatibilidad).
    """
    # Importamos aquí para evitar ciclos de importación

    total_unidades = Decimal('0')
    total_valor    = Decimal('0')

    print(f"\n▶▶ ponderado: ref={referencia.sku}, rango={fecha_inicio}→{fecha_fin}")

    # — Ventas —
    ventas = MovimientoVenta.objects.filter(
        referencia=referencia,
        fecha__range=(fecha_inicio, fecha_fin)
    ).order_by('fecha')
    print(f"   Vendidos: {ventas.count()} movimientos")
    for v in ventas:
        contrib = v.cantidad * v.precio_unitario_venta
        print(f"     [V] {v.fecha} qty={v.cantidad} @ {v.precio_unitario_venta} ⇒ {contrib}")
        total_unidades += v.cantidad
        total_valor    += contrib

    # — Devoluciones de venta —
    devs = MovimientoDevolucionVenta.objects.filter(
        referencia=referencia,
        fecha__range=(fecha_inicio, fecha_fin)
    ).order_by('fecha')
    print(f"   Devueltos: {devs.count()} movimientos")
    for d in devs:
        contrib = d.cantidad * d.precio_venta_unitario
        print(f"     [D] {d.fecha} qty={d.cantidad} @ {d.precio_venta_unitario} ⇒ {contrib}")
        total_unidades += d.cantidad
        total_valor    += contrib

    print(f"   Totales → unidades={total_unidades}, valor={total_valor}")

    if total_unidades == 0:
        print("   ¡No hubo movimientos en el rango! Precio pond = 0\n")
        if return_history:
            return Decimal('0'), total_unidades, total_valor
        return Decimal('0')

    precio_pond = total_valor / total_unidades
    print(f"   Precio ponderado calculado = {precio_pond}\n")

    if return_history:
        return precio_pond, total_unidades, total_valor
    return precio_pond