<?php

declare(strict_types=1);

class Slug
{
    

    private const TRANSLIT_MAP = [
        'à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ā' => 'a','ą' => 'a','ă' => 'a','æ' => 'ae',
        'ç' => 'c','ć' => 'c','č' => 'c','ĉ' => 'c','ċ' => 'c',
        'ð' => 'd','ď' => 'd','đ' => 'd',
        'è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ē' => 'e','ę' => 'e','ě' => 'e','ĕ' => 'e','ė' => 'e',
        'ĝ' => 'g','ğ' => 'g','ġ' => 'g','ģ' => 'g',
        'ĥ' => 'h','ħ' => 'h',
        'ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ī' => 'i','ĩ' => 'i','ĭ' => 'i','į' => 'i','ı' => 'i',
        'ĵ' => 'j','ķ' => 'k',
        'ł' => 'l','ľ' => 'l','ĺ' => 'l','ļ' => 'l','ŀ' => 'l',
        'ñ' => 'n','ń' => 'n','ň' => 'n','ņ' => 'n','ŋ' => 'n',
        'ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ō' => 'o','ő' => 'o','ŏ' => 'o','œ' => 'oe',
        'ŕ' => 'r','ř' => 'r','ŗ' => 'r',
        'ś' => 's','š' => 's','ş' => 's','ŝ' => 's','ș' => 's','ß' => 'ss',
        'ť' => 't','ţ' => 't','ț' => 't','ŧ' => 't',
        'ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ū' => 'u','ů' => 'u','ű' => 'u','ŭ' => 'u','ũ' => 'u','ų' => 'u',
        'ŵ' => 'w','ý' => 'y','ÿ' => 'y','ŷ' => 'y',
        'ź' => 'z','ž' => 'z','ż' => 'z','þ' => 'th','µ' => 'u',
    ];

    public static function generate(string $text): string
    {
        $original = $text;
        $text = mb_strtolower($text, 'UTF-8');
        $text = self::toAscii($text);
        $text = strtolower($text);  
        $text = preg_replace('/[^a-z0-9\s-]/', '', $text);
        $text = preg_replace('/[\s-]+/', '-', $text);
        $slug = trim($text, '-');

         
         
         
         
        if ($slug === '') {
            $slug = 'v' . substr(hash('crc32b', $original !== '' ? $original : 'untitled'), 0, 8);
        }

        return $slug;
    }

    

    private static function toAscii(string $text): string
    {
        if (function_exists('transliterator_transliterate')) {
            $out = @transliterator_transliterate('Any-Latin; Latin-ASCII; Lower()', $text);
            if (is_string($out) && $out !== '') {
                return $out;
            }
        }

        if (function_exists('iconv')) {
            $out = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
            if (is_string($out) && $out !== '') {
                return $out;
            }
        }

        return strtr($text, self::TRANSLIT_MAP);
    }

    public static function unique(string $table, string $text, ?int $excludeId = null, array $scope = []): string
    {
        $slug = self::generate($text);
        $original = $slug;
        $counter = 1;

         
        $scopeWhere = '';
        $scopeParams = [];
        foreach ($scope as $col => $val) {
            $scopeWhere .= " AND {$col} = ?";
            $scopeParams[] = $val;
        }

        while (true) {
            if ($excludeId !== null) {
                $existing = Database::fetchOne(
                    "SELECT id FROM {$table} WHERE slug = ? AND id != ?{$scopeWhere}",
                    array_merge([$slug, $excludeId], $scopeParams)
                );
            } else {
                $existing = Database::fetchOne(
                    "SELECT id FROM {$table} WHERE slug = ?{$scopeWhere}",
                    array_merge([$slug], $scopeParams)
                );
            }

            if ($existing === null) {
                break;
            }

            $slug = $original . '-' . $counter;
            $counter++;
        }

        return $slug;
    }

    public static function fromInput(string $table, mixed $submitted, mixed $fallback, ?int $excludeId = null, array $scope = []): string
    {
        $text = is_string($submitted) ? trim($submitted) : '';
        if ($text === '') {
            $text = is_string($fallback) ? $fallback : '';
        }

        return self::unique($table, $text, $excludeId, $scope);
    }

