import { createLab } from './model.js';
const $=id=>document.getElementById(id);
let scenarios=[],report=null,index=0,cleanup=()=>{},token=0,selectedAssertions=[];
const status=(text,error=false)=>{$('live-status').textContent=text;$('live-status').className='status'+(error?' error':'');};
function download(name,data){const url=URL.createObjectURL(new Blob([JSON.stringify(data,null,2)],{type:'application/json'}));const a=document.createElement('a');a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}
function bind(id,event,fn){$(id).addEventListener(event,fn);}
function renderDemo(){
  cleanup();cleanup=()=>{};token++;const current=token;const lab=createLab($('variant').value),root=$('demo');root.replaceChildren();status('Выберите действие в примере.');
  $('live-note').textContent=scenarios[index].liveNote;
  if(index===0){root.innerHTML='<div class="demo-row"><div><label for="price">Цена, ₽</label><input id="price" type="number" value="199" min="0" step="0.01"></div><div><label for="quantity">Количество</label><input id="quantity" type="number" value="3" min="1" max="100"></div><div><label for="discount">Скидка, %</label><input id="discount" type="number" value="10" min="0" max="100"></div></div><button id="calculate">Рассчитать</button><output id="total"></output>';bind('calculate','click',()=>{$('total').textContent='';try{if(['price','quantity','discount'].some(id=>$(id).value.trim()===''))throw new RangeError('Заполните цену, количество и скидку');const result=lab.total(Math.round(Number($('price').value)*100),Number($('quantity').value),Number($('discount').value));$('total').textContent=(result/100).toFixed(2)+' ₽';status('Расчёт выполнен общим модулем. В исходном тесте эталон — 537,30 ₽.');}catch(e){status(e.message,true);}});}
  if(index===1){root.innerHTML='<label for="email-case">Email для проверки</label><input id="email-case" value="anna@" type="text"><button id="validate">Проверить адрес</button><p>Попробуйте anna@example.com, anna@, адрес с пробелом внутри и пустую строку.</p>';bind('validate','click',()=>status(lab.validateEmail($('email-case').value)?'Адрес принят текущей реализацией.':'Адрес отклонён текущей реализацией.'));}
  if(index===2){root.innerHTML='<div class="demo-tools"><button id="load">Загрузить клиента</button><button id="load-error">Запросить отсутствующий файл</button><button id="cancel-load" disabled>Отменить ожидание</button></div><pre id="customer">Данные ещё не загружены.</pre>';let controller=null,requestId=0;const load=async(error)=>{const request=++requestId;controller?.abort();controller=new AbortController();$('cancel-load').disabled=false;$('customer').textContent='Ожидание нового ответа…';status('Настоящий fetch к файлу этой мастерской…');try{const value=await lab.loadCustomer(fetch,error?'api/does-not-exist.json':'api/customer.json',controller.signal);if(current!==token||request!==requestId)return;$('customer').textContent=JSON.stringify(value,null,2);status('Получен ответ реального HTTP-запроса.');}catch(e){if(current!==token||request!==requestId)return;$('customer').textContent='Данные не получены.';status(e.name==='AbortError'?'Ожидание отменено.':e.message,true);}finally{if(current===token&&request===requestId)$('cancel-load').disabled=true;}};bind('load','click',()=>load(false));bind('load-error','click',()=>load(true));bind('cancel-load','click',()=>controller?.abort());cleanup=()=>{requestId++;controller?.abort();};}
  if(index===3){root.innerHTML='<label for="mock-email">Email</label><input id="mock-email" value="  anna@example.com  "><button id="save-order">Вызвать адаптер заказа</button><pre id="payload">Здесь появятся фактические аргументы вызова.</pre>';bind('save-order','click',async()=>{try{const client={post:async(url,data)=>{$('payload').textContent=JSON.stringify({url,data},null,2);return{id:42};}};await lab.saveOrder(client,$('mock-email').value,53730);status('Вызван учебный адаптер без сети. В записанном Vitest ту же границу проверяет vi.fn().');}catch(e){status(e.message,true);}});}
  if(index===4){root.innerHTML='<label for="search">Введите несколько букв быстро</label><input id="search" type="search" autocomplete="off"><button id="cancel-search">Отменить отложенные вызовы</button><pre id="search-log">Пока вызовов нет.</pre>';const log=[];const search=lab.debounceSearch(query=>{log.push({query,at:new Date().toLocaleTimeString('ru')});$('search-log').textContent=JSON.stringify(log,null,2);status('Фактических вызовов: '+log.length);},500);bind('search','input',e=>search(e.target.value));bind('cancel-search','click',()=>{search.cancel();status('Отложенные вызовы отменены.');});cleanup=()=>search.cancel();}
  if(index===5){cleanup=lab.mountContactForm(root);status('Отправьте пустое поле, затем корректный email. Сообщение формы и фокус меняются в браузере.');}
  if(index===6){cleanup=lab.mountDisclosure(root);status('Откройте параметры, затем нажмите Escape. Следите за фокусом и aria-expanded в исходниках.');}
  if(index===7){root.innerHTML='<label><input id="paid" type="checkbox">Заказ оплачен</label><button id="summary">Сформировать представление</button><pre id="snapshot-value">Результат появится после нажатия.</pre>';bind('summary','click',()=>{$('snapshot-value').textContent=JSON.stringify(lab.summary({id:42,customer:'Анна',cents:53730,paid:$('paid').checked}),null,2);status('Живое представление сформировано. Снимок из теста фиксирует заказ pending / RUB.');});}
  if(index===8){root.innerHTML='<label><input id="check-unit" type="checkbox" checked>Unit прошли</label><label><input id="check-dom" type="checkbox">DOM прошли</label><label><input id="check-build" type="checkbox" checked>Сборка прошла</label><button id="release">Проверить условие выпуска</button><p>Это учебное условие в коде, не настоящий деплой и не запуск CI.</p>';bind('release','click',()=>status(lab.canPublish([$ ('check-unit').checked,$('check-dom').checked,$('check-build').checked])?'Условие разрешает следующий шаг.':'Условие запрещает следующий шаг.'));}
}
function showAssertion(){const a=selectedAssertions[Number($('assertion').value)||0];if(!a)return;$('assertion-state').textContent=(a.status==='passed'?'ПРОЙДЕНА':'ОШИБКА')+' · '+a.title+' · '+Number(a.duration||0).toFixed(2)+' мс в записанном запуске';$('failure').textContent=a.failureMessages?.length?a.failureMessages.join('\n\n').replace(/\u001b\[[0-9;]*m/g,''):'Проверка прошла. Vitest не вернул assertion diff для этой проверки.';}
function renderRecord(){if(!report||!scenarios[index])return;const variant=$('variant').value,r=report.runs[variant],prefix=String(index+1).padStart(2,'0')+' ·';selectedAssertions=r.assertions.filter(a=>a.ancestorTitles.some(t=>t.startsWith(prefix)));$('record-meta').textContent=`Записано ${new Date(report.recordedAt).toLocaleString('ru')} · ${report.runtime.node} · Vitest ${report.runtime.vitest} · ${report.runtime.platform}`;$('record-totals').replaceChildren();for(const[text,failed]of [[`Всего: ${selectedAssertions.length}`,false],[`Прошло: ${selectedAssertions.filter(a=>a.status==='passed').length}`,false],[`Ошибок: ${selectedAssertions.filter(a=>a.status==='failed').length}`,true],[`Exit всего запуска: ${r.exitCode}`,r.exitCode!==0]]){const span=document.createElement('span');span.textContent=text;if(failed)span.className='failed';$('record-totals').append(span);}$('assertion').replaceChildren();selectedAssertions.forEach((a,i)=>{const opt=document.createElement('option');opt.value=String(i);opt.textContent=(a.status==='passed'?'✓ ':'✕ ')+a.title;$('assertion').append(opt);});const firstFailure=selectedAssertions.findIndex(a=>a.status==='failed');$('assertion').value=String(firstFailure>=0?firstFailure:0);showAssertion();$('fix').textContent=scenarios[index].fix;$('test-code').textContent=scenarios[index].testCode;$('browser-boundary').textContent=index===6?'Эта запись — DOM preflight в JSDOM. Настоящий Browser Mode и размеры viewport проверяются отдельным тестом; статус указан ниже.':'Все записи здесь выполнены настоящим Vitest. DOM API предоставлены JSDOM; HTTP-сценарий использует настоящий локальный Node-сервер.';}
function choose(i){index=i;$('scenario').value=String(i);$('purpose').textContent=scenarios[i].purpose;$('experiment').textContent='Попробуйте: '+scenarios[i].experiment;renderDemo();renderRecord();}
bind('variant','change',()=>{if(scenarios.length){renderDemo();renderRecord();}});bind('scenario','change',e=>choose(Number(e.target.value)));bind('next','click',()=>choose((index+1)%scenarios.length));bind('reset','click',()=>choose(index));bind('assertion','change',showAssertion);bind('reload','click',()=>location.reload());bind('download-report','click',()=>download('vitest-real-report.json',report));
Promise.all(['scenarios.json','reports.json'].map(url=>fetch(url).then(r=>{if(!r.ok)throw new Error('Не удалось загрузить '+url);return r.json();}))).then(([items,data])=>{scenarios=items;report=data;items.forEach((s,i)=>{const opt=document.createElement('option');opt.value=String(i);opt.textContent=`${i+1}. ${s.title}`;$('scenario').append(opt);});for(const id of ['scenario','next','reset','download-report'])$(id).disabled=false;for(const[key,label]of [['statements','Выражения'],['branches','Ветки'],['functions','Функции'],['lines','Строки']]){const box=document.createElement('div'),value=document.createElement('strong');value.textContent=report.coverage[key].pct+'%';box.append(value,document.createTextNode(label));$('coverage').append(box);}$('browser-record').textContent=report.browser.status==='not-run'?'Browser Mode: в этой записи не выполнялся. Подготовлены отдельные preview/Playwright конфигурации; JSDOM не подтверждает геометрию и touch.':report.browser.note;choose(0);}).catch(e=>{status(e.message,true);$('reload').hidden=false;});
window.addEventListener('pagehide',()=>{cleanup();token++;});
window.addEventListener('pageshow',event=>{if(event.persisted&&scenarios.length)renderDemo();});
