from django.shortcuts import render, redirect , get_object_or_404
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.http import JsonResponse
from django.views.decorators.http import require_GET
from django.urls import reverse
from .forms import OrdenCompraForm, ItemOrdenCompraManualFormSet , DevolucionCompraForm , DevolucionMuestraComercialForm, DevolucionProduccionForm, MovimientoDevolucionVentaForm, AjusteInventarioFisicoForm, MuestraComercialForm, RecepcionProduccionForm, MovimientoVentaForm, PerdidaProcesoForm
from .models import OrdenCompra, DevolucionCompra, DevolucionMuestraComercial, DevolucionProduccion, MovimientoDevolucionVenta, RecepcionProduccion, MovimientoVenta, ItemOrdenCompra, MuestraComercial
from decimal import Decimal
from .models import Referencia
from fabricacion.models import MaquinaReferencia 
from proveedores.models import ProveedorReferencia
from siip.utils import costo_desde_bom ,calcular_precio_venta_ponderado
from django.utils.dateparse import parse_date
from django.utils.timezone import now
import logging
import datetime
from django.utils import timezone
from django.forms import formset_factory



@staff_member_required
def ordencompra_manual_view(request):
    if request.method == 'POST':
        form = OrdenCompraForm(request.POST)
        if form.is_valid():
            # instanciamos la orden en memoria con los datos del form
            orden_temp = form.save(commit=False)
            orden_temp.creada_por = request.user

            # ahora asociamos el formset a esa misma orden_temp
            formset = ItemOrdenCompraManualFormSet(request.POST, instance=orden_temp)

            if formset.is_valid():
                # guardamos la orden y luego los ítems
                orden_temp.save()
                formset.instance = orden_temp
                formset.save()
                messages.success(request, "Orden de compra creada exitosamente.")
                return redirect('admin:movimientos_ordencompra_changelist')
            else:
                messages.error(request, "Corrige los errores en los ítems.")
        else:
            messages.error(request, "Corrige los errores en el formulario principal.")
            # para renderizar los errores, vuelve a atar el formset a la orden temporal
            orden_temp = form.save(commit=False)
            formset = ItemOrdenCompraManualFormSet(request.POST, instance=orden_temp)
    else:
        form = OrdenCompraForm()
        formset = ItemOrdenCompraManualFormSet(instance=OrdenCompra())

    return render(request,
                  'admin/compras/ordencompra_form.html',
                  {'form': form, 'formset': formset})


def api_referencia_proveedor(request, pk):
    """
    GET /movimientos/ordencompra/api/ref/<pk>/
    JSON:
      - proveedores: [
          {
            prov_ref_id,
            proveedor_id,
            proveedor_nombre,
            lead_time_total,
            esta_certificado,
            empaque_id,
            empaque_nombre,
          },
          ...
        ]
      - proveedor_referencia_id: el primero (o None)
    """
    ref = get_object_or_404(Referencia, pk=pk)

    qs = ProveedorReferencia.objects.filter(referencia=ref).select_related('proveedor','empaque')
    proveedores = []
    for pr in qs:
        proveedores.append({
            'prov_ref_id':      pr.id,
            'proveedor_id':     pr.proveedor_id,
            'proveedor_nombre': str(pr.proveedor),
            'lead_time_total':  pr.lead_time_total,
            'esta_certificado': pr.esta_certificado,
            'empaque_id':       pr.empaque_id,
            'empaque_nombre':   str(pr.empaque),
        })

    return JsonResponse({
        'proveedores': proveedores,
        'proveedor_referencia_id': proveedores[0]['prov_ref_id'] if proveedores else None,
    })
@staff_member_required
def devolucion_compra_manual_view(request):
    """
    Vista personalizada para “Add” de DevolucionCompra.
    """
    if request.method == 'POST':
        form = DevolucionCompraForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Devolución de compra guardada correctamente.")
            return redirect('admin:movimientos_devolucioncompra_changelist')
        else:
            messages.error(request, "Corrige los errores del formulario.")
    else:
        form = DevolucionCompraForm()

    return render(request,
                  'admin/devolucion_compras/add_form.html',
                  {'form': form})

logger = logging.getLogger(__name__)