    public static function uniqueTranslation(string $text, string $langCode, ?int $excludePageId = null): string
    {
        $slug = self::generate($text);
        $original = $slug;
        $counter = 1;

        while (true) {
            if ($excludePageId !== null) {
                $existing = Database::fetchOne(
                    "SELECT id FROM page_translations WHERE slug = ? AND lang_code = ? AND page_id != ?",
                    [$slug, $langCode, $excludePageId]
                );
            } else {
                $existing = Database::fetchOne(
                    "SELECT id FROM page_translations WHERE slug = ? AND lang_code = ?",
                    [$slug, $langCode]
                );
            }

            if ($existing === null) {
                break;
            }

            $slug = $original . '-' . $counter;
            $counter++;
        }

        return $slug;
    }

    public static function uniqueVideoTranslation(string $text, string $langCode, ?int $excludeVideoId = null): string
    {
        $slug = self::generate($text);
        $original = $slug;
        $counter = 1;

        while (true) {
             
            if ($excludeVideoId !== null) {
                $existing = Database::fetchOne(
                    "SELECT id FROM video_translations WHERE slug = ? AND lang_code = ? AND video_id != ?",
                    [$slug, $langCode, $excludeVideoId]
                );
            } else {
                $existing = Database::fetchOne(
                    "SELECT id FROM video_translations WHERE slug = ? AND lang_code = ?",
                    [$slug, $langCode]
                );
            }

             
            $mainExists = Database::fetchOne(
                "SELECT id FROM videos WHERE slug = ?",
                [$slug]
            );

            if ($existing === null && $mainExists === null) {
                break;
            }

            $slug = $original . '-' . $counter;
            $counter++;
        }

        return $slug;
    }

    

    public static function uniqueEntityTranslation(
        string $translationTable,
        string $fkColumn,
        string $mainTable,
        string $text,
        string $langCode,
        ?int $excludeEntityId = null,
        string $videoType = ''
    ): string {
        $slug = self::generate($text);
        $original = $slug;
        $counter = 1;

        while (true) {
             
            if ($videoType !== '') {
                if ($excludeEntityId !== null) {
                    $existing = Database::fetchOne(
                        "SELECT ct.id FROM {$translationTable} ct
                         JOIN {$mainTable} m ON ct.{$fkColumn} = m.id
                         WHERE ct.slug = ? AND ct.lang_code = ? AND m.video_type = ? AND ct.{$fkColumn} != ?",
                        [$slug, $langCode, $videoType, $excludeEntityId]
                    );
                } else {
                    $existing = Database::fetchOne(
                        "SELECT ct.id FROM {$translationTable} ct
                         JOIN {$mainTable} m ON ct.{$fkColumn} = m.id
                         WHERE ct.slug = ? AND ct.lang_code = ? AND m.video_type = ?",
                        [$slug, $langCode, $videoType]
                    );
                }
            } else {
                if ($excludeEntityId !== null) {
                    $existing = Database::fetchOne(
                        "SELECT id FROM {$translationTable} WHERE slug = ? AND lang_code = ? AND {$fkColumn} != ?",
                        [$slug, $langCode, $excludeEntityId]
                    );
                } else {
                    $existing = Database::fetchOne(
                        "SELECT id FROM {$translationTable} WHERE slug = ? AND lang_code = ?",
                        [$slug, $langCode]
                    );
                }
            }

             
            if ($videoType !== '') {
                $mainExists = Database::fetchOne(
                    "SELECT id FROM {$mainTable} WHERE slug = ? AND video_type = ?" .
                    ($excludeEntityId !== null ? " AND id != ?" : ""),
                    $excludeEntityId !== null ? [$slug, $videoType, $excludeEntityId] : [$slug, $videoType]
                );
            } else {
                $mainExists = Database::fetchOne(
                    "SELECT id FROM {$mainTable} WHERE slug = ?" .
                    ($excludeEntityId !== null ? " AND id != ?" : ""),
                    $excludeEntityId !== null ? [$slug, $excludeEntityId] : [$slug]
                );
            }

            if ($existing === null && $mainExists === null) {
                break;
            }

            $slug = $original . '-' . $counter;
            $counter++;
        }

        return $slug;
    }
}
