<?php

namespace App\Http\Controllers;

use App\Models\Bien;
use App\Models\DevolucionPrestamo;
use App\Models\DevolucionTransferencia;
use App\Models\Dotacion;
use App\Models\Embarcacion;
use App\Models\NumOperacion;
use App\Models\Operacion;
use App\Models\Prestamo;
use App\Models\RegistroStock;
use App\Models\Sitio;
use App\Models\Transferencia;
use App\Models\Usuario;
use App\Services\InventoryCostingService;
use App\Services\AsignacionSupportService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class AsignacionController extends Controller
{
    public function __construct(
        private readonly AsignacionSupportService $asignacionSupportService,
        private readonly InventoryCostingService $inventoryCostingService,
    )
    {
    }

    private const BIEN_TYPE_LABELS = [
        1 => 'Equipo',
        2 => 'Herramienta',
        3 => 'Dotacion',
        4 => 'Consumible',
        5 => 'Repuesto',
        6 => 'Material oficina',
        7 => 'Material medico',
    ];

    private function buildSkippedItemsWarning(array $skippedItems): ?string
    {
        if ($skippedItems === []) {
            return null;
        }

        $preview = array_slice($skippedItems, 0, 5);
        $remaining = count($skippedItems) - count($preview);

        $message = 'Items omitidos: ' . implode(' | ', $preview);
        if ($remaining > 0) {
            $message .= ' | +' . $remaining . ' adicional(es).';
        }

        return $message;
    }

    public function iniciar(): RedirectResponse
    {
        session()->forget(['prestamista', 'asignacion.prestamista_id']);

        return redirect()->route('asignacion.solicitante');
    }

    public function solicitante(): RedirectResponse|View
    {
        $prestamistaId = session('asignacion.prestamista_id');

        if ($prestamistaId) {
            $prestamistaUser = Usuario::query()->find($prestamistaId);

            if ($prestamistaUser) {
                session(['prestamista' => $prestamistaUser->cedula]);

                return redirect()->route('asignacion.tipo-operacion');
            }

            session()->forget(['prestamista', 'asignacion.prestamista_id']);
        }

        return view('asignacion.solicitante');
    }

    public function validarSolicitante(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'cedula' => ['required', 'string', 'max:20'],
        ]);

        $cedulaInput = trim($validated['cedula']);

        if ($cedulaInput === '' || ! ctype_digit($cedulaInput)) {
            return redirect()
                ->route('asignacion.solicitante')
                ->withInput()
                ->with('warning', 'La cedula del solicitante debe contener solo numeros.');
        }

        $cedulaNormalized = preg_replace('/\D+/', '', $cedulaInput) ?? '';

        $prestamistaUser = Usuario::query()
            ->where('cedula', $cedulaInput)
            ->orWhere(function ($query) use ($cedulaNormalized): void {
                if ($cedulaNormalized === '') {
                    return;
                }

                $query->whereRaw("REPLACE(REPLACE(cedula, '.', ''), '-', '') = ?", [$cedulaNormalized]);
            })
            ->first();

        if (! $prestamistaUser) {
            return redirect()
                ->route('asignacion.solicitante')
                ->withInput()
                ->with('warning', 'La cedula del solicitante no existe.');
        }

        session([
            'prestamista' => $prestamistaUser->cedula,
            'asignacion.prestamista_id' => $prestamistaUser->id,
        ]);

        return redirect()
            ->route('asignacion.tipo-operacion')
            ->with('status', 'Solicitante encontrado.');
    }

    public function tipoOperacion(): RedirectResponse|View
    {
        $prestamistaId = session('asignacion.prestamista_id');

        if (! $prestamistaId) {
            return redirect()
                ->route('asignacion.solicitante')
                ->with('warning', 'Primero debe indicar la cedula del solicitante.');
        }

        $prestamistaUser = Usuario::query()->find($prestamistaId);

        if (! $prestamistaUser) {
            session()->forget(['prestamista', 'asignacion.prestamista_id']);

            return redirect()
                ->route('asignacion.solicitante')
                ->with('warning', 'El solicitante seleccionado ya no esta disponible.');
        }

        $prestamista = session('prestamista');

        return view('asignacion.tipo-operacion', compact('prestamistaUser', 'prestamista'));
    }

    public function alimentos(): RedirectResponse|View
    {
        $prestamistaId = session('asignacion.prestamista_id');

        if (! $prestamistaId) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $prestamistaUser = Usuario::query()->find($prestamistaId);

        if (! $prestamistaUser) {
            session()->forget(['prestamista', 'asignacion.prestamista_id']);

            return redirect()->route('asignacion.solicitante')->with('warning', 'El solicitante seleccionado ya no esta disponible.');
        }

        $tipoLabel = $prestamistaUser->roleLabel();
        $bienes = $this->asignacionSupportService->loadBienesEnStock();
        $bienesData = $this->asignacionSupportService->mapBienesData($bienes);
        $solicitudPreload = $this->asignacionSupportService->preloadSolicitudForAlimentos((string) session('prestamista'), $bienesData);

        $embaracaciones = Embarcacion::query()
            ->orderBy('nombre')
            ->get(['id', 'nombre']);

        $destinos = Sitio::query()
            ->orderBy('nombre')
            ->get(['id', 'nombre']);

        $items = session('asignacion.alimentos_items', []);
        $itemPreview = session('asignacion.alimentos_preview');

        return view('asignacion.alimentos', [
            'prestamista' => session('prestamista'),
            'prestamistaUser' => $prestamistaUser,
            'tipoLabel' => $tipoLabel,
            'bienes' => $bienes,
            'bienesData' => $bienesData,
            'embarcaciones' => $embaracaciones,
            'destinos' => $destinos,
            'prefilledItems' => $solicitudPreload['items'],
            'solicitudActiva' => $solicitudPreload['solicitud'],
            'solicitudResumen' => $solicitudPreload['summary'],
        ]);
    }

    public function entregarAlimentos(Request $request): RedirectResponse
    {
        if (! session('prestamista')) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $validated = $request->validate([
            'ubicacion' => [
                'required',
                'integer',
                function (string $attribute, mixed $value, \Closure $fail): void {
                    $id = (int) $value;
                    if ($id <= 0) {
                        $fail('El campo Ubicacion o Destino no puede estar vacio.');
                        return;
                    }
                    $exists = Sitio::query()->where('id', $id)->exists()
                        || Embarcacion::query()->where('id', $id)->exists();
                    if (! $exists) {
                        $fail('La ubicacion o destino debe corresponder a un sitio o embarcacion registrado.');
                    }
                },
            ],
            'items' => ['required', 'array', 'min:1'],
            'items.*.codigo' => ['required', 'string', 'max:50', 'distinct'],
            'items.*.descripcion' => ['required', 'string', 'max:200'],
            'items.*.tipo' => ['required', 'string', 'max:100'],
            'items.*.stock' => ['required', 'string', 'max:100'],
            'items.*.stock_disponible' => ['required', 'integer', 'min:0'],
            'items.*.cantidad' => ['required', 'integer', 'min:1'],
            'items.*.observacion' => ['nullable', 'string', 'max:500'],
            'items.*.nuevo' => ['nullable', 'boolean'],
        ]);

        $items = collect($validated['items'])->values();

        if ($items->isEmpty()) {
            return redirect()
                ->route('asignacion.alimentos')
                ->withInput()
                ->with('warning', 'Debe agregar al menos un item en "Items agregados".');
        }

        $ubicacionId = (int) ($validated['ubicacion'] ?? 0);
        if ($ubicacionId <= 0) {
            return redirect()
                ->route('asignacion.alimentos')
                ->withInput()
                ->with('warning', 'El campo Ubicacion o Destino no puede estar vacio.');
        }

        $editorId = (int) (auth()->id() ?? 0);
        $editor = $editorId > 0 ? Usuario::query()->find($editorId) : null;
        if (! $editor || trim((string) $editor->cedula) === '') {
            return redirect()
                ->route('asignacion.alimentos')
                ->withInput()
                ->with('warning', 'Error en uno de los registros de la base de datos.');
        }

        $cedulaSolicita = (string) session('prestamista');
        $cedulaDespacha = (string) $editor->cedula;
        $cedulaAutoriza = '';
        $resultadoItems = [];
        $contProcesados = 0;
        $contOmitidos = 0;
        $skippedItems = [];
        $fechaOperacion = now();

        try {
            $numeroOperacion = DB::transaction(function () use (
                $items,
                $validated,
                $fechaOperacion,
                $cedulaSolicita,
                $cedulaDespacha,
                $cedulaAutoriza,
                $ubicacionId,
                &$resultadoItems,
                &$contProcesados,
                &$contOmitidos,
                &$skippedItems
            ): int {
                $numOperacion = NumOperacion::query()->lockForUpdate()->first();

                if (! $numOperacion) {
                    $numOperacion = NumOperacion::query()->create(['numero' => 0]);
                }

                $numero = (int) $numOperacion->numero + 1;
                $numOperacion->update(['numero' => $numero]);

                Operacion::query()->create([
                    'numero' => $numero,
                    'tipo' => 1,
                    ...(
                        Schema::hasColumn('operaciones', 'cedulaDespacha')
                            ? ['cedulaDespacha' => $cedulaDespacha]
                            : []
                    ),
                ]);

                foreach ($items as $item) {
                    $codigo = (string) $item['codigo'];
                    $cantidadSolicitada = (int) $item['cantidad'];
                    $cantDifSap = 0;

                    $bien = Bien::query()->where('codigo', $codigo)->lockForUpdate()->first();

                    if (! $bien) {
                        $contOmitidos++;
                        $skippedItems[] = $codigo . ': item no encontrado.';
                        continue;
                    }

                    $stockAntes = (int) ($bien->cantidadUnidadUtil ?? 0);
                    if ($cantidadSolicitada > $stockAntes) {
                        $contOmitidos++;
                        $skippedItems[] = $codigo . ': stock insuficiente (solicitado ' . $cantidadSolicitada . ', disponible ' . $stockAntes . ').';
                        continue;
                    }

                    $cantidadUnidadAntes = (int) ($bien->cantidadUnidad ?? 0);
                    $stockDespues = $stockAntes - $cantidadSolicitada;
                    $relacion = (int) ($bien->relacionUnidades ?? 0);
                    $fuenteDespues = $relacion > 0
                        ? (int) floor($stockDespues / $relacion)
                        : $cantidadUnidadAntes;

                    $difFuente = $cantidadUnidadAntes - $fuenteDespues;
                    $cantidadSap = Schema::hasColumn('bienes', 'cantidadSap')
                        ? (int) ($bien->getAttribute('cantidadSap') ?? 0)
                        : 0;
                    $cantidadRotacion = Schema::hasColumn('bienes', 'cantidadRotacion')
                        ? (int) ($bien->getAttribute('cantidadRotacion') ?? 0)
                        : 0;
                    $nuevo = (bool) ($item['nuevo'] ?? false);

                    if (Schema::hasColumn('bienes', 'cantidadSap') && Schema::hasColumn('bienes', 'cantidadRotacion')) {
                        if ($nuevo) {
                            if ($difFuente <= $cantidadSap) {
                                $cantDifSap = $difFuente;
                                $cantidadSap -= $difFuente;
                            } else {
                                $cantDifSap = $cantidadSap;
                                $restante = $difFuente - $cantidadSap;
                                $cantidadSap = 0;
                                $cantidadRotacion -= $restante;
                            }
                        } else {
                            if ($difFuente <= $cantidadRotacion) {
                                $cantidadRotacion -= $difFuente;
                            } else {
                                $restante = $difFuente - $cantidadRotacion;
                                $cantidadRotacion = 0;
                                $cantDifSap = $restante;
                                $cantidadSap -= $restante;
                            }
                        }

                        if ($cantidadSap < 0 || $cantidadRotacion < 0) {
                            $contOmitidos++;
                            $skippedItems[] = $codigo . ': saldo SAP/rotacion insuficiente para la salida.';
                            continue;
                        }
                    }

                    $entregadoAntes = (int) ($bien->cantidadEntregada ?? 0);
                    $entregadoDespues = $entregadoAntes + $cantidadSolicitada;

                    $this->inventoryCostingService->consumeSalida(
                        bien: $bien,
                        cantidad: $cantidadSolicitada,
                        numOperacion: $numero,
                        referencia: 'Asignacion alimentos',
                        fechaMovimiento: $fechaOperacion,
                        cedulaResponsable: $cedulaDespacha,
                    );

                    $updateBien = [
                        'cantidadUnidadUtil' => $stockDespues,
                        'cantidadUnidad' => $fuenteDespues,
                        'cantidadEntregada' => $entregadoDespues,
                        'fechaActualizacion' => $fechaOperacion,
                        'cedulaEditor' => $cedulaDespacha,
                    ];

                    if (Schema::hasColumn('bienes', 'cantidadSap')) {
                        $updateBien['cantidadSap'] = $cantidadSap;
                    }

                    if (Schema::hasColumn('bienes', 'cantidadRotacion')) {
                        $updateBien['cantidadRotacion'] = $cantidadRotacion;
                    }

                    $bien->update($updateBien);

                    $observacionRow = trim((string) ($item['observacion'] ?? ''));
                    $observacionMovimiento = 'Asignacion alimentos - Ubicacion o destino: ' . $ubicacionId
                        . ($observacionRow !== '' ? '; Obs: ' . $observacionRow : '');

                    Prestamo::query()->create([
                        'numOperacion' => $numero,
                        'codigo' => $codigo,
                        'cantidad' => $cantidadSolicitada,
                        'status' => 1,
                        'cedulaSolicita' => $cedulaSolicita,
                        'cedulaDespacha' => $cedulaDespacha,
                        'cedulaAutoriza' => $cedulaAutoriza,
                        'fechaEntrega' => $fechaOperacion,
                        'fechaDevolucion' => null,
                        'cedulaRecibeDev' => null,
                        'ubicacion' => $ubicacionId,
                        'observacion' => $observacionRow !== '' ? $observacionRow : null,
                    ]);

                    RegistroStock::query()->create([
                        'numOperacion' => $numero,
                        'codigo' => $codigo,
                        'cantidad' => $cantidadSolicitada,
                        'objetivo' => 1,
                        'fechaOperacion' => $fechaOperacion,
                        'observacion' => $observacionMovimiento,
                        'cedulaResponsable' => $cedulaDespacha,
                    ]);

                    if (
                        $cantDifSap > 0
                        && Schema::hasTable('salidasap')
                        && Schema::hasColumn('salidasap', 'sap')
                        && Schema::hasColumn('salidasap', 'cantidad')
                        && Schema::hasColumn('salidasap', 'ubicacion')
                        && Schema::hasColumn('salidasap', 'nombreSolicita')
                        && Schema::hasColumn('salidasap', 'nombreAutoriza')
                        && Schema::hasColumn('salidasap', 'numOperacion')
                    ) {
                        $solicitanteNombre = (string) (Usuario::query()->where('cedula', $cedulaSolicita)->value('nombre') ?? '');
                        $autorizaNombre = '';

                        if ($cedulaAutoriza !== '') {
                            $autorizaNombre = (string) (Usuario::query()->where('cedula', $cedulaAutoriza)->value('nombre') ?? '');
                        }

                        DB::table('salidasap')->insert([
                            'sap' => (string) ($bien->sap ?? ''),
                            'cantidad' => $cantDifSap,
                            'ubicacion' => $ubicacionId,
                            'nombreSolicita' => $solicitanteNombre,
                            'nombreAutoriza' => $autorizaNombre,
                            'numOperacion' => $numero,
                        ]);
                    }

                    $contProcesados++;

                    $resultadoItems[] = [
                        'codigo' => $codigo,
                        'descripcion' => (string) ($bien->descripcion ?? $item['descripcion'] ?? ''),
                        'cantidad' => $cantidadSolicitada,
                        'stock_antes' => $stockAntes,
                        'stock_despues' => $stockDespues,
                        'fuente_despues' => $fuenteDespues,
                    ];
                }

                if ($contProcesados === 0) {
                    throw new \RuntimeException('NO_ITEMS_DELIVERED');
                }

                return $numero;
            });
        } catch (\RuntimeException $exception) {
            if ($exception->getMessage() === 'NO_ITEMS_DELIVERED') {
                $skipWarning = $this->buildSkippedItemsWarning($skippedItems);
                return redirect()
                    ->route('asignacion.alimentos')
                    ->withInput()
                    ->with('warning', 'No se pudo generar la entrega porque todos los items fueron omitidos.' . ($skipWarning ? ' ' . $skipWarning : ''));
            }

            throw $exception;
        } catch (\Throwable $exception) {
            Log::error('Asignacion alimentos failed', [
                'user_id' => auth()->id(),
                'exception' => $exception,
            ]);

            return redirect()
                ->route('asignacion.alimentos')
                ->withInput()
                ->with('warning', 'Ocurrio un error interno al procesar la asignacion. Intente nuevamente.');
        }

        $response = redirect()
            ->route('asignacion.alimentos')
            ->with('status', 'Listo. ' . $contProcesados . ' elementos fueron entregados correctamente y ' . $contOmitidos . ' fueron omitidos por falta de stock.')
            ->with('operacion_resultado', [
                'tipo' => 'alimentos',
                'numero' => $numeroOperacion,
                'receipt_url' => route('asignacion.operacion.show', ['tipo' => 'alimentos', 'numero' => $numeroOperacion]),
                'export_url' => route('asignacion.operacion.export', ['tipo' => 'alimentos', 'numero' => $numeroOperacion]),
                'ubicacion' => $ubicacionId,
                'total_items' => $items->count(),
                'procesados' => $contProcesados,
                'omitidos' => $contOmitidos,
                'items' => $resultadoItems,
            ])
            ->with('clear_alimentos_cart', true);

        $skipWarning = $this->buildSkippedItemsWarning($skippedItems);
        if ($skipWarning) {
            $response->with('warning', $skipWarning);
        }

        return $response;
    }

    public function transferencia(): RedirectResponse|View
    {
        $prestamistaId = session('asignacion.prestamista_id');

        if (! $prestamistaId) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $prestamistaUser = Usuario::query()->find($prestamistaId);

        if (! $prestamistaUser) {
            session()->forget(['prestamista', 'asignacion.prestamista_id']);

            return redirect()->route('asignacion.solicitante')->with('warning', 'El solicitante seleccionado ya no esta disponible.');
        }

        $tipoLabel = $prestamistaUser->roleLabel();
        $bienes = $this->asignacionSupportService->loadBienesEnStock();
        $bienesData = $this->asignacionSupportService->mapBienesData($bienes);

        $personal = Usuario::query()
            ->orderByRaw("CAST(REPLACE(REPLACE(cedula, '.', ''), '-', '') AS UNSIGNED) ASC")
            ->orderBy('cedula')
            ->get(['cedula', 'nombre']);

        $embarcaciones = Embarcacion::query()
            ->orderBy('nombre')
            ->get(['id', 'nombre']);

        $destinos = Sitio::query()
            ->orderBy('nombre')
            ->get(['id', 'nombre']);

        return view('asignacion.transferencia', [
            'prestamista' => session('prestamista'),
            'prestamistaUser' => $prestamistaUser,
            'tipoLabel' => $tipoLabel,
            'bienes' => $bienes,
            'bienesData' => $bienesData,
            'personal' => $personal,
            'embarcaciones' => $embarcaciones,
            'destinos' => $destinos,
        ]);
    }

    public function dotacion(): RedirectResponse|View
    {
        $prestamistaId = session('asignacion.prestamista_id');

        if (! $prestamistaId) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $prestamistaUser = Usuario::query()->find($prestamistaId);

        if (! $prestamistaUser) {
            session()->forget(['prestamista', 'asignacion.prestamista_id']);

            return redirect()->route('asignacion.solicitante')->with('warning', 'El solicitante seleccionado ya no esta disponible.');
        }

        $tipoLabel = $prestamistaUser->roleLabel();
        $bienes = $this->asignacionSupportService->loadBienesEnStock();
        $bienesData = $this->asignacionSupportService->mapBienesData($bienes);
        $embarcaciones = Embarcacion::query()->orderBy('nombre')->get(['id', 'nombre']);
        $destinos = Sitio::query()->orderBy('nombre')->get(['id', 'nombre']);

        return view('asignacion.dotacion', [
            'prestamista' => session('prestamista'),
            'prestamistaUser' => $prestamistaUser,
            'tipoLabel' => $tipoLabel,
            'bienes' => $bienes,
            'bienesData' => $bienesData,
            'embarcaciones' => $embarcaciones,
            'destinos' => $destinos,
        ]);
    }

    public function entregarDotacion(Request $request): RedirectResponse
    {
        if (! session('prestamista')) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $validated = $request->validate([
            'items' => ['required', 'array', 'min:1'],
            'items.*.codigo' => ['required', 'string', 'max:50', 'distinct'],
            'items.*.descripcion' => ['required', 'string', 'max:200'],
            'items.*.tipo' => ['required', 'string', 'max:100'],
            'items.*.stock' => ['required', 'string', 'max:100'],
            'items.*.stock_disponible' => ['required', 'integer', 'min:0'],
            'items.*.cantidad' => ['required', 'integer', 'min:1'],
            'items.*.observacion' => ['nullable', 'string', 'max:500'],
        ]);

        $items = collect($validated['items'])->values();

        $editorId = (int) (auth()->id() ?? 0);
        $editor = $editorId > 0 ? Usuario::query()->find($editorId) : null;
        if (! $editor || trim((string) $editor->cedula) === '') {
            return redirect()
                ->route('asignacion.dotacion')
                ->withInput()
                ->with('warning', 'Error en uno de los registros de la base de datos.');
        }

        $cedulaSolicita = (string) session('prestamista');
        $cedulaDespacha = (string) $editor->cedula;
        $cedulaAutoriza = '';
        $resultadoItems = [];
        $contProcesados = 0;
        $contOmitidos = 0;
        $skippedItems = [];
        $fechaOperacion = now();
        $ubicacionDotacion = (int) ($request->input('ubicacion') ?? 0);
        if ($ubicacionDotacion <= 0) {
            return redirect()->route('asignacion.dotacion')->withInput()->with('warning', 'El campo Ubicacion o Destino no puede estar vacio.');
        }

        try {
            $numeroOperacion = DB::transaction(function () use (
                $items,
                $fechaOperacion,
                $cedulaSolicita,
                $cedulaDespacha,
                $cedulaAutoriza,
                $ubicacionDotacion,
                &$resultadoItems,
                &$contProcesados,
                &$contOmitidos,
                &$skippedItems
            ): int {
                $numOperacion = NumOperacion::query()->lockForUpdate()->first();

                if (! $numOperacion) {
                    $numOperacion = NumOperacion::query()->create(['numero' => 0]);
                }

                $numero = (int) $numOperacion->numero + 1;
                $numOperacion->update(['numero' => $numero]);

                Operacion::query()->create([
                    'numero' => $numero,
                    'tipo' => 2,
                    ...(
                        Schema::hasColumn('operaciones', 'cedulaDespacha')
                            ? ['cedulaDespacha' => $cedulaDespacha]
                            : []
                    ),
                ]);

                foreach ($items as $item) {
                    $codigo = (string) $item['codigo'];
                    $cantidadSolicitada = (int) $item['cantidad'];
                    $cantDifSap = 0;

                    $bien = Bien::query()->where('codigo', $codigo)->lockForUpdate()->first();

                    if (! $bien) {
                        $contOmitidos++;
                        $skippedItems[] = $codigo . ': item no encontrado.';
                        continue;
                    }

                    $stockAntes = (int) ($bien->cantidadUnidadUtil ?? 0);
                    if ($cantidadSolicitada > $stockAntes) {
                        $contOmitidos++;
                        $skippedItems[] = $codigo . ': stock insuficiente (solicitado ' . $cantidadSolicitada . ', disponible ' . $stockAntes . ').';
                        continue;
                    }

                    $cantidadUnidadAntes = (int) ($bien->cantidadUnidad ?? 0);
                    $stockDespues = $stockAntes - $cantidadSolicitada;
                    $relacion = (int) ($bien->relacionUnidades ?? 0);
                    $fuenteDespues = $relacion > 0
                        ? (int) floor($stockDespues / $relacion)
                        : $cantidadUnidadAntes;

                    $difFuente = $cantidadUnidadAntes - $fuenteDespues;
                    $cantidadSap = Schema::hasColumn('bienes', 'cantidadSap')
                        ? (int) ($bien->getAttribute('cantidadSap') ?? 0)
                        : 0;
                    $cantidadRotacion = Schema::hasColumn('bienes', 'cantidadRotacion')
                        ? (int) ($bien->getAttribute('cantidadRotacion') ?? 0)
                        : 0;

                    if (Schema::hasColumn('bienes', 'cantidadSap') && Schema::hasColumn('bienes', 'cantidadRotacion')) {
                        if ($difFuente <= $cantidadSap) {
                            $cantDifSap = $difFuente;
                            $cantidadSap -= $difFuente;
                        } else {
                            $cantDifSap = $cantidadSap;
                            $restante = $difFuente - $cantidadSap;
                            $cantidadSap = 0;
                            $cantidadRotacion -= $restante;
                        }

                        if ($cantidadSap < 0 || $cantidadRotacion < 0) {
                            $contOmitidos++;
                            $skippedItems[] = $codigo . ': saldo SAP/rotacion insuficiente para la salida.';
                            continue;
                        }
                    }

                    $entregadoAntes = (int) ($bien->cantidadEntregada ?? 0);
                    $entregadoDespues = $entregadoAntes + $cantidadSolicitada;

                    $this->inventoryCostingService->consumeSalida(
                        bien: $bien,
                        cantidad: $cantidadSolicitada,
                        numOperacion: $numero,
                        referencia: 'Asignacion dotacion',
                        fechaMovimiento: $fechaOperacion,
                        cedulaResponsable: $cedulaDespacha,
                    );

                    $updateBien = [
                        'cantidadUnidadUtil' => $stockDespues,
                        'cantidadUnidad' => $fuenteDespues,
                        'cantidadEntregada' => $entregadoDespues,
                        'fechaActualizacion' => $fechaOperacion,
                        'cedulaEditor' => $cedulaDespacha,
                    ];

                    if (Schema::hasColumn('bienes', 'cantidadSap')) {
                        $updateBien['cantidadSap'] = $cantidadSap;
                    }

                    if (Schema::hasColumn('bienes', 'cantidadRotacion')) {
                        $updateBien['cantidadRotacion'] = $cantidadRotacion;
                    }

                    $bien->update($updateBien);

                    Dotacion::query()->create([
                        'numOperacion' => $numero,
                        'codigo' => $codigo,
                        'cantidad' => $cantidadSolicitada,
                        'status' => 1,
                        'cedulaSolicita' => $cedulaSolicita,
                        'cedulaDespacha' => $cedulaDespacha,
                        'cedulaAutoriza' => $cedulaAutoriza,
                        'fechaEntrega' => $fechaOperacion,
                        'razon' => 1,
                    ]);

                    if (
                        $cantDifSap > 0
                        && Schema::hasTable('salidasap')
                        && Schema::hasColumn('salidasap', 'sap')
                        && Schema::hasColumn('salidasap', 'cantidad')
                        && Schema::hasColumn('salidasap', 'ubicacion')
                        && Schema::hasColumn('salidasap', 'nombreSolicita')
                        && Schema::hasColumn('salidasap', 'nombreAutoriza')
                        && Schema::hasColumn('salidasap', 'numOperacion')
                    ) {
                        $solicitanteNombre = (string) (Usuario::query()->where('cedula', $cedulaSolicita)->value('nombre') ?? '');
                        $autorizaNombre = '';

                        if ($cedulaAutoriza !== '') {
                            $autorizaNombre = (string) (Usuario::query()->where('cedula', $cedulaAutoriza)->value('nombre') ?? '');
                        }

                        DB::table('salidasap')->insert([
                            'sap' => (string) ($bien->sap ?? ''),
                            'cantidad' => $cantDifSap,
                            'ubicacion' => $ubicacionDotacion,
                            'nombreSolicita' => $solicitanteNombre,
                            'nombreAutoriza' => $autorizaNombre,
                            'numOperacion' => $numero,
                        ]);
                    }

                    $contProcesados++;

                    $resultadoItems[] = [
                        'codigo' => $codigo,
                        'descripcion' => (string) ($bien->descripcion ?? $item['descripcion'] ?? ''),
                        'cantidad' => $cantidadSolicitada,
                        'stock_antes' => $stockAntes,
                        'stock_despues' => $stockDespues,
                        'fuente_despues' => $fuenteDespues,
                    ];
                }

                if ($contProcesados === 0) {
                    throw new \RuntimeException('NO_ITEMS_DELIVERED');
                }

                return $numero;
            });
        } catch (\RuntimeException $exception) {
            if ($exception->getMessage() === 'NO_ITEMS_DELIVERED') {
                $skipWarning = $this->buildSkippedItemsWarning($skippedItems);
                return redirect()
                    ->route('asignacion.dotacion')
                    ->withInput()
                    ->with('warning', 'No se pudo generar la entrega porque todos los items fueron omitidos.' . ($skipWarning ? ' ' . $skipWarning : ''));
            }

            throw $exception;
        } catch (\Throwable $exception) {
            Log::error('Asignacion dotacion failed', [
                'user_id' => auth()->id(),
                'exception' => $exception,
            ]);

            return redirect()
                ->route('asignacion.dotacion')
                ->withInput()
                ->with('warning', 'Ocurrio un error interno al procesar la dotacion. Intente nuevamente.');
        }

        $response = redirect()
            ->route('asignacion.dotacion')
            ->with('status', 'Listo. ' . $contProcesados . ' elementos fueron entregados correctamente y ' . $contOmitidos . ' fueron omitidos por falta de stock.')
            ->with('operacion_resultado', [
                'tipo' => 'dotacion',
                'numero' => $numeroOperacion,
                'receipt_url' => route('asignacion.operacion.show', ['tipo' => 'dotacion', 'numero' => $numeroOperacion]),
                'export_url' => route('asignacion.operacion.export', ['tipo' => 'dotacion', 'numero' => $numeroOperacion]),
                'total_items' => $items->count(),
                'procesados' => $contProcesados,
                'omitidos' => $contOmitidos,
                'items' => $resultadoItems,
            ])
            ->with('clear_dotacion_cart', true);

        $skipWarning = $this->buildSkippedItemsWarning($skippedItems);
        if ($skipWarning) {
            $response->with('warning', $skipWarning);
        }

        return $response;
    }

    public function entregarTransferencia(Request $request): RedirectResponse
    {
        if (! session('prestamista')) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $validated = $request->validate([
            'cedulaTransportista' => ['required', 'string', 'max:20', Rule::exists('users', 'cedula')],
            'emisor' => ['required', 'string', 'max:20', Rule::exists('users', 'cedula')],
            'receptor' => ['required', 'string', 'max:20', Rule::exists('users', 'cedula')],
            'ubicacion' => [
                'required',
                'integer',
                function (string $attribute, mixed $value, \Closure $fail): void {
                    $id = (int) $value;
                    if ($id <= 0) {
                        $fail('El campo Ubicacion o Destino no puede estar vacio.');
                        return;
                    }
                    $exists = Sitio::query()->where('id', $id)->exists()
                        || Embarcacion::query()->where('id', $id)->exists();
                    if (! $exists) {
                        $fail('La ubicacion o destino debe corresponder a un sitio o embarcacion registrado.');
                    }
                },
            ],
            'items' => ['required', 'array', 'min:1'],
            'items.*.codigo' => ['required', 'string', 'max:50', 'distinct'],
            'items.*.descripcion' => ['required', 'string', 'max:200'],
            'items.*.tipo' => ['required', 'string', 'max:100'],
            'items.*.stock' => ['required', 'string', 'max:100'],
            'items.*.stock_disponible' => ['required', 'integer', 'min:0'],
            'items.*.cantidad' => ['required', 'integer', 'min:1'],
            'items.*.observacion' => ['nullable', 'string', 'max:500'],
            'items.*.nuevo' => ['nullable', 'boolean'],
        ]);

        $items = collect($validated['items'])->values();
        $ubicacionId = (int) ($validated['ubicacion'] ?? 0);
        if ($ubicacionId <= 0) {
            return redirect()->route('asignacion.transferencia')->withInput()->with('warning', 'El campo Ubicacion o Destino no puede estar vacio.');
        }

        if ($ubicacionId === 0) {
            return redirect()
                ->route('asignacion.transferencia')
                ->withInput()
                ->with('warning', 'El campo Ubicacion o Destino no puede estar vacio.');
        }

        $editorId = (int) (auth()->id() ?? 0);
        $editor = $editorId > 0 ? Usuario::query()->find($editorId) : null;
        if (! $editor || trim((string) $editor->cedula) === '') {
            return redirect()
                ->route('asignacion.transferencia')
                ->withInput()
                ->with('warning', 'Error en uno de los registros de la base de datos.');
        }

        $cedulaSolicita = (string) session('prestamista');
        $cedulaDespacha = (string) $editor->cedula;
        $cedulaAutoriza = '';
        $cedulaTransportista = (string) $validated['cedulaTransportista'];
        $cedulaEmisor = (string) $validated['emisor'];
        $cedulaReceptor = (string) $validated['receptor'];
        $resultadoItems = [];
        $contProcesados = 0;
        $contOmitidos = 0;
        $skippedItems = [];
        $fechaOperacion = now();

        try {
            $numeroOperacion = DB::transaction(function () use (
                $items,
                $fechaOperacion,
                $cedulaSolicita,
                $cedulaDespacha,
                $cedulaAutoriza,
                $cedulaTransportista,
                $cedulaEmisor,
                $cedulaReceptor,
                $ubicacionId,
                &$resultadoItems,
                &$contProcesados,
                &$contOmitidos,
                &$skippedItems
            ): int {
                $numOperacion = NumOperacion::query()->lockForUpdate()->first();

                if (! $numOperacion) {
                    $numOperacion = NumOperacion::query()->create(['numero' => 0]);
                }

                $numero = (int) $numOperacion->numero + 1;
                $numOperacion->update(['numero' => $numero]);

                Operacion::query()->create([
                    'numero' => $numero,
                    'tipo' => 3,
                    ...(
                        Schema::hasColumn('operaciones', 'cedulaDespacha')
                            ? ['cedulaDespacha' => $cedulaDespacha]
                            : []
                    ),
                ]);

                foreach ($items as $item) {
                    $codigo = (string) $item['codigo'];
                    $cantidadSolicitada = (int) $item['cantidad'];
                    $cantDifSap = 0;

                    $bien = Bien::query()->where('codigo', $codigo)->lockForUpdate()->first();

                    if (! $bien) {
                        $contOmitidos++;
                        $skippedItems[] = $codigo . ': item no encontrado.';
                        continue;
                    }

                    $stockAntes = (int) ($bien->cantidadUnidadUtil ?? 0);
                    if ($cantidadSolicitada > $stockAntes) {
                        $contOmitidos++;
                        $skippedItems[] = $codigo . ': stock insuficiente (solicitado ' . $cantidadSolicitada . ', disponible ' . $stockAntes . ').';
                        continue;
                    }

                    $cantidadUnidadAntes = (int) ($bien->cantidadUnidad ?? 0);
                    $stockDespues = $stockAntes - $cantidadSolicitada;
                    $relacion = (int) ($bien->relacionUnidades ?? 0);
                    $fuenteDespues = $relacion > 0
                        ? (int) floor($stockDespues / $relacion)
                        : $cantidadUnidadAntes;

                    $difFuente = $cantidadUnidadAntes - $fuenteDespues;
                    $cantidadSap = Schema::hasColumn('bienes', 'cantidadSap')
                        ? (int) ($bien->getAttribute('cantidadSap') ?? 0)
                        : 0;
                    $cantidadRotacion = Schema::hasColumn('bienes', 'cantidadRotacion')
                        ? (int) ($bien->getAttribute('cantidadRotacion') ?? 0)
                        : 0;
                    $nuevo = (bool) ($item['nuevo'] ?? false);

                    if (Schema::hasColumn('bienes', 'cantidadSap') && Schema::hasColumn('bienes', 'cantidadRotacion')) {
                        if ($nuevo) {
                            if ($difFuente <= $cantidadSap) {
                                $cantDifSap = $difFuente;
                                $cantidadSap -= $difFuente;
                            } else {
                                $cantDifSap = $cantidadSap;
                                $restante = $difFuente - $cantidadSap;
                                $cantidadSap = 0;
                                $cantidadRotacion -= $restante;
                            }
                        } else {
                            if ($difFuente <= $cantidadRotacion) {
                                $cantidadRotacion -= $difFuente;
                            } else {
                                $restante = $difFuente - $cantidadRotacion;
                                $cantidadRotacion = 0;
                                $cantDifSap = $restante;
                                $cantidadSap -= $restante;
                            }
                        }

                        if ($cantidadSap < 0 || $cantidadRotacion < 0) {
                            $contOmitidos++;
                            $skippedItems[] = $codigo . ': saldo SAP/rotacion insuficiente para la salida.';
                            continue;
                        }
                    }

                    $entregadoAntes = (int) ($bien->cantidadEntregada ?? 0);
                    $entregadoDespues = $entregadoAntes + $cantidadSolicitada;

                    $this->inventoryCostingService->consumeSalida(
                        bien: $bien,
                        cantidad: $cantidadSolicitada,
                        numOperacion: $numero,
                        referencia: 'Asignacion transferencia',
                        fechaMovimiento: $fechaOperacion,
                        cedulaResponsable: $cedulaDespacha,
                    );

                    $updateBien = [
                        'cantidadUnidadUtil' => $stockDespues,
                        'cantidadUnidad' => $fuenteDespues,
                        'cantidadEntregada' => $entregadoDespues,
                        'fechaActualizacion' => $fechaOperacion,
                        'cedulaEditor' => $cedulaDespacha,
                    ];

                    if (Schema::hasColumn('bienes', 'cantidadSap')) {
                        $updateBien['cantidadSap'] = $cantidadSap;
                    }

                    if (Schema::hasColumn('bienes', 'cantidadRotacion')) {
                        $updateBien['cantidadRotacion'] = $cantidadRotacion;
                    }

                    $bien->update($updateBien);

                    $observacionRow = trim((string) ($item['observacion'] ?? ''));

                    Transferencia::query()->create([
                        'numOperacion' => $numero,
                        'codigo' => $codigo,
                        'cantidad' => $cantidadSolicitada,
                        'status' => 1,
                        'cedulaSolicita' => $cedulaSolicita,
                        'cedulaDespacha' => $cedulaDespacha,
                        'cedulaAutoriza' => $cedulaAutoriza,
                        'cedulaTransportista' => $cedulaTransportista,
                        'fechaEntrega' => $fechaOperacion,
                        'fechaDevolucion' => null,
                        'cedulaRecibeDev' => null,
                        'emisor' => $cedulaEmisor,
                        'receptor' => $cedulaReceptor,
                        'ubicacion' => $ubicacionId,
                        'observacion' => $observacionRow !== '' ? $observacionRow : null,
                    ]);

                    if (
                        $cantDifSap > 0
                        && Schema::hasTable('salidasap')
                        && Schema::hasColumn('salidasap', 'sap')
                        && Schema::hasColumn('salidasap', 'cantidad')
                        && Schema::hasColumn('salidasap', 'ubicacion')
                        && Schema::hasColumn('salidasap', 'nombreSolicita')
                        && Schema::hasColumn('salidasap', 'nombreAutoriza')
                        && Schema::hasColumn('salidasap', 'numOperacion')
                    ) {
                        $solicitanteNombre = (string) (Usuario::query()->where('cedula', $cedulaSolicita)->value('nombre') ?? '');
                        $autorizaNombre = '';

                        if ($cedulaAutoriza !== '') {
                            $autorizaNombre = (string) (Usuario::query()->where('cedula', $cedulaAutoriza)->value('nombre') ?? '');
                        }

                        DB::table('salidasap')->insert([
                            'sap' => (string) ($bien->sap ?? ''),
                            'cantidad' => $cantDifSap,
                            'ubicacion' => $ubicacionId,
                            'nombreSolicita' => $solicitanteNombre,
                            'nombreAutoriza' => $autorizaNombre,
                            'numOperacion' => $numero,
                        ]);
                    }

                    $contProcesados++;

                    $resultadoItems[] = [
                        'codigo' => $codigo,
                        'descripcion' => (string) ($bien->descripcion ?? $item['descripcion'] ?? ''),
                        'cantidad' => $cantidadSolicitada,
                        'stock_antes' => $stockAntes,
                        'stock_despues' => $stockDespues,
                        'fuente_despues' => $fuenteDespues,
                    ];
                }

                if ($contProcesados === 0) {
                    throw new \RuntimeException('NO_ITEMS_DELIVERED');
                }

                return $numero;
            });
        } catch (\RuntimeException $exception) {
            if ($exception->getMessage() === 'NO_ITEMS_DELIVERED') {
                $skipWarning = $this->buildSkippedItemsWarning($skippedItems);
                return redirect()
                    ->route('asignacion.transferencia')
                    ->withInput()
                    ->with('warning', 'No se pudo generar la entrega porque todos los items fueron omitidos.' . ($skipWarning ? ' ' . $skipWarning : ''));
            }

            throw $exception;
        } catch (\Throwable $exception) {
            Log::error('Asignacion transferencia failed', [
                'user_id' => auth()->id(),
                'exception' => $exception,
            ]);

            return redirect()
                ->route('asignacion.transferencia')
                ->withInput()
                ->with('warning', 'Ocurrio un error interno al procesar la transferencia. Intente nuevamente.');
        }

        $personalNombres = Usuario::query()
            ->whereIn('cedula', [$cedulaTransportista, $cedulaEmisor, $cedulaReceptor])
            ->pluck('nombre', 'cedula');

        $response = redirect()
            ->route('asignacion.transferencia')
            ->with('status', 'Listo. ' . $contProcesados . ' elementos fueron entregados correctamente y ' . $contOmitidos . ' fueron omitidos por falta de stock.')
            ->with('operacion_resultado', [
                'tipo' => 'transferencia',
                'numero' => $numeroOperacion,
                'receipt_url' => route('asignacion.operacion.show', ['tipo' => 'transferencia', 'numero' => $numeroOperacion]),
                'export_url' => route('asignacion.operacion.export', ['tipo' => 'transferencia', 'numero' => $numeroOperacion]),
                'ubicacion' => $ubicacionId,
                'transportista' => $cedulaTransportista,
                'transportista_nombre' => (string) ($personalNombres[$cedulaTransportista] ?? ''),
                'emisor' => $cedulaEmisor,
                'emisor_nombre' => (string) ($personalNombres[$cedulaEmisor] ?? ''),
                'receptor' => $cedulaReceptor,
                'receptor_nombre' => (string) ($personalNombres[$cedulaReceptor] ?? ''),
                'total_items' => $items->count(),
                'procesados' => $contProcesados,
                'omitidos' => $contOmitidos,
                'items' => $resultadoItems,
            ])
            ->with('clear_transferencia_cart', true);

        $skipWarning = $this->buildSkippedItemsWarning($skippedItems);
        if ($skipWarning) {
            $response->with('warning', $skipWarning);
        }

        return $response;
    }

    public function devolucion(): RedirectResponse
    {
        return redirect()->route('asignacion.devolucion.prestamos');
    }

    public function devolucionPrestamos(): RedirectResponse|View
    {
        return $this->renderDevolucionByScope('prestamos');
    }

    public function devolucionTransferencias(): RedirectResponse|View
    {
        return $this->renderDevolucionByScope('transferencias');
    }

    public function procesarDevolucionPrestamos(Request $request): RedirectResponse
    {
        return $this->procesarDevolucionPorScope($request, 'prestamos');
    }

    public function procesarDevolucionTransferencias(Request $request): RedirectResponse
    {
        return $this->procesarDevolucionPorScope($request, 'transferencias');
    }

    private function renderDevolucionByScope(string $scope): RedirectResponse|View
    {
        $prestamistaId = session('asignacion.prestamista_id');

        if (! $prestamistaId) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $prestamistaUser = Usuario::query()->find($prestamistaId);

        if (! $prestamistaUser) {
            session()->forget(['prestamista', 'asignacion.prestamista_id']);

            return redirect()->route('asignacion.solicitante')->with('warning', 'El solicitante seleccionado ya no esta disponible.');
        }

        $tipoLabel = $prestamistaUser->roleLabel();
        $cedulaSolicita = (string) session('prestamista');

        $prestamosActivos = Prestamo::query()
            ->where('cedulaSolicita', $cedulaSolicita)
            ->where('status', 1)
            ->orderBy('numOperacion')
            ->orderBy('id')
            ->get();

        $transferenciasActivas = Transferencia::query()
            ->where('cedulaSolicita', $cedulaSolicita)
            ->where('status', 1)
            ->orderBy('numOperacion')
            ->orderBy('id')
            ->get();

        $codigos = $prestamosActivos->pluck('codigo')
            ->merge($transferenciasActivas->pluck('codigo'))
            ->filter()
            ->unique()
            ->values();

        $bienes = Bien::query()
            ->whereIn('codigo', $codigos)
            ->get(['codigo', 'descripcion', 'marca', 'tipo'])
            ->keyBy('codigo');

        $referenciasPersonal = Usuario::query()
            ->whereIn('cedula', $transferenciasActivas->pluck('emisor')->merge($transferenciasActivas->pluck('receptor'))->filter()->unique())
            ->get(['cedula', 'nombre'])
            ->keyBy('cedula');

        $prestamos = $prestamosActivos
            ->map(function (Prestamo $prestamo) use ($bienes): ?array {
                $bien = $bienes->get((string) $prestamo->codigo);

                if (! $bien || (int) ($bien->tipo ?? 0) > 2) {
                    return null;
                }

                return [
                    'id' => (int) $prestamo->id,
                    'num_operacion' => (int) ($prestamo->numOperacion ?? 0),
                    'codigo' => (string) ($prestamo->codigo ?? ''),
                    'descripcion' => (string) ($bien->descripcion ?? ''),
                    'cantidad' => (int) ($prestamo->cantidad ?? 0),
                    'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                    'fecha_entrega' => $this->asignacionSupportService->formatFechaCorta($prestamo->fechaEntrega),
                    'ubicacion' => (string) ($prestamo->ubicacion ?? ''),
                    'observacion' => (string) ($prestamo->observacion ?? ''),
                ];
            })
            ->filter()
            ->values();

        $transferencias = $transferenciasActivas
            ->map(function (Transferencia $transferencia) use ($bienes, $referenciasPersonal): ?array {
                $bien = $bienes->get((string) $transferencia->codigo);

                if (! $bien) {
                    return null;
                }

                $tipo = (int) ($bien->tipo ?? 0);

                return [
                    'id' => (int) $transferencia->id,
                    'num_operacion' => (int) ($transferencia->numOperacion ?? 0),
                    'codigo' => (string) ($transferencia->codigo ?? ''),
                    'descripcion' => (string) ($bien->descripcion ?? ''),
                    'cantidad' => (int) ($transferencia->cantidad ?? 0),
                    'tipo' => $this->asignacionSupportService->formatBienTipoMarca($tipo, $bien->marca),
                    'tipo_id' => $tipo,
                    'fecha_entrega' => $this->asignacionSupportService->formatFechaCorta($transferencia->fechaEntrega),
                    'emisor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->emisor ?? ''), $referenciasPersonal->all()),
                    'receptor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->receptor ?? ''), $referenciasPersonal->all()),
                    'ubicacion' => (string) ($transferencia->ubicacion ?? ''),
                    'observacion' => (string) ($transferencia->observacion ?? ''),
                    'puede_utilizarse' => $tipo > 2,
                ];
            })
            ->filter()
            ->values();

        $scope = $scope === 'transferencias' ? 'transferencias' : 'prestamos';

        return view('asignacion.devolucion', [
            'prestamista' => $cedulaSolicita,
            'prestamistaUser' => $prestamistaUser,
            'tipoLabel' => $tipoLabel,
            'prestamos' => $prestamos,
            'transferencias' => $transferencias,
            'scope' => $scope,
            'processRoute' => $scope === 'prestamos'
                ? route('asignacion.devolucion.prestamos.procesar')
                : route('asignacion.devolucion.transferencias.procesar'),
        ]);
    }

    private function procesarDevolucionPorScope(Request $request, string $scope): RedirectResponse
    {
        if (! session('prestamista')) {
            return redirect()->route('asignacion.solicitante')->with('warning', 'Primero valide la cedula del solicitante.');
        }

        $scope = $scope === 'transferencias' ? 'transferencias' : 'prestamos';

        $rules = $scope === 'prestamos'
            ? [
                'prestamos' => ['nullable', 'array'],
                'prestamos.*.id' => ['nullable', 'integer', Rule::exists('prestamos', 'id')],
                'prestamos.*.cantidad' => ['nullable', 'integer', 'min:1'],
                'prestamos.*.seleccionado' => ['nullable', 'boolean'],
            ]
            : [
                'transferencias' => ['nullable', 'array'],
                'transferencias.*.id' => ['nullable', 'integer', Rule::exists('transferencias', 'id')],
                'transferencias.*.cantidad' => ['nullable', 'integer', 'min:1'],
                'transferencias.*.devolver' => ['nullable', 'boolean'],
                'transferencias.*.utilizado' => ['nullable', 'boolean'],
            ];

        $validated = $request->validate($rules);
        $prestamosInput = collect($validated['prestamos'] ?? [])->values();
        $transferenciasInput = collect($validated['transferencias'] ?? [])->values();

        $haySeleccion = $scope === 'prestamos'
            ? $prestamosInput->contains(fn (array $row): bool => (bool) ($row['seleccionado'] ?? false))
            : $transferenciasInput->contains(fn (array $row): bool => (bool) ($row['devolver'] ?? false) || (bool) ($row['utilizado'] ?? false));

        if (! $haySeleccion) {
            return redirect()
                ->route($scope === 'prestamos' ? 'asignacion.devolucion.prestamos' : 'asignacion.devolucion.transferencias')
                ->withInput()
                ->with('warning', 'Seleccione al menos un elemento para procesar la devolucion.');
        }

        $editor = Usuario::query()->find((int) (auth()->id() ?? 0));
        if (! $editor || trim((string) $editor->cedula) === '') {
            return redirect()
                ->route($scope === 'prestamos' ? 'asignacion.devolucion.prestamos' : 'asignacion.devolucion.transferencias')
                ->withInput()
                ->with('warning', 'Error en uno de los registros de la base de datos.');
        }

        $cedulaSolicita = (string) session('prestamista');
        $cedulaRecibeDev = (string) $editor->cedula;
        $fechaOperacion = now();
        $fechaDevolucion = $fechaOperacion->format('d/m/Y');
        $prestamosDevueltos = [];
        $transferenciasDevueltas = [];
        $transferenciasUtilizadas = [];
        $contDevueltos = 0;
        $contUtilizados = 0;
        $contOmitidos = 0;
        $numeroOperacion = 0;
        $tipoOperacion = $scope === 'prestamos' ? 5 : 6;
        $tipoRoute = $scope === 'prestamos' ? 'devolucion-prestamo' : 'devolucion-transferencia';

        try {
            DB::transaction(function () use (
                $scope,
                $prestamosInput,
                $transferenciasInput,
                $cedulaSolicita,
                $cedulaRecibeDev,
                $fechaOperacion,
                $fechaDevolucion,
                $tipoOperacion,
                &$numeroOperacion,
                &$prestamosDevueltos,
                &$transferenciasDevueltas,
                &$transferenciasUtilizadas,
                &$contDevueltos,
                &$contUtilizados,
                &$contOmitidos
            ): void {
                $numOperacion = NumOperacion::query()->lockForUpdate()->first();
                if (! $numOperacion) {
                    $numOperacion = NumOperacion::query()->create(['numero' => 0]);
                }

                $numeroOperacion = (int) $numOperacion->numero + 1;
                $numOperacion->update(['numero' => $numeroOperacion]);

                Operacion::query()->create([
                    'numero' => $numeroOperacion,
                    'tipo' => $tipoOperacion,
                    ...(
                        Schema::hasColumn('operaciones', 'cedulaDespacha')
                            ? ['cedulaDespacha' => $cedulaRecibeDev]
                            : []
                    ),
                ]);

                if ($scope === 'prestamos') {
                    foreach ($prestamosInput as $row) {
                        if (! ($row['seleccionado'] ?? false)) {
                            continue;
                        }

                        $prestamo = Prestamo::query()
                            ->where('id', (int) ($row['id'] ?? 0))
                            ->where('cedulaSolicita', $cedulaSolicita)
                            ->where('status', 1)
                            ->lockForUpdate()
                            ->first();

                        $cantidadDevolver = (int) ($row['cantidad'] ?? 0);
                        if (! $prestamo || $cantidadDevolver <= 0 || $cantidadDevolver > (int) ($prestamo->cantidad ?? 0)) {
                            $contOmitidos++;
                            continue;
                        }

                        $bien = Bien::query()->where('codigo', $prestamo->codigo)->lockForUpdate()->first();
                        if (! $bien || (int) ($bien->tipo ?? 0) > 2) {
                            $contOmitidos++;
                            continue;
                        }

                        $stockResultado = $this->asignacionSupportService->aplicarDevolucionBien($bien, $cantidadDevolver, $cedulaRecibeDev, $fechaOperacion);

                        if ($cantidadDevolver === (int) $prestamo->cantidad) {
                            $prestamo->update([
                                'status' => 2,
                                'fechaDevolucion' => $fechaOperacion,
                                'cedulaRecibeDev' => $cedulaRecibeDev,
                            ]);
                        } else {
                            $prestamo->update([
                                'cantidad' => (int) $prestamo->cantidad - $cantidadDevolver,
                            ]);
                        }

                        DevolucionPrestamo::query()->create([
                            'numOperacion' => $numeroOperacion,
                            'codigo' => $prestamo->codigo,
                            'descripcion' => $bien->descripcion,
                            'cantidad' => $cantidadDevolver,
                            'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                            'fechaDevolucion' => $fechaDevolucion,
                            'ubicacion' => $prestamo->ubicacion,
                            'observacion' => $prestamo->observacion,
                        ]);

                        if (Schema::hasTable('registrosstock')) {
                            RegistroStock::query()->create([
                                'numOperacion' => $numeroOperacion,
                                'codigo' => $prestamo->codigo,
                                'cantidad' => $cantidadDevolver,
                                'objetivo' => 2,
                                'fechaOperacion' => $fechaOperacion,
                                'observacion' => 'Devolucion prestamo - Ubicacion: ' . (string) ($prestamo->ubicacion ?? ''),
                                'cedulaResponsable' => $cedulaRecibeDev,
                            ]);
                        }

                        $contDevueltos++;
                        $prestamosDevueltos[] = [
                            'codigo' => (string) $prestamo->codigo,
                            'descripcion' => (string) $bien->descripcion,
                            'cantidad' => $cantidadDevolver,
                            'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                            'ubicacion' => (string) ($prestamo->ubicacion ?? ''),
                            'stock_antes' => $stockResultado['stock_antes'],
                            'stock_despues' => $stockResultado['stock_despues'],
                        ];
                    }

                    return;
                }

                $referenciasCedula = [];

                foreach ($transferenciasInput as $row) {
                    $devolver = (bool) ($row['devolver'] ?? false);
                    $utilizado = (bool) ($row['utilizado'] ?? false);

                    if (! $devolver && ! $utilizado) {
                        continue;
                    }

                    if ($devolver && $utilizado) {
                        $contOmitidos++;
                        continue;
                    }

                    $transferencia = Transferencia::query()
                        ->where('id', (int) ($row['id'] ?? 0))
                        ->where('cedulaSolicita', $cedulaSolicita)
                        ->where('status', 1)
                        ->lockForUpdate()
                        ->first();

                    if (! $transferencia) {
                        $contOmitidos++;
                        continue;
                    }

                    $bien = Bien::query()->where('codigo', $transferencia->codigo)->lockForUpdate()->first();
                    if (! $bien) {
                        $contOmitidos++;
                        continue;
                    }

                    $referenciasCedula[] = (string) ($transferencia->emisor ?? '');
                    $referenciasCedula[] = (string) ($transferencia->receptor ?? '');

                    $referenciasPersonal = Usuario::query()
                        ->whereIn('cedula', array_values(array_filter(array_unique($referenciasCedula))))
                        ->get(['cedula', 'nombre'])
                        ->keyBy('cedula')
                        ->all();

                    if ($utilizado) {
                        if ((int) ($bien->tipo ?? 0) <= 2) {
                            $contOmitidos++;
                            continue;
                        }

                        $transferencia->update(['status' => 3]);

                        DevolucionTransferencia::query()->create([
                            'numOperacion' => $numeroOperacion,
                            'codigo' => $transferencia->codigo,
                            'descripcion' => $bien->descripcion,
                            'cantidad' => (int) ($transferencia->cantidad ?? 0),
                            'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                            'fechaDevolucion' => $fechaDevolucion,
                            'ubicacion' => $transferencia->ubicacion,
                            'observacion' => '[UTILIZADO] ' . (string) ($transferencia->observacion ?? ''),
                            'emisor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->emisor ?? ''), $referenciasPersonal),
                            'receptor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->receptor ?? ''), $referenciasPersonal),
                        ]);

                        if (Schema::hasTable('registrosstock')) {
                            RegistroStock::query()->create([
                                'numOperacion' => $numeroOperacion,
                                'codigo' => $transferencia->codigo,
                                'cantidad' => (int) ($transferencia->cantidad ?? 0),
                                'objetivo' => 3,
                                'fechaOperacion' => $fechaOperacion,
                                'observacion' => 'Transferencia catalogada como utilizada - Ubicacion: ' . (string) ($transferencia->ubicacion ?? ''),
                                'cedulaResponsable' => $cedulaRecibeDev,
                            ]);
                        }

                        $contUtilizados++;
                        $transferenciasUtilizadas[] = [
                            'codigo' => (string) $transferencia->codigo,
                            'descripcion' => (string) ($bien->descripcion ?? ''),
                            'cantidad' => (int) ($transferencia->cantidad ?? 0),
                            'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                            'ubicacion' => (string) ($transferencia->ubicacion ?? ''),
                        ];
                        continue;
                    }

                    $cantidadDevolver = (int) ($row['cantidad'] ?? 0);
                    if ($cantidadDevolver <= 0 || $cantidadDevolver > (int) ($transferencia->cantidad ?? 0)) {
                        $contOmitidos++;
                        continue;
                    }

                    $stockResultado = $this->asignacionSupportService->aplicarDevolucionBien($bien, $cantidadDevolver, $cedulaRecibeDev, $fechaOperacion);

                    if ($cantidadDevolver === (int) $transferencia->cantidad) {
                        $transferencia->update([
                            'status' => 2,
                            'fechaDevolucion' => $fechaOperacion,
                            'cedulaRecibeDev' => $cedulaRecibeDev,
                        ]);
                    } else {
                        $transferencia->update([
                            'cantidad' => (int) $transferencia->cantidad - $cantidadDevolver,
                        ]);
                    }

                    DevolucionTransferencia::query()->create([
                        'numOperacion' => $numeroOperacion,
                        'codigo' => $transferencia->codigo,
                        'descripcion' => $bien->descripcion,
                        'cantidad' => $cantidadDevolver,
                        'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                        'fechaDevolucion' => $fechaDevolucion,
                        'ubicacion' => $transferencia->ubicacion,
                        'observacion' => '[DEVUELTO] ' . (string) ($transferencia->observacion ?? ''),
                        'emisor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->emisor ?? ''), $referenciasPersonal),
                        'receptor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->receptor ?? ''), $referenciasPersonal),
                    ]);

                    if (Schema::hasTable('registrosstock')) {
                        RegistroStock::query()->create([
                            'numOperacion' => $numeroOperacion,
                            'codigo' => $transferencia->codigo,
                            'cantidad' => $cantidadDevolver,
                            'objetivo' => 2,
                            'fechaOperacion' => $fechaOperacion,
                            'observacion' => 'Devolucion transferencia - Ubicacion: ' . (string) ($transferencia->ubicacion ?? ''),
                            'cedulaResponsable' => $cedulaRecibeDev,
                        ]);
                    }

                    $contDevueltos++;
                    $transferenciasDevueltas[] = [
                        'codigo' => (string) $transferencia->codigo,
                        'descripcion' => (string) $bien->descripcion,
                        'cantidad' => $cantidadDevolver,
                        'tipo' => $this->asignacionSupportService->formatBienTipoMarca($bien->tipo, $bien->marca),
                        'ubicacion' => (string) ($transferencia->ubicacion ?? ''),
                        'emisor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->emisor ?? ''), $referenciasPersonal),
                        'receptor' => $this->asignacionSupportService->formatReferenciaPersonal((string) ($transferencia->receptor ?? ''), $referenciasPersonal),
                        'stock_antes' => $stockResultado['stock_antes'],
                        'stock_despues' => $stockResultado['stock_despues'],
                    ];
                }
            });
        } catch (\Throwable $exception) {
            Log::error('Asignacion devolucion failed', [
                'scope' => $scope,
                'user_id' => auth()->id(),
                'exception' => $exception,
            ]);

            return redirect()
                ->route($scope === 'prestamos' ? 'asignacion.devolucion.prestamos' : 'asignacion.devolucion.transferencias')
                ->withInput()
                ->with('warning', 'Ocurrio un error interno al procesar la devolucion. Intente nuevamente.');
        }

        $devolucionResultado = [
            'scope' => $scope,
            'numero_comprobante' => $numeroOperacion,
            'numero' => $numeroOperacion,
            'tipo_route' => $tipoRoute,
            'devueltos' => $contDevueltos,
            'utilizados' => $contUtilizados,
            'omitidos' => $contOmitidos,
            'receipt_url' => route('asignacion.operacion.show', ['tipo' => $tipoRoute, 'numero' => $numeroOperacion]),
            'export_url' => route('asignacion.operacion.export', ['tipo' => $tipoRoute, 'numero' => $numeroOperacion]),
            'prestamos' => $prestamosDevueltos,
            'transferencias' => $transferenciasDevueltas,
            'utilizados_items' => $transferenciasUtilizadas,
        ];

        session(['asignacion.devolucion_resultado' => $devolucionResultado]);

        return redirect()
            ->route($scope === 'prestamos' ? 'asignacion.devolucion.prestamos' : 'asignacion.devolucion.transferencias')
            ->with('status', 'Listo. ' . $contDevueltos . ' elementos fueron devueltos correctamente, ' . $contUtilizados . ' fueron catalogados como utilizados y ' . $contOmitidos . ' fueron omitidos.')
            ->with('devolucion_resultado', $devolucionResultado);
    }

}
