// Client-side vehicle name translator.
//
// Loads the localized vehicle name map produced by the bot's
// init_vehicle_translation_cache() (BOT/utils.py) and exposes:
//
//   1. window.vehicleI18n.translate(internal, fallback, lang?)
//      Synchronous lookup. Returns the localized name when available,
//      otherwise English, otherwise the supplied fallback.
//
//   2. window.vehicleI18n.apply(root?)
//      Walks `root` (defaults to <body>) and rewrites the textContent of
//      every element carrying `data-vehicle-internal="<cdk>"` to the
//      localized name. The element's *original* text is captured into
//      `data-vehicle-fallback` on first apply so re-applies (e.g. after
//      lang switch) don't lose the fallback.
//
// On startup the module:
//   - Eagerly loads the multilang map (via window.apiClient — /api/* is
//     gated by the website's apiSecurityCheck middleware).
//   - On first successful load + DOMContentLoaded, runs apply(document.body)
//     and installs a MutationObserver so any future inserts auto-translate.
//
// Server-side EJS or client-side template strings only need to add the
// data attribute — no per-page wiring required.

(function () {
    const STORAGE_KEY = 'vehicleI18nCache_v2';
    const TTL_MS = 24 * 60 * 60 * 1000;

    let _map = null;       // { internal: { en, ru, ... } }
    let _source = null;    // 'multilang' | 'english_only' | 'none'
    let _loadingPromise = null;
    let _observerInstalled = false;

    function readCache() {
        try {
            const raw = localStorage.getItem(STORAGE_KEY);
            if (!raw) return null;
            const parsed = JSON.parse(raw);
            if (!parsed || !parsed.fetchedAt || (Date.now() - parsed.fetchedAt) > TTL_MS) return null;
            if (parsed.source !== 'multilang') return null;
            return parsed.vehicles || null;
        } catch (e) {
            return null;
        }
    }

    function writeCache(vehicles, source) {
        if (source !== 'multilang') return;
        try {
            localStorage.setItem(STORAGE_KEY, JSON.stringify({ fetchedAt: Date.now(), source, vehicles }));
        } catch (e) {
            // quota exceeded or storage disabled
        }
    }

    function fireReady(detail) {
        // Defer to next tick so any listeners installed after this module
        // executes still see the event.
        setTimeout(() => document.dispatchEvent(new CustomEvent('vehicle-i18n-ready', { detail })), 0);
    }

    async function ensureLoaded() {
        if (_map) return _map;
        const cached = readCache();
        if (cached) {
            _map = cached;
            _source = 'multilang';
            fireReady({ source: 'multilang', cached: true });
            return _map;
        }
        if (_loadingPromise) return _loadingPromise;
        _loadingPromise = (async () => {
            try {
                if (!window.apiClient) {
                    await new Promise((resolve) => {
                        const tick = () => window.apiClient ? resolve() : setTimeout(tick, 50);
                        tick();
                    });
                }
                const body = await window.apiClient.request('/api/i18n/vehicles');
                _map = body && body.vehicles ? body.vehicles : {};
                _source = body && body.source ? body.source : 'none';
                writeCache(_map, _source);
                fireReady({ source: _source });
                return _map;
            } catch (e) {
                console.error('vehicle i18n load failed', e);
                _map = {};
                _source = 'none';
                return _map;
            } finally {
                _loadingPromise = null;
            }
        })();
        return _loadingPromise;
    }

    function currentLang() {
        const m = (document.cookie || '').match(/(?:^|;\s*)lang=([\w-]+)/);
        return (m && m[1]) || (document.documentElement.lang || 'en');
    }

    // Case-insensitive map lookup. The DB historically stored mixed casings of
    // vehicle_internal (e.g. germ_leopard_I vs germ_leopard_i), and the API
    // now lowercases them; the bot's translation cache may or may not match
    // that casing depending on its source. Try the literal key first, then
    // lowercase, then a one-time-built lowercased index for everything else.
    let _lowerIndex = null;
    function lookup(internal) {
        if (!_map || !internal) return null;
        if (_map[internal]) return _map[internal];
        const lower = String(internal).toLowerCase();
        if (_map[lower]) return _map[lower];
        if (!_lowerIndex) {
            _lowerIndex = {};
            for (const k of Object.keys(_map)) _lowerIndex[k.toLowerCase()] = _map[k];
        }
        return _lowerIndex[lower] || null;
    }

    function translate(internal, fallback, lang) {
        const lng = lang || currentLang();
        if (!_map || !internal) return fallback || internal || '';
        const entry = lookup(internal);
        if (!entry) return fallback || internal;
        const localized = entry[lng] || entry.en || fallback || internal;
        // The player-rendered display the DB stores can have a leading
        // country-leak / event glyph (▄ ◘ ◢ ␗ etc.) that WT's client prepends
        // at draw time. The translation map only knows the canonical name
        // without that prefix, so naive replacement would lose it — making
        // e.g. "▄F-16A ADF" (Italy) and "F-16A ADF" (no leak) look identical.
        // Preserve any leading run of non-letter / non-digit / non-space chars
        // from the original fallback when the translation doesn't already
        // include it.
        if (fallback) {
            const m = String(fallback).match(/^[^\p{L}\p{N}\s]+/u);
            if (m && !localized.startsWith(m[0])) return m[0] + localized;
        }
        return localized;
    }

    function applyToElement(el) {
        if (!el || !el.dataset || !el.dataset.vehicleInternal) return;
        const internal = el.dataset.vehicleInternal;
        if (!internal) return;
        // Capture the rendered fallback once so language re-switches still have
        // something to fall back on if the map ever loses an entry.
        if (el.dataset.vehicleFallback === undefined) {
            el.dataset.vehicleFallback = el.textContent || '';
        }
        const lng = currentLang();
        // Cache the lang we last applied so we don't fight the DOM on every
        // mutation tick.
        if (el.dataset.vehicleAppliedLang === lng) return;
        el.textContent = translate(internal, el.dataset.vehicleFallback, lng);
        el.dataset.vehicleAppliedLang = lng;
    }

    function apply(root) {
        if (!_map) return;
        const node = root || document.body;
        if (!node) return;
        if (node.nodeType === 1 && node.dataset && node.dataset.vehicleInternal) {
            applyToElement(node);
        }
        if (node.querySelectorAll) {
            node.querySelectorAll('[data-vehicle-internal]').forEach(applyToElement);
        }
    }

    function installMutationObserver() {
        if (_observerInstalled || typeof MutationObserver === 'undefined' || !document.body) return;
        _observerInstalled = true;
        const obs = new MutationObserver((mutations) => {
            for (const m of mutations) {
                for (const node of m.addedNodes) {
                    if (node.nodeType === 1) apply(node);
                }
                if (m.type === 'attributes' && m.target && m.target.dataset && m.target.dataset.vehicleInternal) {
                    delete m.target.dataset.vehicleAppliedLang;
                    applyToElement(m.target);
                }
            }
        });
        obs.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['data-vehicle-internal'],
        });
    }

    function autoApply() {
        if (!document.body) return;
        apply(document.body);
        installMutationObserver();
    }

    document.addEventListener('vehicle-i18n-ready', autoApply);

    window.vehicleI18n = {
        ensureLoaded,
        translate,
        apply,
        get ready() { return _map !== null; },
        get source() { return _source; },
        get currentLang() { return currentLang(); },
        invalidate() {
            try { localStorage.removeItem(STORAGE_KEY); } catch (e) { /* ignore */ }
            _map = null;
            _source = null;
            _lowerIndex = null;
            // Also clear apply-state markers so a refresh re-translates.
            document.querySelectorAll('[data-vehicle-applied-lang]').forEach(el => {
                delete el.dataset.vehicleAppliedLang;
            });
        },
    };

    // Kick off load. apply() runs from the ready event handler above.
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', ensureLoaded);
    } else {
        ensureLoaded();
    }
})();