@staff_member_required
def api_item_info_devolucion_compra(request, pk):
    """
    Devuelve JSON con:
      - stock_item:        cantidad disponible en el ítem de orden (cantidad antes)
      - stock_referencia:  stock total actual de la referencia
      - precio_ponderado:  precio de costo actual de la referencia
    """
    item = get_object_or_404(ItemOrdenCompra, pk=pk)
    ref  = item.referencia

    return JsonResponse({
        'stock':              float(item.cantidad),
        'stock_referencia':   float(ref.stock),
        'precio_ponderado':   float(ref.precio_costo or 0),
    })
@staff_member_required
def devolucion_muestra_comercial_manual_view(request):
    opts       = DevolucionMuestraComercial._meta
    # esto construye algo como 'admin:mov_devolucion_muestras_comerciales_devolucionmuestracomercial_changelist'
    cancel_url = reverse(f'admin:{opts.app_label}_{opts.model_name}_changelist')

    if request.method == 'POST':
        form = DevolucionMuestraComercialForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Devolución de muestra comercial creada correctamente.")
            return redirect(cancel_url)
        else:
            messages.error(request, "Por favor corrige los errores del formulario.")
    else:
        form = DevolucionMuestraComercialForm()

    return render(request,
                  'admin/mov_devolucion_muestras_comerciales/devolucionmuestracomercial/add_form.html',
                  {
                    'form': form,
                    'cancel_url': cancel_url,
                  })
    
@staff_member_required
def api_referencia_info_muestra(request, pk):
    ref = get_object_or_404(Referencia, pk=pk)
    precio_directo = ref.precio_costo or Decimal('0')
    if ref.tipo in ['PT','PP'] or precio_directo == 0:
        precio = costo_desde_bom(ref)
    else:
        precio = precio_directo
    logger.debug(f"API muestra ref={ref.pk}: stock={ref.stock}, costo={precio}")
    return JsonResponse({
        'stock': float(ref.stock),
        'costo_unitario': float(precio),
    })


@staff_member_required
def devolucion_produccion_manual_view(request):
    opts       = DevolucionProduccion._meta
    cancel_url = reverse(f'admin:{opts.app_label}_{opts.model_name}_changelist')

    if request.method == 'POST':
        form = DevolucionProduccionForm(request.POST)
        print("🐞 POST DATA:", request.POST.dict())
        if form.is_valid():
            print("🐞 CLEANED:", form.cleaned_data)
            form.save()
            messages.success(request, "Devolución guardada correctamente.")
            return redirect(cancel_url)
        else:
            print("🐞 ERRORS:", form.errors)
            messages.error(request, "Corrige los errores del formulario.")
    else:
        form = DevolucionProduccionForm()

    return render(request,
                  'admin/mov_devolucion_produccion/devolucionproduccion/add_form.html',
                  {'form': form, 'cancel_url': cancel_url})


@staff_member_required
def api_referencia_info2(request, pk):
    """
    Devuelve JSON con stock y precio_costo de la referencia,
    para poblar los campos de la devolución.
    """
    print(f"🐞 api_referencia_info called with pk={pk}")
    ref = get_object_or_404(Referencia, pk=pk)
    print(f"🐞 Referencia encontrada: id={ref.id}, stock={ref.stock}, precio_costo={ref.precio_costo}")
    data = {
        'stock': float(ref.stock),
        'precio_costo': float(ref.precio_costo or 0),
    }
    print(f"🐞 Data a retornar: {data}")
    return JsonResponse(data)


@staff_member_required
def devolucion_venta_manual_view(request):
    opts       = MovimientoDevolucionVenta._meta
    cancel_url = reverse(f'admin:{opts.app_label}_{opts.model_name}_changelist')

    if request.method == 'POST':
        form = MovimientoDevolucionVentaForm(request.POST)
        logger.debug("🐞 POST DATA: %s", request.POST.dict())
        if form.is_valid():
            form.save()
            messages.success(request, "Devolución de venta guardada correctamente.")
            return redirect(cancel_url)
        else:
            messages.error(request, "Corrige los errores del formulario.")
    else:
        form = MovimientoDevolucionVentaForm()

    return render(request,
                  'admin/mov_devolucion_venta/movimientodevolucionventa/add_form.html',
                  {'form': form, 'cancel_url': cancel_url})

