<?php

namespace App\Services;

use App\Models\Producto;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class ProductSearchService
{
    /**
     * Search products by multiple strategies within a single empresa.
     * Never crosses empresa boundaries.
     *
     * @return Collection<int, Producto>
     */
    public function search(int $empresaId, string $query, int $limit = 5): Collection
    {
        if (trim($query) === '') {
            return collect();
        }

        $q = trim($query);
        $qLower = mb_strtolower($q);
        $tokens = collect(preg_split('/\s+/', $qLower) ?: [])
            ->map(static fn (string $token): string => trim($token))
            ->filter(static fn (string $token): bool => $token !== '')
            ->values();

        // 1. Exact match (barcode, SKU, or full product name)
        $exact = $this->queryBase($empresaId)
            ->where(function ($b) use ($q): void {
                $b->where('codigo_barras', $q)
                    ->orWhere('sku', $q)
                    ->orWhereRaw('LOWER(productos.nombre) = ?', [mb_strtolower($q)]);
            })
            ->get();

        if ($exact->isNotEmpty()) {
            return $exact->take($limit);
        }

        // 2. Flexible partial match by words, prioritizing names that start with the query.
        $baseQuery = $this->queryBase($empresaId);

        if ($tokens->isNotEmpty()) {
            $baseQuery->where(function ($outer) use ($tokens): void {
                foreach ($tokens as $token) {
                    $like = '%'.$token.'%';
                    $outer->where(function ($inner) use ($like): void {
                        $inner->whereRaw('LOWER(productos.nombre) LIKE ?', [$like])
                            ->orWhereRaw('LOWER(productos.descripcion) LIKE ?', [$like])
                            ->orWhereRaw('LOWER(productos.sku) LIKE ?', [$like])
                            ->orWhereHas('categoria', function ($cat) use ($like): void {
                                $cat->whereRaw('LOWER(nombre) LIKE ?', [$like]);
                            });
                    });
                }
            });
        }

        return $baseQuery
            ->orderByRaw('LOWER(productos.nombre) = ? DESC', [$qLower])
            ->orderByRaw('LOWER(productos.nombre) LIKE ? DESC', [$qLower.'%'])
            ->orderByRaw('LOWER(productos.nombre) LIKE ? DESC', ['%'.$qLower.'%'])
            ->orderByRaw('stock_actual > 0 DESC')
            ->orderBy('nombre')
            ->limit($limit)
            ->get();
    }

    /**
     * Aggregate stock across all sucursales for a product query.
     *
     * @return array{product: Producto|null, stock_por_sucursal: array<int, array{sucursal: string, stock: float}>}
     */
    public function stockPorSucursal(int $empresaId, string $query): array
    {
        $products = $this->search($empresaId, $query, 1);

        if ($products->isEmpty()) {
            return ['product' => null, 'stock_por_sucursal' => []];
        }

        $product = $products->first();

        // Aggregate stock from inventario table by sucursal
        $stockRows = DB::table('inventarios')
            ->join('sucursales', 'inventarios.sucursal_id', '=', 'sucursales.id')
            ->where('inventarios.producto_id', $product->id)
            ->where('sucursales.empresa_id', $empresaId)
            ->where('sucursales.activa', 1)
            ->select('sucursales.nombre as sucursal', DB::raw('SUM(inventarios.cantidad) as stock'))
            ->groupBy('sucursales.id', 'sucursales.nombre')
            ->orderByDesc('stock')
            ->get()
            ->map(static fn ($row): array => [
                'sucursal' => (string) $row->sucursal,
                'stock' => (float) $row->stock,
            ])
            ->all();

        // Fallback to producto.stock_actual if inventario table empty
        if (empty($stockRows)) {
            $sucursalNombre = $product->sucursal?->nombre ?? 'Principal';
            $stockRows = [[
                'sucursal' => $sucursalNombre,
                'stock' => (float) $product->stock_actual,
            ]];
        }

        return ['product' => $product, 'stock_por_sucursal' => $stockRows];
    }

    private function queryBase(int $empresaId)
    {
        return Producto::query()
            ->with(['sucursal', 'categoria'])
            ->where('productos.empresa_id', $empresaId)
            ->where('productos.activo', 1);
    }
}
