<?php
declare(strict_types=1);

namespace App\Support;

final class TextNormalizer
{
    /** @var array<string,array<string,string>> */
    private const FOLDING_BY_SCRIPT = [
        'arabic' => [
            "\u{0623}" => "\u{0627}",
            "\u{0625}" => "\u{0627}",
            "\u{0622}" => "\u{0627}",
            "\u{0671}" => "\u{0627}",
            "\u{0649}" => "\u{064A}",
            "\u{06CC}" => "\u{064A}",
            "\u{0629}" => "\u{0647}",
            "\u{0624}" => "\u{0648}",
            "\u{0626}" => "\u{064A}",
        ],
        'latin' => [],
        'cyrillic' => [],
        'hebrew' => [],
        'han' => [],
    ];

    /** @var array<string,string> */
    private const SCRIPT_BY_LOCALE = [
        'ar' => 'arabic',
        'fa' => 'arabic',
        'ps' => 'arabic',
        'ur' => 'arabic',
    ];

    public function normalize(string $value, ?string $locale = null): string
    {
        $value = trim($value);
        if ($value === '') {
            return '';
        }

        if (class_exists('Normalizer')) {
            $normalized = \Normalizer::normalize($value, \Normalizer::FORM_KC);
            if (is_string($normalized)) {
                $value = $normalized;
            }
        }

        $value = str_replace("\u{0640}", '', $value);
        $value = function_exists('mb_strtolower') ? mb_strtolower($value, 'UTF-8') : strtolower($value);
        $value = preg_replace('/[\p{Mn}\p{Me}]+/u', '', $value) ?? $value;
        $value = strtr($value, $this->foldingMap($locale));
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;

        return trim($value);
    }

    /** @return array<string,string> */
    private function foldingMap(?string $locale): array
    {
        $locale = strtolower(str_replace('_', '-', trim((string) $locale)));
        $script = self::SCRIPT_BY_LOCALE[$locale] ?? null;
        if ($script !== null) {
            return self::FOLDING_BY_SCRIPT[$script];
        }

        $map = [];
        foreach (self::FOLDING_BY_SCRIPT as $scriptMap) {
            $map = array_merge($map, $scriptMap);
        }
        return $map;
    }
}
