<?php

namespace App\Services;

use App\Models\{OrdenCompra, OrdenCompraItem, Insumo, Proveedor};
use Illuminate\Support\Facades\DB;

class PreCompraService
{
    /**
     * Generar pre-compra automática desde insumos con stock bajo
     */
    public function generarDesdeStockBajo(int $sucursalId, $cajaAbierta = null): ?OrdenCompra
    {
        // Obtener insumos bajo el mínimo
        $insumosBajos = $this->obtenerInsumosBajos($sucursalId);

        if ($insumosBajos->isEmpty()) {
            return null;
        }

        // Agrupar por proveedor principal
        $porProveedor = $insumosBajos->groupBy(function ($insumo) {
            return $insumo->proveedorPrincipal()?->id ?? 'sin_proveedor';
        });

        // Crear múltiples órdenes si hay insumos sin proveedor
        $ordenes = collect();
        $saldoDisponible = $cajaAbierta ? ($cajaAbierta->monto_apertura + $cajaAbierta->total_ingresos - $cajaAbierta->total_egresos) : PHP_FLOAT_MAX;
        $totalProyectado = 0;

        foreach ($porProveedor as $proveedorId => $insumos) {
            if ($proveedorId === 'sin_proveedor') {
                continue; // No crear orden para insumos sin proveedor
            }

            $orden = DB::transaction(function () use ($proveedorId, $insumos, $sucursalId, &$totalProyectado, $saldoDisponible, $cajaAbierta) {
                $total = 0;
                $items = [];

                foreach ($insumos as $insumo) {
                    $cantidadUso = $this->calcularCantidadReorden($insumo, $sucursalId);
                    $factor = ($insumo->factor_conversion && $insumo->factor_conversion > 0) ? $insumo->factor_conversion : 1;
                    $cantidadCompra = round($cantidadUso / $factor, 2);
                    if ($cantidadCompra <= 0) $cantidadCompra = 0.01; // Mínimo de seguridad

                    $precio = $insumo->costo_unitario ?? 0;
                    $subtotal = $cantidadCompra * $precio;
                    $total += $subtotal;

                    $items[] = [
                        'insumo_id' => $insumo->id,
                        'cantidad' => $cantidadCompra,
                        'precio_est' => $precio,
                    ];
                }

                $totalProyectado += $total;
                if ($cajaAbierta && $totalProyectado > $saldoDisponible) {
                    throw new \Exception('No hay saldo suficiente en la Caja para generar todas las pre-compras.');
                }

                // Crear orden
                $orden = OrdenCompra::create([
                    'sucursal_id' => $sucursalId,
                    'numero' => $this->generarNumero($sucursalId),
                    'proveedor_id' => $proveedorId,
                    'estado' => 'pre_compra',
                    'total_estimado' => round($total, 2),
                    'solicitado_por' => auth()->id(),
                    'notas' => 'Generada automáticamente desde stock bajo',
                ]);

                // Crear items
                foreach ($items as $item) {
                    $insumo = Insumo::find($item['insumo_id']);
                    $orden->items()->create([
                        'insumo_id' => $item['insumo_id'],
                        'unidad' => $insumo->unidad_compra,
                        'cantidad_solicitada' => $item['cantidad'],
                        'precio_unitario_estimado' => $item['precio_est'],
                    ]);
                }

                return $orden;
            });

            $ordenes->push($orden);
        }

        return $ordenes->first();
    }

    /**
     * Obtener insumos que están bajo el stock mínimo
     */
    public function obtenerInsumosBajos(int $sucursalId): \Illuminate\Database\Eloquent\Collection
    {
        return Insumo::activos()
            ->with(['kardex' => function ($q) use ($sucursalId) {
                $q->where('sucursal_id', $sucursalId)
                  ->latest('created_at');
            }])
            ->get()
            ->filter(function ($insumo) use ($sucursalId) {
                $stock = $insumo->getStockActual($sucursalId);
                $minimo = $insumo->stock_minimo ?? 10;
                return $stock <= $minimo;
            });
    }

    /**
     * Calcular cantidad óptima para reorden (hasta el máximo)
     */
    public function calcularCantidadReorden($insumo, int $sucursalId): float
    {
        $stockActual = $insumo->getStockActual($sucursalId);
        $stockMaximo = $insumo->stock_maximo ?? 100;

        return max(0, $stockMaximo - $stockActual);
    }

    /**
     * Generar orden desde arreglo específico de ítems
     */
    public function generarDesdeArreglo(int $sucursalId, array $itemsReq, string $notas, $cajaAbierta = null): OrdenCompra
    {
        return DB::transaction(function () use ($sucursalId, $itemsReq, $notas, $cajaAbierta) {
            $totalEst = 0;
            $itemsData = [];

            foreach ($itemsReq as $itemReq) {
                $insumo = Insumo::find($itemReq['insumo_id']);
                $factor = ($insumo->factor_conversion && $insumo->factor_conversion > 0) ? $insumo->factor_conversion : 1;
                $cantidadCompra = round($itemReq['cantidad'] / $factor, 2);
                if ($cantidadCompra <= 0) $cantidadCompra = 0.01;

                $precio = $insumo->costo_unitario ?? 0;
                $totalEst += $cantidadCompra * $precio;

                $itemsData[] = [
                    'insumo' => $insumo,
                    'cantidad' => $cantidadCompra,
                    'precio' => $precio,
                ];
            }

            $saldoDisponible = $cajaAbierta ? ($cajaAbierta->monto_apertura + $cajaAbierta->total_ingresos - $cajaAbierta->total_egresos) : PHP_FLOAT_MAX;
            if ($cajaAbierta && $totalEst > $saldoDisponible) {
                $moneda = \App\Models\Configuracion::getParaSucursal('moneda_simbolo', $sucursalId, 'S/.');
                throw new \Exception("No hay saldo suficiente en la Caja para procesar esta orden. Saldo disponible: {$moneda} " . number_format($saldoDisponible, 2));
            }

            $orden = OrdenCompra::create([
                'sucursal_id'    => $sucursalId,
                'numero'         => $this->generarNumero($sucursalId),
                'proveedor_id'   => null,
                'estado'         => 'pre_compra',
                'total_estimado' => round($totalEst, 2),
                'solicitado_por' => auth()->id(),
                'notas'          => $notas,
            ]);

            foreach ($itemsData as $data) {
                $orden->items()->create([
                    'insumo_id'                => $data['insumo']->id,
                    'unidad'                   => $data['insumo']->unidad_compra ?? $data['insumo']->unidad_uso,
                    'cantidad_solicitada'      => $data['cantidad'],
                    'precio_unitario_estimado' => $data['precio'],
                ]);
            }

            return $orden;
        });
    }

    /**
     * Generar número único de orden
     */
    private function generarNumero(int $sucursalId): string
    {
        $count = OrdenCompra::where('sucursal_id', $sucursalId)->count() + 1;
        return 'OC-' . str_pad($sucursalId, 2, '0', STR_PAD_LEFT) . '-' . date('Y') . '-' . str_pad($count, 4, '0', STR_PAD_LEFT);
    }
}