<?php

namespace App\Http\Controllers;

use App\Models\Categoria;
use App\Models\Impuesto;
use App\Models\Producto;
use App\Models\ProductoVariante;
use App\Models\Proveedor;
use App\Models\Sucursal;
use App\Models\UnidadMedida;
use App\Support\PublicMedia;
use Illuminate\Database\QueryException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ProductoController extends Controller
{
    public function index(): View
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $productos = Producto::query()
            ->where('empresa_id', $empresaId)
            ->with(['categoria', 'sucursal', 'proveedor', 'unidadMedida'])
            ->orderByDesc('id')
            ->paginate(20);

        $productVariantCounts = [];
        if (Schema::hasTable('producto_variantes')) {
            $productIds = $productos->getCollection()->pluck('id')->all();
            if ($productIds !== []) {
                $productVariantCounts = ProductoVariante::query()
                    ->where('empresa_id', $empresaId)
                    ->whereIn('producto_id', $productIds)
                    ->where('activo', 1)
                    ->selectRaw('producto_id, COUNT(*) as total')
                    ->groupBy('producto_id')
                    ->pluck('total', 'producto_id')
                    ->map(static fn ($count): int => (int) $count)
                    ->all();
            }
        }

        $categorias = Categoria::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->orderBy('nombre')
            ->get();

        $sucursales = Sucursal::query()
            ->where('empresa_id', $empresaId)
            ->where('activa', 1)
            ->orderBy('nombre')
            ->get();

        $proveedores = Proveedor::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->orderBy('nombre')
            ->get();

        $unidades = UnidadMedida::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->orderBy('nombre')
            ->get();

        $impuestos = Impuesto::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->orderBy('nombre')
            ->get();

        return view('modules.productos.index', [
            'productos' => $productos,
            'categorias' => $categorias,
            'sucursales' => $sucursales,
            'proveedores' => $proveedores,
            'unidades' => $unidades,
            'impuestos' => $impuestos,
            'tenantSucursalId' => tenant_sucursal_id(),
            'productVariantCounts' => $productVariantCounts,
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $validated = $request->validate([
            'nombre' => ['required', 'string', 'max:180'],
            'sku' => [
                'nullable',
                'string',
                'max:80',
                Rule::unique('productos', 'sku')->where(
                    static fn ($query) => $query->where('empresa_id', $empresaId)
                ),
            ],
            'codigo_barras' => ['nullable', 'string', 'max:80'],
            'categoria_id' => ['nullable', 'integer'],
            'proveedor_id' => ['nullable', 'integer'],
            'unidad_medida_id' => ['nullable', 'integer'],
            'impuesto_id' => ['nullable', 'integer'],
            'sucursal_id' => ['nullable', 'integer'],
            'descripcion' => ['nullable', 'string'],
            'precio_compra' => ['nullable', 'numeric', 'min:0'],
            'precio_venta' => ['nullable', 'numeric', 'min:0'],
            'impuesto_porcentaje' => ['nullable', 'numeric', 'min:0', 'max:100'],
            'stock_actual' => ['nullable', 'numeric', 'min:0'],
            'stock_minimo' => ['nullable', 'numeric', 'min:0'],
            'activo' => ['nullable', 'boolean'],
            'imagen' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
            'variantes_habilitadas' => ['nullable', 'boolean'],
            'variantes_modo' => ['nullable', Rule::in(['same', 'individual'])],
            'variante_nombre' => ['nullable', 'array'],
            'variante_nombre.*' => ['nullable', 'string', 'max:140'],
            'variante_precio' => ['nullable', 'array'],
            'variante_precio.*' => ['nullable', 'numeric', 'min:0'],
            'variante_cantidad' => ['nullable', 'array'],
            'variante_cantidad.*' => ['nullable', 'numeric', 'min:0'],
            'variante_stock_minimo' => ['nullable', 'array'],
            'variante_stock_minimo.*' => ['nullable', 'numeric', 'min:0'],
        ]);

        $categoriaId = isset($validated['categoria_id']) && $validated['categoria_id'] !== null && $validated['categoria_id'] !== ''
            ? (int) $validated['categoria_id']
            : null;
        if (
            $categoriaId
            && ! Categoria::query()
                ->whereKey($categoriaId)
                ->where('empresa_id', $empresaId)
                ->exists()
        ) {
            return back()
                ->withErrors(['categoria_id' => 'La categoria seleccionada no pertenece a la empresa activa.'])
                ->withInput();
        }

        $proveedorId = isset($validated['proveedor_id']) && $validated['proveedor_id'] !== null && $validated['proveedor_id'] !== ''
            ? (int) $validated['proveedor_id']
            : null;
        if (
            $proveedorId
            && ! Proveedor::query()
                ->whereKey($proveedorId)
                ->where('empresa_id', $empresaId)
                ->exists()
        ) {
            return back()
                ->withErrors(['proveedor_id' => 'El proveedor seleccionado no pertenece a la empresa activa.'])
                ->withInput();
        }

        $unidadMedidaId = isset($validated['unidad_medida_id']) && $validated['unidad_medida_id'] !== null && $validated['unidad_medida_id'] !== ''
            ? (int) $validated['unidad_medida_id']
            : null;
        if (
            $unidadMedidaId
            && ! UnidadMedida::query()
                ->whereKey($unidadMedidaId)
                ->where('empresa_id', $empresaId)
                ->exists()
        ) {
            return back()
                ->withErrors(['unidad_medida_id' => 'La unidad seleccionada no pertenece a la empresa activa.'])
                ->withInput();
        }

        $sucursalId = isset($validated['sucursal_id']) && $validated['sucursal_id'] !== null && $validated['sucursal_id'] !== ''
            ? (int) $validated['sucursal_id']
            : tenant_sucursal_id();
        if (
            $sucursalId
            && ! Sucursal::query()
                ->whereKey($sucursalId)
                ->where('empresa_id', $empresaId)
                ->exists()
        ) {
            return back()
                ->withErrors(['sucursal_id' => 'La sucursal seleccionada no pertenece a la empresa activa.'])
                ->withInput();
        }

        $impuestoId = isset($validated['impuesto_id']) && $validated['impuesto_id'] !== null && $validated['impuesto_id'] !== ''
            ? (int) $validated['impuesto_id']
            : null;
        $impuestoPorcentaje = (float) ($validated['impuesto_porcentaje'] ?? 0);
        if ($impuestoId) {
            $tax = Impuesto::query()
                ->whereKey($impuestoId)
                ->where('empresa_id', $empresaId)
                ->where('activo', 1)
                ->first();
            if (! $tax) {
                return back()
                    ->withErrors(['impuesto_id' => 'El impuesto seleccionado no pertenece a la empresa activa.'])
                    ->withInput();
            }
            $impuestoPorcentaje = (float) $tax->porcentaje;
        }

        $variantsEnabled = $request->boolean('variantes_habilitadas');
        $variantsMode = (string) ($validated['variantes_modo'] ?? 'same');
        if (! in_array($variantsMode, ['same', 'individual'], true)) {
            $variantsMode = 'same';
        }

        $variantRows = [];
        if ($variantsEnabled && Schema::hasTable('producto_variantes')) {
            $variantNames = is_array($validated['variante_nombre'] ?? null) ? $validated['variante_nombre'] : [];
            $variantPrices = is_array($validated['variante_precio'] ?? null) ? $validated['variante_precio'] : [];
            $variantStocks = is_array($validated['variante_cantidad'] ?? null) ? $validated['variante_cantidad'] : [];
            $variantMinimums = is_array($validated['variante_stock_minimo'] ?? null) ? $validated['variante_stock_minimo'] : [];
            $defaultPrice = (float) ($validated['precio_venta'] ?? 0);

            foreach ($variantNames as $index => $rawName) {
                $variantName = trim((string) $rawName);
                if ($variantName === '') {
                    continue;
                }

                $variantPrice = $variantsMode === 'individual'
                    ? (float) ($variantPrices[$index] ?? 0)
                    : $defaultPrice;
                $variantStock = max(0.0, (float) ($variantStocks[$index] ?? 0));
                $variantMinStock = max(0.0, (float) ($variantMinimums[$index] ?? 0));

                $variantRows[] = [
                    'nombre' => $variantName,
                    'precio_venta' => $variantPrice,
                    'cantidad' => $variantStock,
                    'stock_minimo' => $variantMinStock,
                ];
            }

            if ($variantRows === []) {
                return back()
                    ->withErrors(['variantes_habilitadas' => 'Debes crear al menos una variación válida para guardar el producto variable.'])
                    ->withInput();
            }
        }

        $imagePath = null;
        if ($request->hasFile('imagen')) {
            $image = $request->file('imagen');
            if ($image instanceof UploadedFile) {
                $imagePath = PublicMedia::store($image, 'productos', 'producto');
            }
        }

        $stockActual = $variantsEnabled
            ? array_sum(array_map(static fn (array $row): float => (float) ($row['cantidad'] ?? 0), $variantRows))
            : (float) ($validated['stock_actual'] ?? 0);
        $stockMinimo = $variantsEnabled
            ? array_sum(array_map(static fn (array $row): float => (float) ($row['stock_minimo'] ?? 0), $variantRows))
            : (float) ($validated['stock_minimo'] ?? 0);

        DB::transaction(function () use (
            $empresaId,
            $sucursalId,
            $categoriaId,
            $proveedorId,
            $unidadMedidaId,
            $validated,
            $request,
            $impuestoPorcentaje,
            $stockActual,
            $stockMinimo,
            $imagePath,
            $variantsEnabled,
            $variantRows
        ): void {
            $producto = Producto::query()->create([
                'empresa_id' => $empresaId,
                'sucursal_id' => $sucursalId,
                'categoria_id' => $categoriaId,
                'proveedor_id' => $proveedorId,
                'unidad_medida_id' => $unidadMedidaId,
                'nombre' => $validated['nombre'],
                'sku' => $validated['sku'] ?? null,
                'codigo_barras' => $validated['codigo_barras'] ?? null,
                'descripcion' => $validated['descripcion'] ?? null,
                'imagen' => $imagePath,
                'precio_compra' => (float) ($validated['precio_compra'] ?? 0),
                'precio_venta' => (float) ($validated['precio_venta'] ?? 0),
                'impuesto_porcentaje' => $impuestoPorcentaje,
                'stock_actual' => $stockActual,
                'stock_minimo' => $stockMinimo,
                'activo' => $request->boolean('activo', true),
            ]);

            if (! $variantsEnabled || $variantRows === [] || ! Schema::hasTable('producto_variantes')) {
                return;
            }

            $baseSku = trim((string) ($validated['sku'] ?? ''));

            foreach ($variantRows as $index => $row) {
                $variantSku = $baseSku !== ''
                    ? $baseSku.'-V'.($index + 1)
                    : 'PRD'.$producto->id.'-V'.($index + 1);

                ProductoVariante::query()->create([
                    'empresa_id' => $empresaId,
                    'producto_id' => (int) $producto->id,
                    'nombre' => (string) ($row['nombre'] ?? ''),
                    'sku' => $variantSku,
                    'cantidad' => (int) round((float) ($row['cantidad'] ?? 0)),
                    'stock_minimo' => (int) round((float) ($row['stock_minimo'] ?? 0)),
                    'precio_venta' => (float) ($row['precio_venta'] ?? 0),
                    'activo' => true,
                ]);
            }
        });

        return redirect()->route('productos.index')
            ->with('status', 'Producto creado correctamente.');
    }

    public function downloadTemplate(): StreamedResponse
    {
        abort_if(! tenant_empresa_id(), 403, 'No hay empresa activa.');

        $headers = [
            'nombre',
            'sku',
            'codigo_barras',
            'descripcion',
            'precio_compra',
            'precio_venta',
            'impuesto_porcentaje',
            'stock_actual',
            'stock_minimo',
            'categoria_nombre',
            'proveedor_nombre',
            'unidad_nombre',
        ];

        $example = [
            'Camiseta Basica',
            'CAM-001',
            '7701234567890',
            'Camiseta de algodon talla M',
            '15000',
            '30000',
            '19',
            '50',
            '5',
            'Ropa',
            'Proveedor Ejemplo',
            'Unidad',
        ];

        return response()->streamDownload(function () use ($headers, $example): void {
            $out = fopen('php://output', 'wb');
            if (! is_resource($out)) {
                return;
            }

            fwrite($out, "\xEF\xBB\xBF");
            fputcsv($out, $headers, ',');
            fputcsv($out, $example, ',');
            fclose($out);
        }, 'plantilla_productos.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
            'Cache-Control' => 'no-cache, no-store, must-revalidate',
        ]);
    }

    public function import(Request $request): RedirectResponse
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $validated = $request->validate([
            'archivo_csv' => ['required', 'file', 'mimes:csv,txt', 'max:5120'],
            'accion_duplicado' => ['required', Rule::in(['omitir', 'actualizar'])],
        ]);

        $csvFile = $request->file('archivo_csv');
        if (! $csvFile || ! $csvFile->isValid()) {
            return redirect()
                ->route('productos.index')
                ->with('error', 'Debes seleccionar un archivo CSV valido.')
                ->with('open_import_modal', true)
                ->withInput();
        }

        $csvPath = $csvFile->getRealPath();
        if (! is_string($csvPath) || $csvPath === '' || ! is_file($csvPath)) {
            return redirect()
                ->route('productos.index')
                ->with('error', 'No se pudo leer el archivo subido.')
                ->with('open_import_modal', true)
                ->withInput();
        }

        $handle = fopen($csvPath, 'rb');
        if (! is_resource($handle)) {
            return redirect()
                ->route('productos.index')
                ->with('error', 'No se pudo abrir el archivo CSV.')
                ->with('open_import_modal', true)
                ->withInput();
        }

        $categoriasByName = Categoria::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->get()
            ->mapWithKeys(fn (Categoria $categoria): array => [
                $this->normalizeCsvLookupKey((string) $categoria->nombre) => $categoria,
            ]);

        $proveedoresByName = Proveedor::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->get()
            ->mapWithKeys(fn (Proveedor $proveedor): array => [
                $this->normalizeCsvLookupKey((string) $proveedor->nombre) => $proveedor,
            ]);

        $unidadesByName = collect();
        foreach (
            UnidadMedida::query()
                ->where('empresa_id', $empresaId)
                ->where('activo', 1)
                ->get() as $unidad
        ) {
            $keyByName = $this->normalizeCsvLookupKey((string) $unidad->nombre);
            if ($keyByName !== '' && ! $unidadesByName->has($keyByName)) {
                $unidadesByName->put($keyByName, $unidad);
            }

            $keyByAbbreviation = $this->normalizeCsvLookupKey((string) $unidad->abreviatura);
            if ($keyByAbbreviation !== '' && ! $unidadesByName->has($keyByAbbreviation)) {
                $unidadesByName->put($keyByAbbreviation, $unidad);
            }
        }

        $headers = [];
        $columnIndexes = [];
        $requiredColumns = ['nombre', 'sku', 'precio_venta', 'categoria_nombre', 'proveedor_nombre', 'unidad_nombre'];

        $errors = [];
        $imported = 0;
        $updated = 0;
        $skipped = 0;
        $rowNumber = 1;
        $tenantSucursalId = tenant_sucursal_id();

        try {
            $bom = fread($handle, 3);
            if ($bom !== "\xEF\xBB\xBF") {
                rewind($handle);
            }

            $headerRow = fgetcsv($handle, 0, ',');
            if (! is_array($headerRow) || $headerRow === []) {
                return redirect()
                    ->route('productos.index')
                    ->with('error', 'El archivo CSV no tiene encabezados validos.')
                    ->with('open_import_modal', true)
                    ->withInput();
            }

            $headers = array_map(
                fn ($value): string => $this->normalizeCsvHeader((string) $value),
                $headerRow
            );
            $columnIndexes = array_flip($headers);

            $missingColumns = array_values(array_diff($requiredColumns, array_keys($columnIndexes)));
            if ($missingColumns !== []) {
                return redirect()
                    ->route('productos.index')
                    ->with('error', 'Faltan columnas obligatorias: '.implode(', ', $missingColumns).'. Descarga la plantilla correcta.')
                    ->with('open_import_modal', true)
                    ->withInput();
            }

            while (($row = fgetcsv($handle, 0, ',')) !== false) {
                $rowNumber++;
                if ($this->isCsvRowEmpty($row)) {
                    continue;
                }

                $nombre = $this->getCsvValue($row, $columnIndexes, 'nombre');
                $sku = $this->getCsvValue($row, $columnIndexes, 'sku');
                $precioVentaRaw = $this->getCsvValue($row, $columnIndexes, 'precio_venta');
                $categoriaNombre = $this->getCsvValue($row, $columnIndexes, 'categoria_nombre');
                $proveedorNombre = $this->getCsvValue($row, $columnIndexes, 'proveedor_nombre');
                $unidadNombre = $this->getCsvValue($row, $columnIndexes, 'unidad_nombre');

                if (
                    $nombre === ''
                    || $sku === ''
                    || $precioVentaRaw === ''
                    || $categoriaNombre === ''
                    || $proveedorNombre === ''
                    || $unidadNombre === ''
                ) {
                    $errors[] = "Fila {$rowNumber}: nombre, sku, precio_venta, categoria_nombre, proveedor_nombre y unidad_nombre son obligatorios.";
                    continue;
                }

                $precioVenta = $this->parseCsvDecimal($precioVentaRaw);
                if ($precioVenta <= 0) {
                    $errors[] = "Fila {$rowNumber}: precio_venta debe ser mayor a cero.";
                    continue;
                }

                $categoria = $categoriasByName->get($this->normalizeCsvLookupKey($categoriaNombre));
                if (! $categoria instanceof Categoria) {
                    $errors[] = "Fila {$rowNumber}: categoria '{$categoriaNombre}' no encontrada en la empresa.";
                    continue;
                }

                $proveedor = $proveedoresByName->get($this->normalizeCsvLookupKey($proveedorNombre));
                if (! $proveedor instanceof Proveedor) {
                    $errors[] = "Fila {$rowNumber}: proveedor '{$proveedorNombre}' no encontrado o inactivo.";
                    continue;
                }

                $unidad = $unidadesByName->get($this->normalizeCsvLookupKey($unidadNombre));
                if (! $unidad instanceof UnidadMedida) {
                    $errors[] = "Fila {$rowNumber}: unidad de medida '{$unidadNombre}' no encontrada o inactiva.";
                    continue;
                }

                $taxRate = $this->parseCsvDecimal($this->getCsvValue($row, $columnIndexes, 'impuesto_porcentaje'));
                if ($taxRate < 0 || $taxRate > 100) {
                    $taxRate = 0.0;
                }

                $payload = [
                    'sucursal_id' => $tenantSucursalId,
                    'categoria_id' => (int) $categoria->id,
                    'proveedor_id' => (int) $proveedor->id,
                    'unidad_medida_id' => (int) $unidad->id,
                    'nombre' => $nombre,
                    'sku' => $sku,
                    'codigo_barras' => $this->nullableCsvValue($this->getCsvValue($row, $columnIndexes, 'codigo_barras')),
                    'descripcion' => $this->nullableCsvValue($this->getCsvValue($row, $columnIndexes, 'descripcion')),
                    'precio_compra' => max(0.0, $this->parseCsvDecimal($this->getCsvValue($row, $columnIndexes, 'precio_compra'))),
                    'precio_venta' => $precioVenta,
                    'impuesto_porcentaje' => $taxRate,
                    'stock_actual' => max(0.0, $this->parseCsvDecimal($this->getCsvValue($row, $columnIndexes, 'stock_actual'))),
                    'stock_minimo' => max(0.0, $this->parseCsvDecimal($this->getCsvValue($row, $columnIndexes, 'stock_minimo'))),
                ];

                $existing = Producto::query()
                    ->where('empresa_id', $empresaId)
                    ->where('sku', $sku)
                    ->first();

                if ($existing instanceof Producto) {
                    if ($validated['accion_duplicado'] === 'omitir') {
                        $skipped++;
                        continue;
                    }

                    try {
                        $existing->fill($payload);
                        $existing->activo = true;
                        $existing->save();
                        $updated++;
                    } catch (\Throwable $exception) {
                        $errors[] = "Fila {$rowNumber} (SKU '{$sku}'): ".$exception->getMessage();
                    }

                    continue;
                }

                try {
                    Producto::query()->create(array_merge($payload, [
                        'empresa_id' => $empresaId,
                        'imagen' => 'assets/img/default-producto.jpg',
                        'activo' => true,
                    ]));
                    $imported++;
                } catch (\Throwable $exception) {
                    $errors[] = "Fila {$rowNumber} (SKU '{$sku}'): ".$exception->getMessage();
                }
            }
        } finally {
            fclose($handle);
        }

        $processedSummary = [];
        if ($imported > 0) {
            $processedSummary[] = "{$imported} creado(s)";
        }
        if ($updated > 0) {
            $processedSummary[] = "{$updated} actualizado(s)";
        }
        if ($skipped > 0) {
            $processedSummary[] = "{$skipped} omitido(s) por SKU duplicado";
        }
        $summaryMessage = $processedSummary !== [] ? implode(', ', $processedSummary).'.' : 'Ningun producto procesado.';

        $errorPreview = implode(' | ', array_slice($errors, 0, 5));
        if ($imported === 0 && $updated === 0) {
            $fullMessage = $errorPreview !== ''
                ? 'No se proceso ningun producto. Errores: '.$errorPreview
                : 'No se proceso ningun producto.';

            return redirect()
                ->route('productos.index')
                ->with('error', $fullMessage)
                ->with('open_import_modal', true)
                ->withInput();
        }

        if ($errors === []) {
            return redirect()
                ->route('productos.index')
                ->with('status', 'Importacion completada: '.$summaryMessage);
        }

        return redirect()
            ->route('productos.index')
            ->with('status', 'Importacion parcial: '.$summaryMessage)
            ->with('error', count($errors).' error(es): '.$errorPreview);
    }

    public function edit(Producto $producto): View
    {
        $this->ensureProductoAccess($producto);

        $empresaId = tenant_empresa_id();
        $categorias = Categoria::query()
            ->where('empresa_id', $empresaId)
            ->orderBy('nombre')
            ->get();
        $sucursales = Sucursal::query()
            ->where('empresa_id', $empresaId)
            ->orderBy('nombre')
            ->get();

        return view('modules.productos.edit', [
            'producto' => $producto,
            'categorias' => $categorias,
            'sucursales' => $sucursales,
        ]);
    }

    public function update(Request $request, Producto $producto): RedirectResponse
    {
        $this->ensureProductoAccess($producto);

        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $validated = $request->validate([
            'nombre' => ['required', 'string', 'max:180'],
            'sku' => [
                'nullable',
                'string',
                'max:80',
                Rule::unique('productos', 'sku')
                    ->ignore($producto->id)
                    ->where(static fn ($query) => $query->where('empresa_id', $empresaId)),
            ],
            'codigo_barras' => ['nullable', 'string', 'max:80'],
            'categoria_id' => ['nullable', 'integer'],
            'sucursal_id' => ['nullable', 'integer'],
            'descripcion' => ['nullable', 'string'],
            'precio_compra' => ['nullable', 'numeric', 'min:0'],
            'precio_venta' => ['nullable', 'numeric', 'min:0'],
            'impuesto_porcentaje' => ['nullable', 'numeric', 'min:0', 'max:100'],
            'stock_minimo' => ['nullable', 'numeric', 'min:0'],
            'activo' => ['nullable', 'boolean'],
        ]);

        $categoriaId = $validated['categoria_id'] ? (int) $validated['categoria_id'] : null;
        if (
            $categoriaId
            && ! Categoria::query()
                ->whereKey($categoriaId)
                ->where('empresa_id', $empresaId)
                ->exists()
        ) {
            return back()
                ->withErrors(['categoria_id' => 'La categoria seleccionada no pertenece a la empresa activa.'])
                ->withInput();
        }

        $sucursalId = $validated['sucursal_id'] ? (int) $validated['sucursal_id'] : null;
        if (
            $sucursalId
            && ! Sucursal::query()
                ->whereKey($sucursalId)
                ->where('empresa_id', $empresaId)
                ->exists()
        ) {
            return back()
                ->withErrors(['sucursal_id' => 'La sucursal seleccionada no pertenece a la empresa activa.'])
                ->withInput();
        }

        $producto->update([
            'nombre' => $validated['nombre'],
            'sku' => $validated['sku'] ?? null,
            'codigo_barras' => $validated['codigo_barras'] ?? null,
            'categoria_id' => $categoriaId,
            'sucursal_id' => $sucursalId,
            'descripcion' => $validated['descripcion'] ?? null,
            'precio_compra' => (float) ($validated['precio_compra'] ?? 0),
            'precio_venta' => (float) ($validated['precio_venta'] ?? 0),
            'impuesto_porcentaje' => (float) ($validated['impuesto_porcentaje'] ?? 0),
            'stock_minimo' => (float) ($validated['stock_minimo'] ?? 0),
            'activo' => $request->boolean('activo'),
        ]);

        return redirect()->route('productos.index')
            ->with('status', 'Producto actualizado correctamente.');
    }

    public function deactivate(Producto $producto): RedirectResponse
    {
        $this->ensureProductoAccess($producto);

        if (! $producto->activo) {
            return redirect()->route('productos.index')
                ->with('status', 'El producto ya estaba desactivado.');
        }

        $producto->update(['activo' => false]);

        return redirect()->route('productos.index')
            ->with('status', 'Producto desactivado correctamente.');
    }

    public function reactivate(Producto $producto): RedirectResponse
    {
        $this->ensureProductoAccess($producto);

        if ($producto->activo) {
            return redirect()->route('productos.index')
                ->with('status', 'El producto ya estaba activo.');
        }

        $producto->update(['activo' => true]);

        return redirect()->route('productos.index')
            ->with('status', 'Producto reactivado correctamente.');
    }

    public function destroy(Producto $producto): RedirectResponse
    {
        $this->ensureProductoAccess($producto);

        try {
            $producto->delete();

            return redirect()->route('productos.index')
                ->with('status', 'Producto eliminado correctamente.');
        } catch (QueryException) {
            return redirect()->route('productos.index')
                ->with('error', 'No se pudo eliminar el producto porque tiene inventario o movimientos relacionados.');
        }
    }

    private function ensureProductoAccess(Producto $producto): void
    {
        abort_unless((int) $producto->empresa_id === (int) tenant_empresa_id(), 403, 'No tienes acceso a este producto.');
    }

    private function normalizeCsvHeader(string $value): string
    {
        return strtolower(trim(str_replace("\xEF\xBB\xBF", '', $value)));
    }

    private function normalizeCsvLookupKey(string $value): string
    {
        $normalized = preg_replace('/\s+/u', ' ', trim($value));

        return strtolower((string) $normalized);
    }

    private function isCsvRowEmpty(array $row): bool
    {
        foreach ($row as $value) {
            if (trim((string) $value) !== '') {
                return false;
            }
        }

        return true;
    }

    private function getCsvValue(array $row, array $columnIndexes, string $column): string
    {
        $index = $columnIndexes[$column] ?? null;
        if (! is_int($index)) {
            return '';
        }

        return trim((string) ($row[$index] ?? ''));
    }

    private function nullableCsvValue(string $value): ?string
    {
        $trimmed = trim($value);

        return $trimmed !== '' ? $trimmed : null;
    }

    private function parseCsvDecimal(string $value): float
    {
        $raw = trim($value);
        if ($raw === '') {
            return 0.0;
        }

        $raw = str_replace(["\xc2\xa0", ' '], '', $raw);

        $hasComma = str_contains($raw, ',');
        $hasDot = str_contains($raw, '.');

        if ($hasComma && $hasDot) {
            $lastComma = strrpos($raw, ',');
            $lastDot = strrpos($raw, '.');
            if ($lastComma !== false && $lastDot !== false && $lastComma > $lastDot) {
                $raw = str_replace('.', '', $raw);
                $raw = str_replace(',', '.', $raw);
            } else {
                $raw = str_replace(',', '', $raw);
            }
        } elseif ($hasComma) {
            $raw = str_replace(',', '.', $raw);
        }

        return is_numeric($raw) ? (float) $raw : 0.0;
    }
}
