<?php

namespace App\Support;

/**
 * Mirrors resolveBrandMatch / normalizeBrandText / getBrandMonogram from bugets-smart-money/src/lib/brands.ts
 * Uses resources/data/brand-catalog.json (normalized alias rows) + brand-icons.json (SVG paths).
 */
final class BrandResolver
{
    /** @var list<array{alias: string, slug?: string, monogram?: string, color?: string}>|null */
    private static ?array $catalog = null;

    /** @var array<string, array{hex: string, path: string, title: string}>|null */
    private static ?array $icons = null;

    public static function normalize(string $value): string
    {
        $s = mb_strtolower($value);
        if (class_exists(\Normalizer::class)) {
            $norm = \Normalizer::normalize($s, \Normalizer::FORM_D);
            if (is_string($norm)) {
                $s = $norm;
            }
        }
        $s = preg_replace('/\p{M}/u', '', $s) ?? $s;
        $s = preg_replace('/[^a-z0-9+]+/i', ' ', $s);

        return trim($s);
    }

    public static function monogram(?string $value): string
    {
        if ($value === null || trim($value) === '') {
            return '?';
        }
        $parts = array_values(array_filter(preg_split('/\s+/', trim($value)) ?: []));
        $out = '';
        foreach (array_slice($parts, 0, 2) as $chunk) {
            $out .= strtoupper(mb_substr($chunk, 0, 1));
        }

        return $out !== '' ? $out : '?';
    }

    /**
     * @return array{
     *     kind: 'svg',
     *     title: string,
     *     hex: string,
     *     path: string,
     *     rgbaBg: string,
     *     fg: string
     * }|array{
     *     kind: 'mono',
     *     title: string,
     *     text: string,
     *     color: string,
     *     rgbaBg: string
     * }|null
     */
    public static function resolve(?string $label): ?array
    {
        if ($label === null || trim($label) === '') {
            return null;
        }

        $normalizedValue = self::normalize($label);
        if ($normalizedValue === '') {
            return null;
        }

        foreach (self::catalog() as $row) {
            $alias = $row['alias'];
            if ($alias !== '' && self::catalogAliasMatches($normalizedValue, $alias)) {
                if (! empty($row['slug'])) {
                    $slug = $row['slug'];
                    $icon = self::icons()[$slug] ?? null;
                    if ($icon !== null) {
                        $rgb = self::hexToRgb($icon['hex']);

                        return [
                            'kind' => 'svg',
                            'title' => $icon['title'],
                            'hex' => $icon['hex'],
                            'path' => $icon['path'],
                            'rgbaBg' => sprintf('rgba(%d,%d,%d,0.12)', $rgb['r'], $rgb['g'], $rgb['b']),
                            'fg' => '#'.$icon['hex'],
                        ];
                    }
                }

                if (! empty($row['monogram']) && ! empty($row['color'])) {
                    $rgb = self::hexToRgb(str_replace('#', '', (string) $row['color']));

                    return [
                        'kind' => 'mono',
                        'title' => $label,
                        'text' => $row['monogram'],
                        'color' => $row['color'],
                        'rgbaBg' => sprintf('rgba(%d,%d,%d,0.09)', $rgb['r'], $rgb['g'], $rgb['b']),
                    ];
                }
            }
        }

        return null;
    }

    /**
     * Phrase aliases (contain a space) keep substring semantics. Single-token aliases match as whole
     * words so short keys like "x" or "loom" do not hit inside "siriusxm" or "bloomberg".
     * Aliases are normalized the same way as merchant labels so accents match (e.g. intermarché ↔ intermarche).
     */
    private static function catalogAliasMatches(string $normalizedValue, string $alias): bool
    {
        $aliasNorm = self::normalize($alias);
        if ($aliasNorm === '') {
            return false;
        }
        if (str_contains($aliasNorm, ' ')) {
            return str_contains($normalizedValue, $aliasNorm);
        }

        return (bool) preg_match(
            '/(?:^|\s)'.preg_quote($aliasNorm, '/').'(?:\s|$)/u',
            $normalizedValue
        );
    }

    /**
     * @return list<array{alias: string, slug?: string, monogram?: string, color?: string}>
     */
    private static function catalog(): array
    {
        if (self::$catalog !== null) {
            return self::$catalog;
        }
        $path = resource_path('data/brand-catalog.json');
        if (! is_readable($path)) {
            self::$catalog = [];

            return self::$catalog;
        }
        $json = json_decode((string) file_get_contents($path), true);
        $catalog = is_array($json) ? $json : [];
        usort(
            $catalog,
            static fn (array $a, array $b): int => strlen((string) ($b['alias'] ?? '')) <=> strlen((string) ($a['alias'] ?? ''))
        );
        self::$catalog = $catalog;

        return self::$catalog;
    }

    /**
     * @return array<string, array{hex: string, path: string, title: string}>
     */
    private static function icons(): array
    {
        if (self::$icons !== null) {
            return self::$icons;
        }
        $path = resource_path('data/brand-icons.json');
        if (! is_readable($path)) {
            self::$icons = [];

            return self::$icons;
        }
        $json = json_decode((string) file_get_contents($path), true);
        self::$icons = is_array($json) ? $json : [];

        return self::$icons;
    }

    /**
     * @return array{r: int, g: int, b: int}
     */
    private static function hexToRgb(string $hex): array
    {
        $parsed = str_replace('#', '', $hex);
        if (strlen($parsed) === 3) {
            $parsed = implode('', array_map(fn ($c) => $c.$c, str_split($parsed)));
        }
        $value = hexdec(strlen($parsed) === 6 ? $parsed : '334155');

        return [
            'r' => ($value >> 16) & 255,
            'g' => ($value >> 8) & 255,
            'b' => $value & 255,
        ];
    }
}
