<?php

declare(strict_types=1);

namespace App\Modules\Inventory\Application\Services;

use PDO;

final class ItemLookupService
{
    public function __construct(private PDO $pdo)
    {
    }

    public function search(string $query): array
    {
        $query = $this->cleanSearchText($query);
        if ($query === '' || mb_strlen($query) < 1) {
            return [];
        }

        $tokens = $this->tokens($query);
        if (!$tokens) {
            return [];
        }

        $where = ['COALESCE(is_active,1)=1'];
        $params = [];
        foreach ($tokens as $idx => $token) {
            $nameKey = ':name' . $idx;
            $barcodeKey = ':barcode' . $idx;
            $where[] = "(name LIKE {$nameKey} OR COALESCE(barcode,'') LIKE {$barcodeKey} OR COALESCE(brand,'') LIKE {$nameKey})";
            $params['name' . $idx] = '%' . $token . '%';
            $params['barcode' . $idx] = '%' . $token . '%';
        }

        $statement = $this->pdo->prepare(
            'SELECT id, name, barcode, price FROM items WHERE ' . implode(' AND ', $where) . ' ORDER BY name LIMIT 50'
        );
        $statement->execute($params);
        $rows = $statement->fetchAll() ?: [];

        usort($rows, function (array $a, array $b) use ($query, $tokens): int {
            return $this->scoreRow($b, $query, $tokens) <=> $this->scoreRow($a, $query, $tokens);
        });

        $rows = array_slice($rows, 0, 20);
        $output = [];
        foreach ($rows as $row) {
            $name = (string) ($row['name'] ?? '');
            $barcode = (string) ($row['barcode'] ?? '');
            $label = $name;
            if ($barcode !== '') {
                $label .= ' — [' . $barcode . ']';
            }
            $output[] = [
                'id' => (int) ($row['id'] ?? 0),
                'name' => $name,
                'barcode' => $barcode,
                'code' => $barcode,
                'price' => (float) ($row['price'] ?? 0),
                'text' => $label,
            ];
        }

        return $output;
    }

    private function cleanSearchText(string $value): string
    {
        $value = preg_replace('/^\xEF\xBB\xBF/u', '', $value) ?? $value;
        $value = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', '', $value) ?? $value;
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;
        return trim($value);
    }

    private function tokens(string $query): array
    {
        $parts = preg_split('/[%\s\-_]+/u', mb_strtolower($this->cleanSearchText($query))) ?: [];
        return array_values(array_filter(array_map('trim', $parts), static fn(string $v): bool => $v !== ''));
    }

    private function normalized(string $value): string
    {
        return preg_replace('/[%\s\-_]+/u', '', mb_strtolower($this->cleanSearchText($value))) ?? '';
    }

    private function scoreRow(array $row, string $query, array $tokens): int
    {
        $name = $this->normalized((string)($row['name'] ?? ''));
        $barcode = $this->normalized((string)($row['barcode'] ?? ''));
        $queryNorm = $this->normalized($query);

        $score = 0;
        if ($barcode !== '' && $barcode === $queryNorm) { $score += 1000; }
        if ($name === $queryNorm) { $score += 900; }
        if ($barcode !== '' && str_starts_with($barcode, $queryNorm)) { $score += 400; }
        if ($queryNorm !== '' && str_starts_with($name, $queryNorm)) { $score += 350; }
        if ($this->orderedContains($name, $tokens)) { $score += 220; }
        if ($barcode !== '' && $this->orderedContains($barcode, $tokens)) { $score += 180; }
        $score += max(0, 100 - strlen((string)($row['name'] ?? '')));
        return $score;
    }

    private function orderedContains(string $haystack, array $tokens): bool
    {
        if ($haystack === '' || !$tokens) {
            return false;
        }
        $offset = 0;
        foreach ($tokens as $token) {
            $pos = mb_strpos($haystack, $this->normalized($token), $offset);
            if ($pos === false) {
                return false;
            }
            $offset = $pos + mb_strlen($this->normalized($token));
        }
        return true;
    }

    public function info(int $id): array
    {
        if ($id <= 0) {
            return ['ok' => false];
        }
        $statement = $this->pdo->prepare('SELECT id, name, barcode, default_unit_id, cost FROM items WHERE id=? LIMIT 1');
        $statement->execute([$id]);
        $item = $statement->fetch();
        if (!$item) {
            return ['ok' => false];
        }

        return [
            'ok' => true,
            'id' => (int) $item['id'],
            'name' => (string) $item['name'],
            'barcode' => (string) ($item['barcode'] ?? ''),
            'default_unit_id' => (int) ($item['default_unit_id'] ?? 0),
            'cost' => (float) ($item['cost'] ?? 0),
        ];
    }
}