logger = logging.getLogger(__name__)

@staff_member_required
def api_venta_info_devolucion(request, pk):
    """
    GET params (opcionales): inicio=YYYY-MM-DD, fin=YYYY-MM-DD
    Dado el PK de una Venta, devuelve JSON con:
      - referencia:                 pk de la referencia vendida
      - stock_venta:                cantidad originalmente vendida en esa Venta
      - stock_referencia:           stock total actual de la referencia
      - precio_venta_unitario:      precio unitario de la venta original
      - precio_venta_ponderado:     precio ponderado calculado entre inicio→fin
      - historical_unidades:        suma de cantidades devueltas en el rango
      - historical_valor:           suma de (cantidad * precio_unitario) en el rango
    """
    venta = get_object_or_404(MovimientoVenta, pk=pk)
    ref   = venta.referencia

    # parseo de parámetros de fecha o valores por defecto
    inicio_str = request.GET.get('inicio')
    fin_str    = request.GET.get('fin')
    inicio = parse_date(inicio_str) if inicio_str else now().date().replace(day=1)
    fin    = parse_date(fin_str)    if fin_str    else now().date()

    # calculamos precio ponderado y recogemos histórico
    pond, hist_unidades, hist_valor = calcular_precio_venta_ponderado(
        ref, inicio, fin, return_history=True
    )

    return JsonResponse({
        'referencia':               ref.pk,
        'stock_venta':              float(venta.cantidad),
        'stock_referencia':         float(ref.stock),
        'precio_venta_unitario':    float(venta.precio_unitario_venta),
        'precio_venta_ponderado':   float(pond),
        'historical_unidades':      float(hist_unidades),
        'historical_valor':         float(hist_valor),
    })
def api_stock_referencia(request, idreferencia):
    """
    GET /movimientos/api/stock-referencia/<idreferencia>/

    Devuelve JSON con:
      - stock: float
      - precio_costo: float (opcional)
      - opciones_planta_maquina: [
          { planta_id, planta_nombre, maquina_id, maquina_nombre }, ...
        ]
    """
    # Obtiene la referencia o 404
    ref = get_object_or_404(Referencia, pk=idreferencia)

    # Datos básicos
    data = {
        'stock': float(ref.stock),
        # Si quieres el precio de costo actual:
        'precio_costo': float(ref.precio_costo or 0),
    }

    # Todas las relaciones planta–máquina de esta referencia
    qs = (
        MaquinaReferencia.objects
        .filter(referencia=ref)
        .select_related('maquina__planta')
    )
    opciones = []
    for mr in qs:
        planta  = mr.maquina.planta
        maquina = mr.maquina
        opciones.append({
            'planta_id':     planta.id,
            'planta_nombre': planta.nombre,
            'maquina_id':    maquina.id,
            'maquina_nombre': maquina.nombre,
        })
    print(f"🐞 API ref={idreferencia}: stock={ref.stock}, opciones={opciones}")
    data['opciones_planta_maquina'] = opciones
    return JsonResponse(data)

def ajuste_manual_view(request):
    if request.method == 'POST':
        form = AjusteInventarioFisicoForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('movimientos:ajuste_manual')  # redirecciona a la misma página o a donde prefieras
    else:
        form = AjusteInventarioFisicoForm()

    return render(request, 'admin/inventario_fisico/ajuste_manual.html', {
        'form': form,
        'titulo': 'Ajuste de Inventario Manual',
    })
    
@staff_member_required
def muestra_comercial_manual_view(request):
    """
    Formulario propio para crear una MuestraComercial,
    con cálculos de stock y valor de salida en cliente.
    """
    if request.method == 'POST':
        form = MuestraComercialForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Muestra comercial creada correctamente.")
            return redirect('admin:movimientos_muestracomercial_changelist')
        else:
            messages.error(request, "Por favor corrige los errores del formulario.")
    else:
        form = MuestraComercialForm()

    return render(request,
                  'admin/mov_muestra_comercial/muestracomercial/add_form.html',
                  {
                    'form': form,
                    'cancel_url': reverse('admin:movimientos_muestracomercial_changelist')
                  })
