<?php
declare(strict_types=1);

namespace App\Services\ZaloroxAI;

class TagSuggestionService
{
    private const MAX_TAGS = 15;

    /** @var string[] */
    private array $stopWords = [
        'a', 'an', 'and', 'as', 'at', 'by', 'for', 'from', 'in', 'is', 'it', 'of', 'on', 'or', 'the', 'to', 'with',
        've', 'veya', 'ile', 'icin', 'için', 'bir', 'bu', 'su', 'şu', 'da', 'de', 'mi', 'mu', 'mı', 'mü', 'en',
        'yeni', 'mod', 'modu', 'muzik', 'müzik', 'sarki', 'şarkı', 'parca', 'parça', 'demo', 'versiyon',
        'john', 'deere', 'turkish', 'march', 'gercekci', 'realistic',
    ];

    /** @var array<string, array<int, string>> */
    private array $modDictionary = [
        'fs25' => ['fs25', 'farming simulator'],
        'fs 25' => ['fs25', 'farming simulator'],
        'farming simulator' => ['farming simulator', 'fs25'],
        'john deere' => ['john deere', 'traktör'],
        'case ih' => ['case ih', 'traktör'],
        'new holland' => ['new holland', 'traktör'],
        'fendt' => ['fendt', 'traktör'],
        'traktor' => ['traktör', 'araç modu', 'tarım'],
        'tractor' => ['traktör', 'araç modu', 'tarım'],
        'arac' => ['araç modu'],
        'vehicle' => ['araç modu'],
        'truck' => ['kamyon', 'araç modu'],
        'kamyon' => ['kamyon', 'araç modu'],
        'car' => ['araba', 'araç modu'],
        'araba' => ['araba', 'araç modu'],
        'harita' => ['harita', 'gerçekçi harita'],
        'map' => ['harita', 'gerçekçi harita'],
        'anadolu' => ['anadolu', 'türkiye'],
        'turkiye' => ['türkiye'],
        'koy' => ['köy', 'roleplay', 'gerçekçi harita'],
        'village' => ['köy', 'roleplay'],
        'ciftlik' => ['çiftlik', 'tarım'],
        'farm' => ['çiftlik', 'tarım'],
        'tarim' => ['tarım', 'çiftlik'],
        'realistic' => ['gerçekçi mod'],
        'gercekci' => ['gerçekçi mod'],
        'roleplay' => ['roleplay'],
        'romork' => ['römork', 'araç modu'],
        'trailer' => ['römork', 'araç modu'],
        'bicerdover' => ['biçerdöver', 'tarım'],
        'harvester' => ['biçerdöver', 'tarım'],
        'pack' => ['mod paketi'],
        'paket' => ['mod paketi'],
        'skin' => ['kaplama'],
        'texture' => ['kaplama'],
    ];

    /** @var array<string, array<int, string>> */
    private array $musicDictionary = [
        'arabesk rap' => ['arabesk rap', 'duygusal', 'türkçe rap'],
        'rap' => ['rap', 'türkçe rap'],
        'turkce rap' => ['türkçe rap'],
        'phonk' => ['phonk', 'drift phonk', 'energetic'],
        'drift' => ['drift phonk', 'energetic'],
        'trap' => ['trap'],
        'remix' => ['remix'],
        'turku' => ['türkü', 'akustik'],
        'karadeniz' => ['karadeniz', 'hareketli'],
        'oyun muzigi' => ['oyun müziği', 'game soundtrack'],
        'game music' => ['oyun müziği', 'game soundtrack'],
        'mozart' => ['mozart', 'classical remix'],
        'turkish march' => ['turkish march', 'classical remix'],
        'classical' => ['classical remix'],
        'dark' => ['dark rap', 'melankolik'],
        'karanlik' => ['dark rap', 'melankolik'],
        'melankolik' => ['melankolik'],
        'duygusal' => ['duygusal', 'melankolik'],
        'energetic' => ['energetic'],
        'enerjik' => ['energetic'],
        'zalorox' => ['zalorox'],
        'beat' => ['beat'],
        'beats' => ['beat'],
    ];

    /**
     * @return string[]
     */
    public function suggest(string $type, string $title, string $description = '', string $category = ''): array
    {
        $type = $type === 'music' ? 'music' : 'mod';
        $source = trim($title . ' ' . $description . ' ' . $category);
        $normalizedSource = $this->normalize($source);
        $suggestions = [];

        $this->pushCategoryHints($suggestions, $type, $category);
        $this->pushDictionaryMatches($suggestions, $type, $normalizedSource);
        $this->pushTokenMatches($suggestions, $type, $source);
        $this->pushFallbacks($suggestions, $type, $normalizedSource);

        return $this->dedupe($suggestions);
    }

