'use strict';
const $ = (id) => document.getElementById(id);
const enc = new TextEncoder();
let fixtures = [], index = 0, controller = null, generation = 0, last = null;
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 130"><rect width="420" height="130" rx="18" fill="#073b35"/><g fill="#e6f5d5" font-family="sans-serif" font-size="19" text-anchor="middle"><text x="72" y="70">Markdown</text><text x="210" y="70">PHP</text><text x="348" y="70">HTML</text></g><g stroke="#64d3bd" stroke-width="3"><path d="M135 64h35m-10-8 10 8-10 8M250 64h35m-10-8 10 8-10 8"/></g></svg>';
const imageData = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
const previewCSS = 'body{font:15px/1.7 system-ui,sans-serif;color:#173e34;padding:16px;overflow-wrap:anywhere}h1{font-size:27px}h2{font-size:22px}h3{font-size:18px}pre{overflow:auto;background:#edf3eb;padding:13px;border-radius:8px}code{font-family:monospace}table{border-collapse:collapse;display:block;overflow:auto}td,th{border:1px solid #bccfc0;padding:8px;min-width:80px}img{max-width:100%;height:auto}blockquote{border-left:3px solid #89af8e;padding-left:15px;margin-left:0}a{color:#096751}.lab-note{display:inline-block;background:#f2edc8;border-left:3px solid #bb992f;padding:7px 10px;border-radius:4px}.table-of-contents{background:#eff5e9;padding:16px 16px 16px 36px}input{min-width:16px;min-height:16px}';

