<?php

namespace App\Services;

use App\Models\Inventario;
use App\Models\MovimientoInventario;
use App\Models\Producto;
use Illuminate\Validation\ValidationException;

class InventoryFlowService
{
    /**
     * @return array{stock_actual: float, stock_nuevo: float, cantidad_movimiento: float}
     */
    public function applyMovement(
        int $empresaId,
        ?int $sucursalId,
        Producto $producto,
        string $tipo,
        float $cantidad,
        float $costoUnitario = 0.0,
        ?string $referencia = null
    ): array {
        if (! in_array($tipo, ['entrada', 'salida', 'ajuste'], true)) {
            throw ValidationException::withMessages([
                'tipo' => 'Tipo de movimiento de inventario invalido.',
            ]);
        }

        if ($cantidad <= 0) {
            throw ValidationException::withMessages([
                'cantidad' => 'La cantidad debe ser mayor a cero.',
            ]);
        }

        $inventarioQuery = Inventario::query()
            ->where('empresa_id', $empresaId)
            ->where('producto_id', $producto->id);

        if ($sucursalId) {
            $inventarioQuery->where('sucursal_id', $sucursalId);
        } else {
            $inventarioQuery->whereNull('sucursal_id');
        }

        $inventario = $inventarioQuery->lockForUpdate()->first();

        if (! $inventario) {
            $inventario = new Inventario([
                'empresa_id' => $empresaId,
                'sucursal_id' => $sucursalId,
                'producto_id' => $producto->id,
                'stock' => 0,
                'costo_promedio' => 0,
            ]);
        }

        $stockActual = (float) $inventario->stock;
        $stockNuevo = $stockActual;
        $cantidadMovimiento = $cantidad;
        $costoPromedio = (float) $inventario->costo_promedio;

        if ($tipo === 'entrada') {
            $stockNuevo = $stockActual + $cantidad;

            if ($stockNuevo > 0 && $costoUnitario > 0) {
                $costoPromedio = (($stockActual * $costoPromedio) + ($cantidad * $costoUnitario)) / $stockNuevo;
            }
        } elseif ($tipo === 'salida') {
            if ($cantidad > $stockActual) {
                throw ValidationException::withMessages([
                    'cantidad' => 'No hay stock suficiente para registrar la salida.',
                ]);
            }

            $stockNuevo = $stockActual - $cantidad;
        } else {
            // En ajuste, la cantidad se interpreta como el nuevo stock final.
            $stockNuevo = $cantidad;
            $cantidadMovimiento = $stockNuevo - $stockActual;
        }

        $inventario->fill([
            'stock' => $stockNuevo,
            'costo_promedio' => $costoPromedio,
            'updated_at' => now(),
        ]);
        $inventario->save();

        MovimientoInventario::query()->create([
            'empresa_id' => $empresaId,
            'sucursal_id' => $sucursalId,
            'producto_id' => $producto->id,
            'tipo' => $tipo,
            'referencia' => $referencia,
            'cantidad' => $cantidadMovimiento,
            'costo_unitario' => $costoUnitario,
            'created_at' => now(),
        ]);

        $stockTotal = (float) Inventario::query()
            ->where('empresa_id', $empresaId)
            ->where('producto_id', $producto->id)
            ->sum('stock');

        $producto->update(['stock_actual' => $stockTotal]);

        return [
            'stock_actual' => $stockActual,
            'stock_nuevo' => $stockNuevo,
            'cantidad_movimiento' => $cantidadMovimiento,
        ];
    }
}
