<?php

declare(strict_types=1);

class TranslateTypes
{
    public const PROMPTS = [
        'entity_name' => [
            'begin'  => 'ai_entity_translate_begin',
            'end'    => 'ai_entity_translate_end',
            'token'  => 'name',
            'dbegin' => "Translate this tag/category name into the requested languages.\nName: {name}",
            'dend'   => "Rules: Use the natural, commonly-used equivalent in each language. Keep the same meaning. Short and clean (1-5 words max). If it is a proper name, keep it as-is.\n\nReturn the translation in this EXACT format — one XML tag per language, the translated term inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
        'person_name' => [
            'begin'  => 'ai_person_name_translate_begin',
            'end'    => 'ai_person_name_translate_end',
            'token'  => 'name',
            'dbegin' => "Write this performer's name as a reader of each requested language would see it.\nName: {name}",
            'dend'   => "Rules: This is a person's name. NEVER translate its meaning and never substitute a different name. If the target language is written in the Latin alphabet, return the name EXACTLY as given, unchanged. If the target language uses another script (Arabic, Cyrillic, Greek, Hebrew, Chinese, Japanese, Korean, Thai, Devanagari and so on), write the name in that script so it reads with the same pronunciation, using the spelling a native reader would expect. Keep the given name and surname in the same order. No titles, no honorifics, no nicknames, no commentary.\n\nReturn the result in this EXACT format - one XML tag per language, the name inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
        'brand_name' => [
            'begin'  => 'ai_brand_name_translate_begin',
            'end'    => 'ai_brand_name_translate_end',
            'token'  => 'name',
            'dbegin' => "Write this studio or channel name as it should appear for readers of each requested language.\nName: {name}",
            'dend'   => "Rules: This is a brand name. NEVER translate what it means. If the target language is written in the Latin alphabet, return it EXACTLY as given, unchanged. If the target language uses another script, write it in that script so it reads with the same pronunciation, and leave untouched any part that is normally kept in Latin letters, such as a domain, an abbreviation or a number. No extra words, no explanation.\n\nReturn the result in this EXACT format - one XML tag per language, the name inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
        'title' => [
            'begin'  => 'ai_translate_begin',
            'end'    => 'ai_translate_end',
            'token'  => 'title',
            'dbegin' => 'Title: {title}',
            'dend'   => "Return the translated title in this EXACT format — one XML tag per language, the translation inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
        'desc' => [
            'begin'  => 'ai_translate_desc_begin',
            'end'    => 'ai_translate_desc_end',
            'token'  => 'description',
            'dbegin' => 'Description: {description}',
            'dend'   => "Return the translated description in this EXACT format — one XML tag per language, the translation inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
        'entity_text' => [
            'begin'  => 'ai_entity_text_translate_begin',
            'end'    => 'ai_entity_text_translate_end',
            'token'  => 'text',
            'dbegin' => "Translate this text into the requested languages.\nText: {text}",
            'dend'   => "Rules: Keep the same meaning, tone and length. NEVER translate proper names (performer names, studio names, brand names) — leave them exactly as written. Do not add or remove information. Plain text only, no markdown.\n\nReturn the translation in this EXACT format — one XML tag per language, the translation inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
        'page' => [
            'begin'  => 'ai_page_translate_begin',
            'end'    => 'ai_page_translate_end',
            'token'  => 'text',
            'dbegin' => "Translate this web page content into the requested languages.\nContent: {text}",
            'dend'   => "Rules: Preserve the HTML structure EXACTLY — same tags, same attributes, same order; translate only the human-readable text between them. Never translate URLs, email addresses, shortcodes or code samples. Keep proper names as written.\n\nReturn the translation in this EXACT format — one XML tag per language, the translated content inside each tag, nothing else (no language name, no quotes, no commentary):\n{format}",
        ],
    ];

    /**
     * How many languages one AI call may carry, per field kind.
     *
     * No model reliably returns thirty faithful translations in a single reply: the tail gets
     * dropped, shortened or silently skipped. So every field is translated in chunks, matching
     * the convention the catalogue importer has used all along
     * (`CatalogueProcessor::AI_TRANSLATE_CHUNK_SIZE = 5` for titles and descriptions).
     *
     *   name  8 — a tag or category label is a couple of words, so a wider chunk is still safe
     *   title 5 — exactly the catalogue importer's proven chunk for the same kind of string
     *   text  3 — descriptions and biographies are paragraphs; three at a time stays accurate
     *   html  1 — a page body carries markup that must survive intact, one language per call
     *
     * Chunking does NOT change what the customer pays (the price is per item, per field, per
     * language); it only changes how the work is issued, and it means one language failing no
     * longer costs the others. langsPerCall() narrows this further from the real source length.
     */
    public const LANGS_PER_CALL = [
        'name'  => 8,
        'title' => 5,
        'text'  => 3,
        'html'  => 1,
    ];

    /** Characters of translated output one reply may reasonably carry. */
    private const OUTPUT_BUDGET = 9000;

    public static function langsPerCall(string $kind, int $sourceLength): int
    {
        $cap = self::LANGS_PER_CALL[$kind] ?? 4;
        if ($sourceLength <= 0) {
            return $cap;
        }
        $byLength = (int) floor(self::OUTPUT_BUDGET / max(1, $sourceLength));
        return max(1, min($cap, $byLength));
    }

    private const F_NAME = ['src' => 'name', 'dst' => 'name', 'kind' => 'name', 'prompt' => 'entity_name', 'slug' => true, 'cache' => true, 'label' => 'Name', 'max' => 100];
    private const F_PERSON_NAME = ['src' => 'name', 'dst' => 'name', 'kind' => 'name', 'prompt' => 'person_name', 'slug' => true, 'cache' => false, 'label' => 'Name', 'max' => 100, 'hint' => 'A name is not translated: it is written in the reader\'s own script, so Arabic, Cyrillic, Chinese and the like get a readable spelling. Languages written in the Latin alphabet keep the name exactly as it is.'];
    private const F_BRAND_NAME  = ['src' => 'name', 'dst' => 'name', 'kind' => 'name', 'prompt' => 'brand_name', 'slug' => true, 'cache' => false, 'label' => 'Name', 'max' => 100, 'hint' => 'A studio name is not translated: it is written in the reader\'s own script where that differs. Languages written in the Latin alphabet keep it exactly as it is.'];
    private const F_DESC = ['src' => 'description', 'dst' => 'description', 'kind' => 'text', 'prompt' => 'entity_text', 'slug' => false, 'cache' => false, 'label' => 'Description', 'max' => 8000];
    private const F_BIO  = ['src' => 'bio', 'dst' => 'bio', 'kind' => 'text', 'prompt' => 'entity_text', 'slug' => false, 'cache' => false, 'label' => 'Biography', 'max' => 8000];

    public static function all(): array
    {
        return [
            'category' => [
                'main' => 'categories', 'translations' => 'category_translations', 'fk' => 'category_id',
                'endpoint' => 'categories', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'folder', 'group' => 'Videos',
                'title_many' => 'Categories', 'label_many' => 'categories', 'label_count' => 'category(ies)', 'label_one' => 'category',
                'fields' => ['name' => self::F_NAME, 'description' => self::F_DESC],
            ],
            'tag' => [
                'main' => 'tags', 'translations' => 'tag_translations', 'fk' => 'tag_id',
                'endpoint' => 'tags', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'tag', 'group' => 'Videos',
                'title_many' => 'Tags', 'label_many' => 'tags', 'label_count' => 'tag(s)', 'label_one' => 'tag',
                'fields' => ['name' => self::F_NAME],
            ],
            'performer' => [
                'main' => 'performers', 'translations' => 'performer_translations', 'fk' => 'performer_id',
                'endpoint' => 'performers', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'user', 'group' => 'Videos',
                'title_many' => 'Pornstars', 'label_many' => 'pornstars', 'label_count' => 'pornstar(s)', 'label_one' => 'pornstar',
                'fields' => ['name' => self::F_PERSON_NAME, 'bio' => self::F_BIO],
            ],
            'channel' => [
                'main' => 'channels', 'translations' => 'channel_translations', 'fk' => 'channel_id',
                'endpoint' => 'channels', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'building', 'group' => 'Videos',
                'title_many' => 'Channels', 'label_many' => 'channels', 'label_count' => 'channel(s)', 'label_one' => 'channel',
                'fields' => ['name' => self::F_BRAND_NAME, 'description' => self::F_DESC],
            ],
            'album_category' => [
                'main' => 'album_categories', 'translations' => 'album_category_translations', 'fk' => 'category_id',
                'endpoint' => 'albums/categories', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'folder', 'group' => 'Albums',
                'title_many' => 'Album Categories', 'label_many' => 'album categories', 'label_count' => 'album category(ies)', 'label_one' => 'album category',
                'fields' => ['name' => self::F_NAME, 'description' => self::F_DESC],
            ],
            'album_tag' => [
                'main' => 'album_tags', 'translations' => 'album_tag_translations', 'fk' => 'tag_id',
                'endpoint' => 'albums/tags', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'tag', 'group' => 'Albums',
                'title_many' => 'Album Tags', 'label_many' => 'album tags', 'label_count' => 'album tag(s)', 'label_one' => 'album tag',
                'fields' => ['name' => self::F_NAME],
            ],
            'album_performer' => [
                'main' => 'album_performers', 'translations' => 'album_performer_translations', 'fk' => 'performer_id',
                'endpoint' => 'albums/performers', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'user', 'group' => 'Albums',
                'title_many' => 'Album Pornstars', 'label_many' => 'album pornstars', 'label_count' => 'album pornstar(s)', 'label_one' => 'album pornstar',
                'fields' => ['name' => self::F_PERSON_NAME, 'bio' => self::F_BIO],
            ],
            'album_channel' => [
                'main' => 'album_channels', 'translations' => 'album_channel_translations', 'fk' => 'channel_id',
                'endpoint' => 'albums/channels', 'slug_fn' => 'entity', 'type_col' => 'video_type',
                'icon' => 'building', 'group' => 'Albums',
                'title_many' => 'Album Channels', 'label_many' => 'album channels', 'label_count' => 'album channel(s)', 'label_one' => 'album channel',
                'fields' => ['name' => self::F_BRAND_NAME, 'description' => self::F_DESC],
            ],
            'video' => [
                'main' => 'videos', 'translations' => 'video_translations', 'fk' => 'video_id',
                'endpoint' => 'videos', 'slug_fn' => 'video', 'type_col' => 'video_type',
                'icon' => 'film', 'group' => 'Content',
                'title_many' => 'Videos', 'label_many' => 'videos', 'label_count' => 'video(s)', 'label_one' => 'video',
                'fields' => [
                    'title'       => ['src' => 'title', 'dst' => 'title', 'kind' => 'title', 'prompt' => 'title', 'slug' => true, 'cache' => false, 'label' => 'Title', 'max' => 500],
                    'description' => ['src' => 'description', 'dst' => 'description', 'kind' => 'text', 'prompt' => 'desc', 'slug' => false, 'cache' => false, 'label' => 'Description', 'max' => 8000],
                ],
            ],
            'album' => [
                'main' => 'albums', 'translations' => 'album_translations', 'fk' => 'album_id',
                'endpoint' => 'albums', 'slug_fn' => 'album', 'type_col' => 'video_type',
                'icon' => 'images', 'group' => 'Content',
                'title_many' => 'Albums', 'label_many' => 'albums', 'label_count' => 'album(s)', 'label_one' => 'album',
                'fields' => [
                    'title'       => ['src' => 'title', 'dst' => 'title', 'kind' => 'title', 'prompt' => 'title', 'slug' => true, 'cache' => false, 'label' => 'Title', 'max' => 500],
                    'description' => ['src' => 'description', 'dst' => 'description', 'kind' => 'text', 'prompt' => 'desc', 'slug' => false, 'cache' => false, 'label' => 'Description', 'max' => 8000],
                ],
            ],
            'page' => [
                'main' => 'pages', 'translations' => 'page_translations', 'fk' => 'page_id',
                'endpoint' => 'pages', 'slug_fn' => 'page', 'type_col' => null,
                'icon' => 'file-lines', 'group' => 'Content',
                'title_many' => 'Pages', 'label_many' => 'pages', 'label_count' => 'page(s)', 'label_one' => 'page',
                'fields' => [
                    'title'            => ['src' => 'title', 'dst' => 'title', 'kind' => 'title', 'prompt' => 'title', 'slug' => true, 'cache' => false, 'label' => 'Title', 'max' => 500],
                    'body'             => ['src' => 'body', 'dst' => 'body', 'kind' => 'html', 'prompt' => 'page', 'slug' => false, 'cache' => false, 'label' => 'Content', 'max' => 20000],
                    'meta_title'       => ['src' => 'meta_title', 'dst' => 'meta_title', 'kind' => 'title', 'prompt' => 'title', 'slug' => false, 'cache' => false, 'label' => 'SEO title', 'max' => 500],
                    'meta_description' => ['src' => 'meta_description', 'dst' => 'meta_description', 'kind' => 'text', 'prompt' => 'entity_text', 'slug' => false, 'cache' => false, 'label' => 'SEO description', 'max' => 2000],
                ],
            ],
        ];
    }

    public static function meta(string $entityType): ?array
    {
        return self::all()[$entityType] ?? null;
    }

    public static function keys(): array
    {
        return array_keys(self::all());
    }

    public static function prompt(string $profile): array
    {
        return self::PROMPTS[$profile] ?? self::PROMPTS['entity_name'];
    }

    /** Field keys of a type, optionally narrowed to an operator-chosen subset. */
    public static function fieldKeys(array $meta, ?array $chosen = null): array
    {
        $all = array_keys($meta['fields']);
        if ($chosen === null || $chosen === []) {
            return $all;
        }
        $out = array_values(array_intersect($all, $chosen));
        return $out ?: $all;
    }
}