function status(text, type = '') { $('status').textContent = text; $('status').className = 'status ' + type; }
function stop() { generation++; if (controller) controller.abort(); controller = null; $('cancel').disabled = true; $('convert').disabled = fixtures.length === 0; }
function clearResult() {
  last = null; $('document').innerHTML = '<option>Нет результата</option>'; $('document').disabled = true;
  $('preview').srcdoc = '<!doctype html><html lang="ru"><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; style-src \'unsafe-inline\'"><style>body{font:15px/1.6 system-ui;padding:18px;color:#52665f}</style><p>Нажмите «Преобразовать в PHP», чтобы увидеть результат.</p></html>';
  $('html-code').textContent = ''; $('metrics').textContent = 'Результат появится после преобразования.';
  ['download-html', 'download-json', 'copy-html'].forEach(id => $(id).disabled = true);
}
function bytes() {
  const documents = fixtures[index]?.batch ? $('markdown').value.split(/^---DOC---\s*$/m) : [$('markdown').value];
  const n = documents.reduce((sum, text) => sum + enc.encode(text).byteLength, 0);
  $('bytes').textContent = `${n.toLocaleString('ru')} / 8192 байта UTF-8${fixtures[index]?.batch ? ' суммарно · документов: ' + documents.length : ''}`;
  $('bytes').className = 'small' + (n > 8192 ? ' over' : '');
}
function loadScenario(i) {
  stop(); index = i; const item = fixtures[index]; $('scenario').value = String(i);
  $('markdown').value = item.markdown; $('profile').value = item.profile; $('policy').value = 'escape';
  $('purpose').textContent = item.purpose; $('experiment').textContent = 'Попробуйте: ' + item.experiment;
  $('input-hint').textContent = item.batch ? 'Пакет из 1–5 документов. Разделяйте их отдельной строкой ---DOC---. Общий лимит — 8 КиБ.' : 'Редактируйте пример или вставьте свой Markdown. Лимит — 8192 байта, русские буквы занимают больше одного байта.';
  $('editor-label').textContent = item.batch ? 'Markdown нескольких документов' : 'Исходный текст';
  clearResult(); bytes(); status('Пример загружен. Измените текст или сразу нажмите «Преобразовать в PHP».');
}
function isolatedDocument(html) {
  // A detached template keeps image resources inert while preview policy is applied.
  const template = document.createElement('template'); template.innerHTML = html;
  template.content.querySelectorAll('a').forEach(a => { const href = a.getAttribute('href') || ''; if (!href.startsWith('#')) { a.removeAttribute('href'); a.setAttribute('aria-disabled', 'true'); a.title = 'Переход отключён в предпросмотре'; } a.removeAttribute('target'); });
  template.content.querySelectorAll('img').forEach(img => { if (img.getAttribute('src') === 'assets/diagram.svg') { img.setAttribute('src', imageData); } else { const note = document.createElement('span'); note.textContent = '[Изображение отключено: ' + (img.getAttribute('alt') || 'без описания') + ']'; img.replaceWith(note); } });
  return '<!doctype html><html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; img-src data:; style-src \'unsafe-inline\'; base-uri \'none\'; form-action \'none\'"><style>' + previewCSS + '</style></head><body>' + template.innerHTML + '</body></html>';
}
function showDocument() {
  if (!last) return; const result = last.results[Number($('document').value) || 0];
  $('html-code').textContent = result.html; $('preview').srcdoc = isolatedDocument(result.html);
  $('metrics').textContent = `${result.engine} · ${result.inputBytes} → ${result.outputBytes} байт · ${result.elapsedMs} мс на PHP-конвертацию · ${result.profile} / ${result.htmlPolicy}`;
}
async function convert(forceError = false) {
  if (!fixtures[index]) { status('Примеры ещё не загружены. Если загрузка завершилась ошибкой, обновите страницу и повторите.', 'error'); return; }
  stop(); clearResult(); const id = generation; controller = new AbortController(); $('convert').disabled = true; $('cancel').disabled = false;
  const data = { profile: $('profile').value, htmlPolicy: $('policy').value };
  if (forceError) data.markdown = 'x'.repeat(8193);
  else if (fixtures[index].batch) data.documents = $('markdown').value.split(/^---DOC---\s*$/m);
  else data.markdown = $('markdown').value;
  status(forceError ? 'Отправляем 8193 байта, чтобы проверить настоящий ответ 413…' : 'PHP разбирает Markdown…', 'pending');
  const timeout = setTimeout(() => { if (generation === id) controller?.abort('timeout'); }, 10000);
  try {
    const response = await fetch('convert.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), signal: controller.signal, credentials: 'omit', cache: 'no-store' });
    const payload = await response.json(); if (id !== generation) return;
    if (!response.ok || !payload.ok) throw new Error(`HTTP ${response.status}: ${payload.error || 'Ошибка конвертации'}`);
    last = payload; $('document').innerHTML = ''; payload.results.forEach((r, i) => { const option = document.createElement('option'); option.value = String(i); option.textContent = 'Документ ' + (i + 1); $('document').append(option); });
    $('document').disabled = payload.results.length === 1; showDocument();
    ['download-html', 'download-json', 'copy-html'].forEach(id => $(id).disabled = false);
    status(`Готово: ${payload.results.length === 1 ? 'документ преобразован' : 'преобразовано документов: ' + payload.results.length}. Это ответ PHP на ваш текст.`);
  } catch (error) {
    if (id !== generation) return;
    status(error.name === 'AbortError' || error === 'timeout' ? 'Ожидание прервано. Можно запустить преобразование снова.' : (error instanceof SyntaxError ? 'Сервер не вернул JSON. Проверьте PHP, Composer и путь convert.php.' : error.message), 'error');
  } finally { clearTimeout(timeout); if (id === generation) { controller = null; $('convert').disabled = false; $('cancel').disabled = true; } }
}
function download(name, content, type) { const url = URL.createObjectURL(new Blob([content], {type})); const a = document.createElement('a'); a.href = url; a.download = name; a.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); }
$('convert').addEventListener('click', () => convert());
$('error-demo').addEventListener('click', () => convert(true));
$('cancel').addEventListener('click', () => { stop(); status('Ожидание отменено. Текст сохранён в редакторе; повторите преобразование, когда будете готовы.'); });
$('scenario').addEventListener('change', e => loadScenario(Number(e.target.value)));
$('next').addEventListener('click', () => loadScenario((index + 1) % fixtures.length));
$('reset').addEventListener('click', () => loadScenario(index));
$('markdown').addEventListener('input', () => { stop(); bytes(); clearResult(); status('Текст изменён. Преобразуйте его, чтобы получить актуальный HTML.'); });
[$('profile'), $('policy')].forEach(el => el.addEventListener('change', () => { stop(); clearResult(); status('Настройки изменены. Нажмите «Преобразовать в PHP».'); }));
$('document').addEventListener('change', showDocument);
function setView(html) { $('html-output').hidden = !html; $('preview').hidden = html; $('show-html').setAttribute('aria-pressed', String(html)); $('show-preview').setAttribute('aria-pressed', String(!html)); $('show-html').className = 'btn ' + (html ? 'btn-dark' : 'btn-outline-dark'); $('show-preview').className = 'btn ' + (html ? 'btn-outline-dark' : 'btn-dark'); }
$('show-preview').addEventListener('click', () => setView(false)); $('show-html').addEventListener('click', () => setView(true));
$('download-html').addEventListener('click', () => { if(last) download('markdown-result.html', isolatedDocument(last.results[Number($('document').value) || 0].html), 'text/html;charset=utf-8'); });
$('download-json').addEventListener('click', () => { if(last) download('markdown-result.json', JSON.stringify(last, null, 2), 'application/json'); });
$('copy-html').addEventListener('click', async () => { if (!last) return; try { await navigator.clipboard.writeText(last.results[Number($('document').value) || 0].html); status('Точный HTML из PHP скопирован. Предпросмотр имеет дополнительные ограничения.'); } catch { setView(true); status('Буфер обмена недоступен. Выделите HTML и скопируйте его вручную.'); } });
['scenario', 'next', 'reset', 'error-demo', 'convert'].forEach(id => $(id).disabled = true);
$('retry-fixtures').addEventListener('click', () => location.reload());
fetch('fixtures.json').then(r => { if(!r.ok) throw new Error('Не удалось загрузить примеры.'); return r.json(); }).then(data => { fixtures = data; data.forEach((item, i) => { const opt = document.createElement('option'); opt.value = String(i); opt.textContent = `${i + 1}. ${item.title}`; $('scenario').append(opt); }); ['scenario', 'next', 'reset', 'error-demo', 'convert'].forEach(id => $(id).disabled = false); loadScenario(0); }).catch(e => { status(e.message, 'error'); $('convert').disabled = true; $('retry-fixtures').hidden = false; });
