<?php

declare(strict_types=1);

namespace App\Commerce;

use App\Commerce\Dto\CommerceReferenceContext;

final readonly class CommerceReferenceResolver
{
    /**
     * @param array<int, array<string, mixed>> $shopResults
     */
    public function resolveFromCommerceTurn(
        string $prompt,
        string $answerText,
        array $shopResults
    ): ?CommerceReferenceContext {
        $fromText = $this->resolveFromText($prompt, $answerText);
        $fromShop = $this->resolveFromShopResults($prompt, $shopResults);

        if ($fromText !== null && $fromShop !== null && $this->areCompatibleProductNames($fromText->productName, $fromShop->productName)) {
            return new CommerceReferenceContext(
                productName: $fromShop->productName,
                productNumber: $fromShop->productNumber,
                manufacturer: $fromShop->manufacturer ?? $fromText->manufacturer,
                url: $fromShop->url,
                sourceType: 'shop',
                confidence: 1.0,
                resolvedFromPrompt: $fromText->resolvedFromPrompt ?? $fromShop->resolvedFromPrompt,
                resolvedAt: (new \DateTimeImmutable())->format(\DateTimeInterface::ATOM),
                focusTerms: $this->mergeFocusTerms(
                    $fromText->focusTerms,
                    $fromShop->focusTerms
                ),
            );
        }

        if ($fromText !== null) {
            return $fromText;
        }

        return $fromShop;
    }

    /**
     * @param array<int, array<string, mixed>> $shopResults
     */
    private function resolveFromShopResults(string $prompt, array $shopResults): ?CommerceReferenceContext
    {
        $top = $shopResults[0] ?? null;

        if (!is_array($top)) {
            return null;
        }

        $name = $this->extractString($top, 'name');
        $productNumber = $this->extractString($top, 'productNumber');
        $manufacturer = $this->extractString($top, 'manufacturer');
        $url = $this->extractString($top, 'url');

        if ($name === '') {
            return null;
        }

        return new CommerceReferenceContext(
            productName: $name,
            productNumber: $productNumber !== '' ? $productNumber : null,
            manufacturer: $manufacturer !== '' ? $manufacturer : null,
            url: $url !== '' ? $url : null,
            sourceType: 'shop',
            confidence: 1.0,
            resolvedFromPrompt: trim($prompt) !== '' ? trim($prompt) : null,
            resolvedAt: (new \DateTimeImmutable())->format(\DateTimeInterface::ATOM),
            focusTerms: $this->extractFocusTerms($prompt . "\n" . $name),
        );
    }

    private function resolveFromText(string $prompt, string $answerText): ?CommerceReferenceContext
    {
        $text = trim($prompt . "\n" . $answerText);

        if ($text === '') {
            return null;
        }

        $patterns = [
            '/\b(Testomat\s+2000\s+THCL)\b/ui',
            '/\b(Testomat\s+808)\b/ui',
            '/\b(Testomat\s+EVO\s+TH)\b/ui',
            '/\b(Testomat\s+EVO\s+CALC)\b/ui',
            '/\b(Testomat\s+ECO\s+PLUS)\b/ui',
            '/\b(Testomat\s+ECO\s+C)\b/ui',
            '/\b(Testomat\s+ECO)\b/ui',
            '/\b(Testomat\s+LAB\s+CL)\b/ui',
            '/\b(Testomat\s+LAB\s+MONO)\b/ui',
            '/\b(Testomat\s+2000)\b/ui',
        ];

        foreach ($patterns as $pattern) {
            if (!preg_match($pattern, $text, $matches)) {
                continue;
            }

            $productName = trim((string) ($matches[1] ?? ''));

            if ($productName === '') {
                continue;
            }

            return new CommerceReferenceContext(
                productName: $productName,
                productNumber: null,
                manufacturer: null,
                url: null,
                sourceType: 'conversation',
                confidence: 0.8,
                resolvedFromPrompt: trim($prompt) !== '' ? trim($prompt) : null,
                resolvedAt: (new \DateTimeImmutable())->format(\DateTimeInterface::ATOM),
                focusTerms: $this->extractFocusTerms($text),
            );
        }

        return null;
    }

    /**
     * @return string[]
     */
    private function extractFocusTerms(string $text): array
    {
        $normalized = $this->normalizeText($text);

        if ($normalized === '') {
            return [];
        }

        $patterns = [
            'indikator' => '/\bindikator(?:en)?\b/u',
            'indikatoren' => '/\bindikator(?:en)?\b/u',
            'reagenz' => '/\breagenz(?:ien)?\b/u',
            'reagenzien' => '/\breagenz(?:ien)?\b/u',
            'zubehör' => '/\bzubeh[oö]r\b/u',
            'ersatzteil' => '/\bersatzteile?\b/u',
            'ersatzteile' => '/\bersatzteile?\b/u',
            'service-set' => '/\bservice(?:\s|-)?set\b/u',
            'filter' => '/\bfilter\b/u',
            'pumpenkopf' => '/\bpumpenkopf\b/u',
            'motorblock' => '/\bmotorblock\b/u',
            'mehrwertpaket' => '/\bmehrwertpaket\b/u',
            'neotecmaster' => '/\bneotecmaster\b/u',
        ];

        $terms = [];

        foreach ($patterns as $canonical => $pattern) {
            if (preg_match($pattern, $normalized) === 1) {
                $terms[] = $canonical;
            }
        }

        return array_values(array_unique($terms));
    }

    /**
     * @param string[] $left
     * @param string[] $right
     * @return string[]
     */
    private function mergeFocusTerms(array $left, array $right): array
    {
        $merged = [];

        foreach ([$left, $right] as $list) {
            foreach ($list as $item) {
                if (!is_string($item)) {
                    continue;
                }

                $item = trim($item);

                if ($item === '') {
                    continue;
                }

                $merged[$item] = $item;
            }
        }

        return array_values($merged);
    }

    private function areCompatibleProductNames(string $left, string $right): bool
    {
        $left = $this->normalizeName($left);
        $right = $this->normalizeName($right);

        if ($left === '' || $right === '') {
            return false;
        }

        if ($left === $right) {
            return true;
        }

        return str_contains($left, $right) || str_contains($right, $left);
    }

    private function normalizeName(string $value): string
    {
        $value = mb_strtolower(trim($value), 'UTF-8');
        $value = preg_replace('/[^\p{L}\p{N}]+/u', ' ', $value) ?? $value;
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;

        return trim($value);
    }

    private function normalizeText(string $value): string
    {
        $value = mb_strtolower(trim($value), 'UTF-8');
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;

        return trim($value);
    }

    /**
     * @param array<string, mixed> $row
     */
    private function extractString(array $row, string $key): string
    {
        $value = $row[$key] ?? null;

        if (!is_string($value)) {
            return '';
        }

        return trim($value);
    }
}