<?php

$_atType = $aiTranslateEntityType ?? 'tag';
$_atMeta = EntityTranslateProcessor::typeMeta($_atType);
if ($_atMeta === null) { $_atType = 'tag'; $_atMeta = EntityTranslateProcessor::typeMeta('tag'); }
$_atEndpoint = $_atMeta['endpoint'];
$_atTitle    = $_atMeta['title_many'];
$_atLower    = $_atMeta['label_many'];
$_atIcon     = $_atMeta['icon'];
$_atTypeArg  = $aiTranslateTypeFilter ?? '';

$_atCreditRate = (static function () {
    $r = Setting::get('catalogue_credit_rate', '');
    return $r !== '' ? (float) $r : 0.01;
})();
?>

<style>
.at-chip{display:inline-flex;align-items:center;gap:.35rem;padding:.3rem .55rem;border:1px solid #e2e8f0;background:#fff;border-radius:8px;font-size:11px;color:#475569;cursor:pointer;transition:all .12s;user-select:none;line-height:1.1}
.at-chip:hover{border-color:#bae6fd;background:#f0f9ff}
.at-chip.on{border-color:#0ea5e9;background:#e0f2fe;color:#0369a1;font-weight:600}
.at-chip img{width:16px;height:12px;border-radius:2px;flex-shrink:0}
.at-chip .at-tick{width:12px;text-align:center;color:#0ea5e9;opacity:0}
.at-chip.on .at-tick{opacity:1}
.at-langs{display:flex;flex-wrap:wrap;gap:.35rem;max-height:168px;overflow-y:auto;padding:.15rem}
.at-fieldrow{display:flex;flex-wrap:wrap;gap:.4rem}
.at-mini{font-size:10.5px;color:#94a3b8}
.at-linkbtn{font-size:11px;color:#0284c7;background:none;border:none;padding:0;cursor:pointer}
.at-linkbtn:hover{text-decoration:underline}
@media (max-width:640px){.at-langs{max-height:132px}}
</style>

<div id="atOverlay" class="hidden fixed inset-0 bg-black/50 backdrop-blur-sm z-[70] transition-opacity"></div>
<div id="atModal" class="hidden fixed inset-0 z-[70] flex items-center justify-center p-4">
    <div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg flex flex-col" style="max-height:min(92vh,760px)" onclick="event.stopPropagation()">

        <div class="px-6 pt-5 pb-4 border-b border-gray-100 shrink-0">
            <div class="flex items-start justify-between">
                <div>
                    <h3 class="text-base font-bold text-gray-900 flex items-center gap-2">
                        <i class="fa-solid fa-language text-sky-500"></i>
                        <span>AI Translate <?= htmlspecialchars($_atTitle) ?></span>
                    </h3>
                    <p class="text-xs text-gray-500 mt-0.5" id="atSubtitle">Preparing…</p>
                </div>
                <button type="button" onclick="AT.close()" class="p-1.5 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors">
                    <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
                </button>
            </div>
        </div>

        <div id="atStep1" class="px-6 py-5 space-y-4 overflow-y-auto">

            <div id="atLoading" class="text-center py-8">
                <svg class="w-6 h-6 mx-auto mb-2 animate-spin text-gray-300" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/></svg>
                <p class="text-sm text-gray-400">Preparing translation…</p>
            </div>

            <div id="atSetup" class="hidden space-y-4">

                <div id="atFieldsBox" class="hidden">
                    <div class="flex items-center justify-between mb-1.5">
                        <span class="text-[11px] font-bold text-gray-900 uppercase tracking-wider">What to translate</span>
                    </div>
                    <div class="at-fieldrow" id="atFields"></div>
                    <p id="atFieldHint" class="hidden mt-1.5 text-[11px] leading-relaxed text-gray-500"></p>
                </div>

                <div>
                    <div class="flex items-center justify-between mb-1.5">
                        <span class="text-[11px] font-bold text-gray-900 uppercase tracking-wider"><span id="atLangTitle">Target languages</span> <span class="text-gray-400 font-medium normal-case" id="atLangSummary"></span></span>
                        <span class="flex items-center gap-2" id="atLangPick">
                            <button type="button" class="at-linkbtn" onclick="AT.allLangs(true)">Select all</button>
                            <span class="text-gray-200">|</span>
                            <button type="button" class="at-linkbtn" onclick="AT.allLangs(false)">None</button>
                        </span>
                    </div>
                    <div class="at-langs" id="atLangs"></div>
                    <p class="at-mini mt-1.5">Your site language is never translated — it is the source.</p>
                </div>

                <div class="rounded-xl border border-gray-200 overflow-hidden">
                    <div class="px-4 py-2.5 flex items-center justify-between text-sm border-b border-gray-100">
                        <span class="flex items-center gap-2 text-gray-700">
                            <i class="fa-solid fa-<?= htmlspecialchars($_atIcon) ?> text-gray-400 text-xs"></i>
                            <span id="atItemLabel">Items to translate</span>
                        </span>
                        <span class="font-semibold text-gray-900" id="atItemCount">0</span>
                    </div>
                    <div class="px-4 py-3 bg-sky-50/60">
                        <div id="atBreakdown" class="space-y-1 mb-1.5"></div>
                        <div class="flex items-center justify-between pt-1.5 border-t border-sky-200">
                            <span class="text-sm font-bold text-gray-900" id="atTotalLabel">Total cost</span>
                            <span class="flex items-baseline gap-1.5">
                                <span class="text-base font-bold text-sky-700" id="atTotalCost">0.00 Cr</span>
                                <span class="text-xs font-medium text-gray-400" id="atTotalCostUsd"></span>
                            </span>
                        </div>
                    </div>
                </div>

                <div class="flex items-center justify-between px-4 py-2.5 rounded-xl border border-gray-200">
                    <span class="text-sm text-gray-600">Your balance</span>
                    <span class="text-sm font-bold" id="atBalance">0.00 Cr</span>
                </div>

                <div id="atWarnings" class="hidden space-y-2"></div>

                <div id="atNotConnectedWarn" class="hidden rounded-xl bg-blue-50 border border-blue-200 p-4">
                    <div class="flex items-start gap-3">
                        <div class="w-9 h-9 rounded-lg bg-blue-100 flex items-center justify-center shrink-0">
                            <i class="fa-solid fa-link text-blue-600"></i>
                        </div>
                        <div class="flex-1 min-w-0">
                            <div class="text-sm font-bold text-blue-800">Connect your TubePress account</div>
                            <p class="text-xs text-blue-700 mt-1 leading-relaxed">AI translation runs on your TubePress credits. Connecting takes a few seconds.</p>
                        </div>
                    </div>
                    <div class="flex items-center gap-2 mt-3 ml-12">
                        <button type="button" onclick="AT.connect()" class="inline-flex items-center gap-1.5 px-4 py-2 text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors">
                            <i class="fa-solid fa-link"></i> Connect
                        </button>
                    </div>
                </div>

                <div id="atInsufficientWarn" class="hidden rounded-xl bg-amber-50 border border-amber-300 p-4">
                    <div class="flex items-start gap-3">
                        <div class="w-9 h-9 rounded-lg bg-amber-100 flex items-center justify-center shrink-0">
                            <i class="fa-solid fa-triangle-exclamation text-amber-600"></i>
                        </div>
                        <div class="flex-1 min-w-0">
                            <div class="text-sm font-bold text-amber-800">Not enough credits for everything</div>
                            <p class="text-xs text-amber-700 mt-1 leading-relaxed" id="atInsufficientMsg"></p>
                        </div>
                    </div>
                    <div class="flex items-center gap-2 mt-3 ml-12">
                        <button type="button" onclick="AT.openTopUp()" class="inline-flex items-center gap-1.5 px-4 py-2 text-sm font-semibold text-white bg-amber-500 hover:bg-amber-600 rounded-lg transition-colors">
                            <i class="fa-solid fa-plus"></i> Buy credits
                        </button>
                    </div>
                </div>

                <label class="flex items-start gap-2 cursor-pointer">
                    <input type="checkbox" id="atAutoNew" onchange="AT.setAuto(this)" <?= EntityTranslateProcessor::autoEnabled($_atType) ? 'checked' : '' ?> class="w-4 h-4 mt-0.5 rounded border-gray-300 text-emerald-600 focus:ring-emerald-500">
                    <span class="text-xs text-gray-600">
                        <i class="fa-solid fa-bolt text-emerald-500 mr-0.5"></i>
                        Keep new <?= htmlspecialchars($_atLower) ?> translated automatically
                        <span class="block at-mini">Runs in the background whenever something new appears, using the languages enabled on your site.</span>
                    </span>
                </label>
            </div>
        </div>

        <div id="atStep2" class="hidden px-6 py-5 space-y-4 overflow-y-auto">
            <div class="space-y-2">
                <div class="flex items-center justify-between text-xs text-gray-500">
                    <span id="atProgressLabel">Translating…</span>
                    <span id="atProgressPct">0%</span>
                </div>
                <div class="w-full h-2 bg-gray-100 rounded-full overflow-hidden">
                    <div id="atProgressBar" class="h-full bg-sky-500 rounded-full transition-all duration-300" style="width:0%"></div>
                </div>
            </div>
            <div class="grid grid-cols-3 gap-2 text-center">
                <div class="rounded-lg border border-gray-200 py-2">
                    <div class="text-sm font-bold text-green-600" id="atDoneCount">0</div>
                    <div class="at-mini">done</div>
                </div>
                <div class="rounded-lg border border-gray-200 py-2">
                    <div class="text-sm font-bold text-red-500" id="atFailCount">0</div>
                    <div class="at-mini">failed</div>
                </div>
                <div class="rounded-lg border border-gray-200 py-2">
                    <div class="text-sm font-bold text-sky-600" id="atChargedCount">0.00</div>
                    <div class="at-mini">credits used</div>
                </div>
            </div>
            <div id="atPausedBox" class="hidden rounded-xl bg-amber-50 border border-amber-300 px-3 py-2.5 text-xs text-amber-800 flex items-start gap-2">
                <i class="fa-solid fa-circle-pause mt-0.5"></i>
                <span id="atPausedMsg"></span>
            </div>
            <div class="space-y-2">
                <div class="flex items-center gap-2 px-3 py-2 bg-blue-50 border border-blue-100 rounded-lg text-[11px] text-blue-600">
                    <i class="fa-solid fa-info-circle shrink-0"></i>
                    <span>You can close this window — the run continues in the background.</span>
                </div>
                <div class="flex items-center gap-2 px-3 py-2 bg-gray-50 border border-gray-200 rounded-lg text-[11px] text-gray-500">
                    <i class="fa-solid fa-coins shrink-0"></i>
                    <span>You are charged as it goes, only for what actually gets translated.</span>
                </div>
            </div>
        </div>

        <div id="atStep3" class="hidden px-6 py-5 text-center overflow-y-auto">
            <div class="w-14 h-14 mx-auto mb-4 bg-green-100 rounded-full flex items-center justify-center">
                <svg class="w-7 h-7 text-green-600" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>
            </div>
            <h4 class="text-lg font-bold text-gray-900 mb-1" id="atCompleteTitle">Translation complete</h4>
            <p class="text-sm text-gray-600 mb-4" id="atCompleteSummary"></p>
            <div class="inline-flex items-center gap-1.5 px-3 py-1.5 bg-green-50 border border-green-200 rounded-full text-xs font-semibold text-green-700">
                <i class="fa-solid fa-coins"></i> Balance: <span id="atFinalBalance">0.00</span> Cr
            </div>
        </div>

        <div class="px-6 py-3.5 bg-gray-50/80 border-t border-gray-100 flex items-center justify-between shrink-0">
            <button type="button" id="atCancelBtn" onclick="AT.close()" class="text-sm text-gray-400 hover:text-gray-600 transition-colors">Cancel</button>
            <div class="flex items-center gap-2.5">
                <button type="button" id="atRetryBtn" onclick="AT.retry()" class="hidden inline-flex items-center gap-1.5 px-4 py-2 text-sm font-semibold text-amber-700 bg-amber-50 hover:bg-amber-100 border border-amber-200 rounded-lg transition-colors">
                    <i class="fa-solid fa-rotate-right"></i> Retry failed
                </button>
                <button type="button" id="atPauseBtn" onclick="AT.pause()" class="hidden inline-flex items-center gap-1.5 px-4 py-2 text-sm font-semibold text-gray-600 bg-white hover:bg-gray-50 border border-gray-300 rounded-lg transition-colors">
                    <i class="fa-solid fa-pause"></i> Pause
                </button>
                <button type="button" id="atResumeBtn" onclick="AT.resume()" class="hidden inline-flex items-center gap-1.5 px-4 py-2 text-sm font-semibold text-emerald-700 bg-emerald-50 hover:bg-emerald-100 border border-emerald-200 rounded-lg transition-colors">
                    <i class="fa-solid fa-play"></i> Resume
                </button>
                <button type="button" id="atStopBtn" onclick="AT.stop()" class="hidden inline-flex items-center gap-1.5 px-4 py-2 text-sm font-semibold text-red-600 bg-red-50 hover:bg-red-100 border border-red-200 rounded-lg transition-colors">
                    <i class="fa-solid fa-stop"></i> Stop
                </button>
                <button type="button" id="atStartBtn" onclick="AT.start()" class="inline-flex items-center gap-1.5 px-5 py-2 text-sm font-semibold text-white bg-sky-600 hover:bg-sky-700 rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed" disabled>
                    <i class="fa-solid fa-language"></i> Start
                </button>
                <button type="button" id="atCloseBtn" onclick="AT.closeAndReload()" class="hidden inline-flex items-center gap-1.5 px-5 py-2 text-sm font-semibold text-white bg-green-600 hover:bg-green-700 rounded-lg transition-colors">
                    <i class="fa-solid fa-check"></i> Done
                </button>
            </div>
        </div>
    </div>
</div>

<script>
(function () {
    var ENDPOINT = <?= json_encode($_atEndpoint) ?>;
    var LABEL    = <?= json_encode($_atLower) ?>;
    var TYPEFILT = <?= json_encode($_atTypeArg) ?>;
    var CSRF     = <?= json_encode(Router::csrfToken()) ?>;
    var RATE     = <?= json_encode((float) $_atCreditRate) ?>;
    var POLL_MS  = 2000;

    var state = {
        mode: 'all', ids: [], jobId: null, pollTimer: null, quoteTimer: null,
        langs: [], fields: [], allLangs: [], allFields: [], fieldHints: {}, quote: null, closing: false,
        preset: null, lockLangs: false, oneLabel: ''
    };

    function $(id) { return document.getElementById(id); }
    function show(el) { if (el) el.classList.remove('hidden'); }
    function hide(el) { if (el) el.classList.add('hidden'); }
    function esc(s) { var d = document.createElement('div'); d.textContent = s == null ? '' : s; return d.innerHTML; }
    function cr(n) { return (parseFloat(n) || 0).toFixed(2) + ' Cr'; }
    function usd(n) {
        var v = (parseFloat(n) || 0) * RATE;
        if (v <= 0) return '';
        var s = v.toFixed(2);
        if (parseFloat(s) === 0) s = v.toFixed(4);
        return '~$' + s;
    }
    function api(path, body) {
        var opts = { credentials: 'same-origin' };
        if (body !== undefined) {
            opts.method = 'POST';
            opts.headers = { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF };
            opts.body = JSON.stringify(body);
        }
        return fetch('/admin/' + ENDPOINT + '/' + path, opts).then(function (r) { return r.json(); });
    }

    var AT = window.AT = {

        open: function (mode, ids, opts) {
            opts = opts || {};
            state.mode = mode;
            state.ids = ids || [];
            state.jobId = null;
            state.closing = false;
            state.preset = (opts.langs && opts.langs.length) ? opts.langs.slice() : null;
            state.lockLangs = !!opts.lock;
            state.oneLabel = opts.label || '';
            show($('atOverlay')); show($('atModal'));
            show($('atStep1')); hide($('atStep2')); hide($('atStep3'));
            show($('atLoading')); hide($('atSetup'));
            show($('atStartBtn')); hide($('atStopBtn')); hide($('atCloseBtn'));
            hide($('atPauseBtn')); hide($('atResumeBtn')); hide($('atRetryBtn'));
            show($('atCancelBtn'));
            $('atStartBtn').disabled = true;

            api('ai-translate-status').then(function (d) {
                if (d && d.active) { state.jobId = d.job_id; AT.showProgress(d); return; }
                AT.quote(true);
            }).catch(function () { AT.quote(true); });
        },

        quote: function (first) {
            var body = {
                all: state.mode === 'all',
                ids: state.mode === 'all' ? [] : state.ids,
                type_filter: TYPEFILT
            };
            if (!first) { body.languages = state.langs; body.fields = state.fields; }
            else if (state.preset) { body.languages = state.preset; }

            api('ai-translate-init', body).then(function (d) {
                if (d.error) { AT.fatal(d.error); return; }
                AT.render(d, first);
            }).catch(function () { AT.fatal('Connection error. Please try again.'); });
        },

        fatal: function (msg) {
            hide($('atLoading')); show($('atSetup'));
            $('atSetup').innerHTML = '<div class="rounded-xl bg-red-50 border border-red-200 p-4 text-sm text-red-700"><i class="fa-solid fa-circle-exclamation mr-1"></i> ' + esc(msg) + '</div>';
            $('atStartBtn').disabled = true;
            $('atSubtitle').textContent = '';
        },

        render: function (d, first) {
            hide($('atLoading')); show($('atSetup'));
            state.quote = d;

            if (first) {
                state.allLangs = d.languages.map(function (l) { return l.code; });
                state.langs = d.languages.filter(function (l) { return l.selected; }).map(function (l) { return l.code; });
                state.allFields = d.fields.map(function (f) { return f.key; });
                state.fields = d.fields.filter(function (f) { return f.selected; }).map(function (f) { return f.key; });
                AT.paintLangs(d.languages);
                AT.paintFields(d.fields);
                if (state.lockLangs) {
                    var box = $('atLangs');
                    var names = d.languages.filter(function (l) { return l.selected; }).map(function (l) { return l.name; });
                    box.innerHTML = '<div class="at-chip on" style="cursor:default"><i class="fa-solid fa-check at-tick"></i>' + esc(names.join(', ')) + '</div>';
                    $('atLangPick').style.display = 'none';
                    $('atLangTitle').textContent = 'Target language';
                }
            }

            var n = d.count || 0;
            $('atItemCount').textContent = n.toLocaleString();
            $('atItemLabel').textContent = d.exact ? ('Items needing translation') : ('Items in scope (maximum)');
            $('atSubtitle').textContent = n === 0
                ? ('Nothing left to translate in ' + LABEL)
                : (n.toLocaleString() + ' ' + LABEL + ' · ' + state.langs.length + ' language' + (state.langs.length === 1 ? '' : 's'));
            $('atLangSummary').textContent = state.lockLangs ? '' : (state.langs.length + '/' + state.allLangs.length + ' selected');

            $('atTotalLabel').textContent = d.exact ? 'Total cost' : 'Maximum cost';
            $('atTotalCost').textContent = cr(d.total_cost);
            $('atTotalCostUsd').textContent = usd(d.total_cost);
            var bal = $('atBalance');
            bal.textContent = cr(d.balance);
            bal.className = 'text-sm font-bold ' + (d.sufficient ? 'text-green-600' : 'text-red-600');

            AT.paintBreakdown(d);

            var warn = $('atWarnings');
            if (d.warnings && d.warnings.length) {
                warn.innerHTML = d.warnings.map(function (w) {
                    return '<div class="flex items-start gap-2 px-3 py-2 bg-amber-50 border border-amber-200 rounded-lg text-xs text-amber-800"><i class="fa-solid fa-circle-info text-amber-500 mt-0.5 shrink-0"></i><span>' + esc(w) + '</span></div>';
                }).join('');
                show(warn);
            } else { hide(warn); }

            hide($('atNotConnectedWarn'));
            hide($('atInsufficientWarn'));

            var btn = $('atStartBtn');
            if (d.not_connected) {
                show($('atNotConnectedWarn')); btn.disabled = true;
                btn.innerHTML = '<i class="fa-solid fa-language"></i> Start';
            } else if (n === 0) {
                btn.disabled = true;
                btn.innerHTML = '<i class="fa-solid fa-check text-green-300"></i> Nothing to do';
            } else if (state.langs.length === 0) {
                btn.disabled = true;
                btn.innerHTML = '<i class="fa-solid fa-language"></i> Pick a language';
            } else if (state.fields.length === 0) {
                btn.disabled = true;
                btn.innerHTML = '<i class="fa-solid fa-language"></i> Pick a field';
            } else if (!d.sufficient) {
                show($('atInsufficientWarn'));
                var covered = d.covered_items || 0;
                $('atInsufficientMsg').innerHTML = 'This run needs <strong>' + cr(d.total_cost) + '</strong> and you have <strong>' + cr(d.balance) +
                    '</strong>. You can still start — it will translate about <strong>' + covered.toLocaleString() + '</strong> ' + esc(LABEL) +
                    ' and pause when the balance runs out. Nothing is charged for what it does not translate.';
                btn.disabled = false;
                btn.innerHTML = '<i class="fa-solid fa-language"></i> Start anyway';
            } else {
                btn.disabled = false;
                btn.innerHTML = '<i class="fa-solid fa-language"></i> Start';
            }
        },

        paintBreakdown: function (d) {
            var box = $('atBreakdown');
            var prices = d.unit_prices || {};
            var rows = [];
            (d.fields || []).forEach(function (f) {
                if (state.fields.indexOf(f.key) === -1) return;
                var p = prices[f.kind];
                if (p === undefined) return;
                rows.push('<div class="flex items-center justify-between text-[11px] text-gray-500"><span>' + esc(f.label) +
                    '</span><span>' + parseFloat(p).toFixed(2) + ' Cr &times; ' + state.langs.length + ' lang</span></div>');
            });
            box.innerHTML = rows.join('');
        },

        paintFields: function (fields) {
            var box = $('atFields');
            if (!fields || fields.length < 2) { hide($('atFieldsBox')); return; }
            show($('atFieldsBox'));
            state.fieldHints = {};
            (fields || []).forEach(function (f) { if (f.hint) state.fieldHints[f.key] = f.hint; });
            box.innerHTML = fields.map(function (f) {
                return '<button type="button" class="at-chip' + (f.selected ? ' on' : '') + '" data-field="' + esc(f.key) + '" onclick="AT.toggleField(this)"' +
                    (f.hint ? ' title="' + esc(f.hint) + '"' : '') + '>' +
                    '<i class="fa-solid fa-check at-tick"></i>' + esc(f.label) + '</button>';
            }).join('');
            AT.paintFieldHint();
        },

        // Says plainly what picking a field buys, so nobody pays for a language that
        // will hand the name straight back unchanged.
        paintFieldHint: function () {
            var el = $('atFieldHint');
            if (!el) return;
            var hints = (state.fields || [])
                .map(function (k) { return (state.fieldHints || {})[k]; })
                .filter(function (h) { return !!h; });
            if (!hints.length) { el.classList.add('hidden'); el.innerHTML = ''; return; }
            el.classList.remove('hidden');
            el.innerHTML = hints.map(function (h) {
                return '<i class="fa-solid fa-circle-info"></i> ' + esc(h);
            }).join('<br>');
        },

        paintLangs: function (langs) {
            $('atLangs').innerHTML = langs.map(function (l) {
                return '<button type="button" class="at-chip' + (l.selected ? ' on' : '') + '" data-lang="' + esc(l.code) + '" onclick="AT.toggleLang(this)" title="' + esc(l.name) + '">' +
                    '<img src="https://flagcdn.com/16x12/' + esc(l.flag) + '.png" alt="">' + esc(l.name) + '</button>';
            }).join('');
        },

        toggleField: function (el) {
            var k = el.dataset.field;
            var i = state.fields.indexOf(k);
            if (i === -1) { state.fields.push(k); el.classList.add('on'); }
            else { state.fields.splice(i, 1); el.classList.remove('on'); }
            AT.paintFieldHint();
            AT.requote();
        },

        toggleLang: function (el) {
            var c = el.dataset.lang;
            var i = state.langs.indexOf(c);
            if (i === -1) { state.langs.push(c); el.classList.add('on'); }
            else { state.langs.splice(i, 1); el.classList.remove('on'); }
            AT.requote();
        },

        allLangs: function (on) {
            state.langs = on ? state.allLangs.slice() : [];
            Array.prototype.forEach.call($('atLangs').querySelectorAll('[data-lang]'), function (el) {
                el.classList.toggle('on', on);
            });
            AT.requote();
        },

        requote: function () {
            $('atStartBtn').disabled = true;
            $('atLangSummary').textContent = state.lockLangs ? '' : (state.langs.length + '/' + state.allLangs.length + ' selected');
            if (state.quoteTimer) clearTimeout(state.quoteTimer);
            if (state.langs.length === 0 || state.fields.length === 0) {
                AT.render(Object.assign({}, state.quote, { total_cost: 0, sufficient: true, warnings: [] }), false);
                return;
            }
            state.quoteTimer = setTimeout(function () { AT.quote(false); }, 320);
        },

        start: function () {
            var btn = $('atStartBtn');
            btn.disabled = true;
            btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Starting…';
            api('ai-translate-start', {
                scope: state.mode === 'all' ? 'all' : 'ids',
                ids: state.mode === 'all' ? [] : state.ids,
                languages: state.langs,
                fields: state.fields,
                type_filter: TYPEFILT
            }).then(function (d) {
                if (d.error) {
                    btn.disabled = false;
                    btn.innerHTML = '<i class="fa-solid fa-language"></i> Start';
                    alert(d.error);
                    return;
                }
                state.jobId = d.job_id;
                AT.showProgress({ total: state.quote ? state.quote.count : 0, processed: 0, translated: 0, failed: 0, charged: 0 });
            }).catch(function () {
                btn.disabled = false;
                btn.innerHTML = '<i class="fa-solid fa-language"></i> Start';
                alert('Connection error. Please try again.');
            });
        },

        showProgress: function (d) {
            hide($('atStep1')); show($('atStep2')); hide($('atStep3'));
            hide($('atStartBtn')); hide($('atCloseBtn')); hide($('atRetryBtn'));
            show($('atStopBtn')); hide($('atCancelBtn'));
            AT.updateProgress(d);
            AT.startPolling();
        },

        startPolling: function () {
            if (state.pollTimer) clearInterval(state.pollTimer);
            state.pollTimer = setInterval(AT.poll, POLL_MS);
        },

        poll: function () {
            var url = 'ai-translate-status' + (state.jobId ? '?job_id=' + state.jobId : '');
            fetch('/admin/' + ENDPOINT + '/' + url, { credentials: 'same-origin' })
                .then(function (r) { return r.json(); })
                .then(function (d) {
                    if (!d.active) {
                        clearInterval(state.pollTimer); state.pollTimer = null;
                        AT.updateProgress(d);
                        AT.finish(d);
                        return;
                    }
                    AT.updateProgress(d);
                }).catch(function () {});
        },

        updateProgress: function (d) {
            var total = d.total || 1;
            var processed = d.processed || 0;
            var pct = Math.min(100, Math.round((processed / total) * 100));

            $('atDoneCount').textContent = (d.translated || 0).toLocaleString();
            $('atFailCount').textContent = (d.failed || 0).toLocaleString();
            $('atChargedCount').textContent = (parseFloat(d.charged) || 0).toFixed(2);
            $('atProgressBar').style.width = pct + '%';
            $('atProgressPct').textContent = pct + '%';
            $('atProgressLabel').textContent = 'Translating ' + processed.toLocaleString() + ' / ' + total.toLocaleString() + '…';

            if (d.paused) {
                show($('atPausedBox'));
                $('atPausedMsg').textContent = d.reason || 'Paused.';
                hide($('atPauseBtn')); show($('atResumeBtn'));
                $('atProgressLabel').textContent = 'Paused at ' + processed.toLocaleString() + ' / ' + total.toLocaleString();
            } else {
                hide($('atPausedBox'));
                show($('atPauseBtn')); hide($('atResumeBtn'));
            }
        },

        finish: function (d) {
            hide($('atStep2')); show($('atStep3'));
            hide($('atStopBtn')); hide($('atPauseBtn')); hide($('atResumeBtn'));
            show($('atCloseBtn')); show($('atCancelBtn'));

            var cancelled = d.status === 'cancelled';
            $('atCompleteTitle').textContent = cancelled ? 'Translation stopped' : 'Translation complete';
            var parts = [];
            if ((d.translated || 0) > 0) parts.push((d.translated || 0).toLocaleString() + ' translated');
            if ((d.failed || 0) > 0) parts.push((d.failed || 0).toLocaleString() + ' failed');
            if ((parseFloat(d.charged) || 0) > 0) parts.push(cr(d.charged) + ' used');
            if ((parseFloat(d.refunded) || 0) > 0) parts.push(cr(d.refunded) + ' refunded');
            $('atCompleteSummary').textContent = parts.join(' · ') || 'Nothing needed translating.';

            if ((d.failed || 0) > 0) { show($('atRetryBtn')); }

            fetch('/admin/catalogue/balance', { credentials: 'same-origin' })
                .then(function (r) { return r.json(); })
                .then(function (b) {
                    if (b && typeof b.credits !== 'undefined') $('atFinalBalance').textContent = parseFloat(b.credits).toFixed(2);
                }).catch(function () {});
        },

        pause: function () {
            api('ai-translate-pause', { job_id: state.jobId }).then(AT.poll).catch(function () {});
        },

        resume: function () {
            api('ai-translate-resume', { job_id: state.jobId }).then(AT.poll).catch(function () {});
        },

        retry: function () {
            var btn = $('atRetryBtn');
            btn.disabled = true;
            api('ai-translate-retry', { job_id: state.jobId }).then(function () {
                btn.disabled = false;
                AT.showProgress({ total: 0, processed: 0, translated: 0, failed: 0, charged: 0 });
            }).catch(function () { btn.disabled = false; });
        },

        stop: function () {
            var btn = $('atStopBtn');
            btn.disabled = true;
            btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Stopping…';
            api('ai-translate-cancel', { job_id: state.jobId }).then(function () {
                btn.disabled = false;
                btn.innerHTML = '<i class="fa-solid fa-stop"></i> Stop';
                AT.poll();
            }).catch(function () {
                btn.disabled = false;
                btn.innerHTML = '<i class="fa-solid fa-stop"></i> Stop';
            });
        },

        close: function () {
            if (state.pollTimer) clearInterval(state.pollTimer);
            state.pollTimer = null;
            hide($('atOverlay')); hide($('atModal'));
        },

        closeAndReload: function () {
            if (state.pollTimer) clearInterval(state.pollTimer);
            hide($('atOverlay')); hide($('atModal'));
            location.reload();
        },

        connect: function () {
            if (typeof MP !== 'undefined' && MP.autoConnect) {
                MP.autoConnect();
                window.addEventListener('message', function handler(e) {
                    if (!e.data || e.data.type !== 'tubepress-connect') return;
                    window.removeEventListener('message', handler);
                    fetch('/admin/catalogue/save-config', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                        body: JSON.stringify({
                            catalogue_api_key: e.data.api_key,
                            catalogue_api_secret: e.data.api_secret,
                            catalogue_api_url: 'https://tubepress.io/v1'
                        })
                    }).then(function () {
                        show($('atLoading')); hide($('atSetup'));
                        AT.quote(false);
                    });
                });
            } else {
                window.open('/admin/import', '_blank');
            }
        },

        openTopUp: function () {
            if (typeof MP !== 'undefined' && MP.openTopUpModal) {
                hide($('atOverlay')); hide($('atModal'));
                MP.openTopUpModal();
                var iv = setInterval(function () {
                    var m = document.getElementById('mpTopUpModal');
                    if (m && m.classList.contains('hidden')) {
                        clearInterval(iv);
                        show($('atOverlay')); show($('atModal'));
                        show($('atLoading')); hide($('atSetup'));
                        AT.quote(false);
                    }
                }, 500);
            } else {
                window.open('/admin/import', '_blank');
            }
        },

        setAuto: function (cb) {
            var on = !!cb.checked;
            api('ai-translate-auto', { enabled: on }).then(function (d) {
                if (!d || !d.success) cb.checked = !on;
            }).catch(function () { cb.checked = !on; });
        },

        disableAuto: function () {
            fetch('/admin/' + ENDPOINT + '/ai-translate-auto', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                body: JSON.stringify({ enabled: false }),
                credentials: 'same-origin'
            }).then(function () { location.reload(); }).catch(function () { location.reload(); });
        },

        translateAll: function () { AT.open('all', []); },

        translateOne: function (id, lang, label) {
            AT.open('selected', [parseInt(id, 10)], lang ? { langs: [lang], lock: true, label: label } : {});
        },

        translateSelected: function () {
            var ids = [];
            Array.prototype.forEach.call(document.querySelectorAll('[data-bulk-id]:checked'), function (cb) {
                ids.push(parseInt(cb.dataset.bulkId, 10));
            });
            if (!ids.length) { alert('Select at least one item first.'); return; }
            AT.open('selected', ids);
        }
    };
})();
</script>