@staff_member_required
def api_referencia_info_muestra(request, pk):
    """
    API que devuelve JSON con:
      - stock actual de la referencia
      - costo unitario: si es PT/PP o tiene precio_costo=0, lo recalcula desde el BOM;
                         en caso contrario usa precio_costo directo.
    """
    ref = get_object_or_404(Referencia, pk=pk)

    precio_directo = ref.precio_costo or 0
    if ref.tipo in ['PT', 'PP'] or precio_directo == 0:
        precio = costo_desde_bom(ref)
    else:
        precio = precio_directo

    return JsonResponse({
        'stock': float(ref.stock),
        'costo_unitario': float(precio),
    })

@staff_member_required
def recepcion_produccion_manual_view(request):
    opts = RecepcionProduccion._meta
    cancel_url = reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name))

    if request.method == 'POST':
        form = RecepcionProduccionForm(request.POST)
        print("🐞 POST DATA:", request.POST.dict())           # <–– imprimimos todo el POST
        if form.is_valid():
            print("🐞 CLEANED DATA:", form.cleaned_data)      # <–– todo limpio
            form.save()
            messages.success(request, "Recepción guardada.")
            return redirect(cancel_url)
        else:
            print("🐞 FORM ERRORS:", form.errors)              # <–– si falla, vemos por qué
            messages.error(request, "Corrige los errores.")
    else:
        form = RecepcionProduccionForm()

    return render(request,
                  'admin/mov_produccion/recepcionproduccion/add_form.html',
                  {'form': form, 'cancel_url': cancel_url})

logger = logging.getLogger(__name__)

@staff_member_required
def venta_manual_view(request):
    opts       = MovimientoVenta._meta
    cancel_url = reverse(f'admin:{opts.app_label}_{opts.model_name}_changelist')

    if request.method == 'POST':
        form = MovimientoVentaForm(request.POST)
        logger.debug("🐞 POST DATA: %s", request.POST.dict())
        if form.is_valid():
            logger.debug("🐞 CLEANED: %s", form.cleaned_data)
            form.save()
            messages.success(request, "Venta registrada correctamente.")
            return redirect(cancel_url)
        else:
            logger.debug("🐞 ERRORES FORM: %s", form.errors)
            messages.error(request, "Corrige los errores del formulario.")
    else:
        form = MovimientoVentaForm()

    return render(request,
                  'admin/venta/movimientoventa/add_form.html',
                  {'form': form, 'cancel_url': cancel_url})
from django.http           import JsonResponse
from django.views.decorators.http import require_GET
from django.utils.timezone import now
from datetime import datetime


logger = logging.getLogger(__name__)

@staff_member_required
@require_GET

def api_referencia_info_venta(request, pk):
    """
    GET params:
      - inicio=YYYY-MM-DD   (opcional; default: primer día del mes actual)
      - fin=YYYY-MM-DD      (opcional; default: hoy)
    Devuelve JSON con:
      - stock
      - precio_venta_ponderado
      - opciones_planta_maquina: [
            {
              planta_id,
              planta_nombre,
              maquina_id,
              maquina_nombre
            }, ...
        ]
    """
    ref = get_object_or_404(Referencia, pk=pk)

    # parsear fechas
    hoy        = now().date()
    def parse_date(s):
        try:
            return datetime.strptime(s, "%Y-%m-%d").date()
        except Exception:
            return None

    inicio = parse_date(request.GET.get('inicio')) or hoy.replace(day=1)
    fin    = parse_date(request.GET.get('fin'))    or hoy

    precio_pond = calcular_precio_venta_ponderado(ref, inicio, fin)

    # buscar todas las relaciones planta–máquina para esta referencia
    qs = (
        MaquinaReferencia.objects
        .filter(referencia=ref)
        .select_related('maquina__planta')
    )
    opciones = []
    for mr in qs:
        planta = mr.maquina.planta
        maquina = mr.maquina
        opciones.append({
            'planta_id':     planta.id,
            'planta_nombre': planta.nombre,
            'maquina_id':    maquina.id,
            'maquina_nombre': maquina.nombre,
        })
    print(f"🐞 API ref={pk}: stock={ref.stock}, precio_pond={precio_pond}, opciones={opciones}")

    return JsonResponse({
        'stock':                      float(ref.stock),
        'precio_venta_ponderado':     float(precio_pond),
        'inicio':                     inicio.isoformat(),
        'fin':                        fin.isoformat(),
        'opciones_planta_maquina':    opciones,
    })