    /**
     * @param string[] $suggestions
     */
    private function pushCategoryHints(array &$suggestions, string $type, string $category): void
    {
        $category = trim($category);
        if ($category === '') {
            return;
        }

        $normalized = $this->normalize($category);
        $dictionary = $type === 'music' ? $this->musicDictionary : $this->modDictionary;

        foreach ($dictionary as $needle => $tags) {
            if (str_contains($normalized, $needle)) {
                foreach ($tags as $tag) {
                    $this->add($suggestions, $tag);
                }
            }
        }
    }

    /**
     * @param string[] $suggestions
     */
    private function pushDictionaryMatches(array &$suggestions, string $type, string $normalizedSource): void
    {
        $dictionary = $type === 'music' ? $this->musicDictionary : $this->modDictionary;

        foreach ($dictionary as $needle => $tags) {
            if (!str_contains($normalizedSource, $needle)) {
                continue;
            }

            foreach ($tags as $tag) {
                $this->add($suggestions, $tag);
            }
        }
    }

    /**
     * @param string[] $suggestions
     */
    private function pushTokenMatches(array &$suggestions, string $type, string $source): void
    {
        preg_match_all('/[\p{L}\p{N}]+/u', $source, $matches);
        $tokens = $matches[0] ?? [];
        $limit = $type === 'music' ? 5 : 6;

        foreach ($tokens as $token) {
            $token = trim((string) $token);
            $normalized = $this->normalize($token);

            if ($normalized === '' || strlen($normalized) < 3 || in_array($normalized, $this->stopWords, true)) {
                continue;
            }

            if (preg_match('/^[0-9]+$/', $normalized)) {
                continue;
            }

            $this->add($suggestions, $this->humanizeToken($token));
            if (count($suggestions) >= $limit + 8) {
                break;
            }
        }
    }

    /**
     * @param string[] $suggestions
     */
    private function pushFallbacks(array &$suggestions, string $type, string $normalizedSource): void
    {
        if ($type === 'music') {
            if (str_contains($normalizedSource, 'remix') && !str_contains($normalizedSource, 'phonk')) {
                $this->add($suggestions, 'remix');
            }

            if (count($suggestions) < 3) {
                $this->add($suggestions, 'music');
            }

            return;
        }

        if (str_contains($normalizedSource, 'fs') || str_contains($normalizedSource, 'farm')) {
            $this->add($suggestions, 'fs25');
        }

        foreach (['mod', 'fs25', 'farming simulator'] as $tag) {
            $this->add($suggestions, $tag);
        }
    }

    /**
     * @param string[] $suggestions
     */
    private function add(array &$suggestions, string $tag): void
    {
        $tag = trim(preg_replace('/\s+/', ' ', $tag) ?: '');
        $tag = trim($tag, " \t\n\r\0\x0B,.;:#");

        if ($tag === '' || strlen($this->normalize($tag)) < 2) {
            return;
        }

        $suggestions[] = function_exists('mb_strtolower') ? mb_strtolower($tag, 'UTF-8') : strtolower($tag);
    }

    /**
     * @param string[] $tags
     * @return string[]
     */
    private function dedupe(array $tags): array
    {
        $seen = [];
        $clean = [];

        foreach ($tags as $tag) {
            $key = $this->normalize($tag);
            if ($key === '' || isset($seen[$key]) || in_array($key, $this->stopWords, true)) {
                continue;
            }

            $seen[$key] = true;
            $clean[] = $tag;

            if (count($clean) >= self::MAX_TAGS) {
                break;
            }
        }

        return $clean;
    }

    private function humanizeToken(string $token): string
    {
        $token = trim(preg_replace('/\s+/', ' ', $token) ?: '');

        return function_exists('mb_strtolower') ? mb_strtolower($token, 'UTF-8') : strtolower($token);
    }

    private function normalize(string $value): string
    {
        $value = function_exists('mb_strtolower') ? mb_strtolower($value, 'UTF-8') : strtolower($value);
        $value = strtr($value, [
            'ç' => 'c',
            'ğ' => 'g',
            'ı' => 'i',
            'i̇' => 'i',
            'ö' => 'o',
            'ş' => 's',
            'ü' => 'u',
            'â' => 'a',
            'î' => 'i',
            'û' => 'u',
        ]);
        $value = preg_replace('/[^a-z0-9]+/u', ' ', $value) ?: '';

        return trim(preg_replace('/\s+/', ' ', $value) ?: '');
    }
}