@staff_member_required
def devolver_orden_unificado(request):
    """
    Permite devolver varios ítems de una OrdenCompra de forma unificada.
    """
    # 1) Todas las órdenes para el selector
    ordenes = OrdenCompra.objects.all()

    # 2) Detectar la orden seleccionada (GET o POST)
    orden_selected = request.POST.get('orden_select') or request.GET.get('orden_select')

    # 3) Creamos un formset sin extras: un form por cada ítem
    DevolucionFormSet = formset_factory(DevolucionCompraForm, extra=0)
    formset = None

    if request.method == 'POST':
        # procesar POST
        formset = DevolucionFormSet(request.POST)
        if formset.is_valid():
            creadas = 0
            for form in formset:
                dv = form.cleaned_data.get('cantidad_devuelta') or 0
                # sólo guardamos si el usuario devolvió algo
                if dv > 0:
                    form.save()
                    creadas += 1
            if creadas:
                messages.success(request, f"{creadas} devoluciones creadas correctamente.")
                return redirect('admin:movimientos_devolucioncompra_changelist')
            else:
                messages.info(request, "No se devolvió ninguna cantidad.")
        else:
            messages.error(request, "Corrige los errores del formulario.")

    elif orden_selected:
        # GET tras elegir una orden: precargamos initial con cada ítem
        orden = get_object_or_404(OrdenCompra, pk=orden_selected)
        items = orden.items.select_related('referencia').all()

        initial = []
        hoy = timezone.now().date()
        for item in items:
            initial.append({
                'item':                      item.pk,
                'referencia':                item.referencia.pk,
                'fecha':                     hoy,
                'cantidad_antes':            item.cantidad,
                'precio_unitario_devuelto':  item.precio_unitario,
            })
        formset = DevolucionFormSet(initial=initial)

    return render(request,
                  'admin/devolucion_compras/add_form.html',
                  {
                      'ordenes':        ordenes,
                      'orden_selected': orden_selected,
                      'formset':        formset,
                  })
# movimientos/views.py

def muestra_info_api(request, pk):
    """
    Retorna JSON con:
      - referencia: pk de la referencia asociada
      - stock_muestra: cantidad de esta muestra
      - stock_referencia: stock total de la referencia
      - costo_unitario: costo unitario de la referencia
      - usuario: pk del usuario que creó la muestra
    """
    muestra = get_object_or_404(MuestraComercial, pk=pk)
    ref = muestra.referencia

    return JsonResponse({
        'referencia': ref.pk,
        'stock_muestra': float(muestra.cantidad_entregada),
        'stock_referencia': float(ref.stock),
        'costo_unitario': float(ref.precio_costo),
        'usuario': muestra.entregado_por.pk,
    })



def recepcion_info_api(request, pk):
    recepcion = get_object_or_404(RecepcionProduccion, pk=pk)
    ref       = recepcion.referencia

    return JsonResponse({
        'referencia':                    ref.pk,
        'stock_recepcion':               float(recepcion.cantidad_despues),
        'stock_referencia':              float(ref.stock),
        'precio_costo':                  float(ref.precio_costo or 0),
        'costo_unitario_produccion':     float(recepcion.costo_unitario_produccion),
    })
    
def perdida_add(request):
    referencias = Referencia.objects.all()
    if request.method == 'POST':
        form = PerdidaProcesoForm(request.POST)
        # Asegurar queryset de referencias
        form.fields['referencia'].queryset = referencias
        if form.is_valid():
            form.save()
            return redirect('movimientos:perdida_add')
    else:
        form = PerdidaProcesoForm()
        # Asegurar queryset de referencias
        form.fields['referencia'].queryset = referencias
    return render(request, 'admin/mov_perdida_proceso/add_form.html', {
        'form': form,
        'referencias': referencias,
    })

