import { chromium } from 'playwright';
import { writeFile, mkdir } from 'node:fs/promises';
import assert from 'node:assert/strict';
import { serve } from './serve.mjs';
import { ADS, getFormat } from '../src/config.js';
const {server,url}=await serve();const browser=await chromium.launch({args:['--no-sandbox','--disable-dev-shm-usage'],...(process.env.CHROMIUM_PATH?{executablePath:process.env.CHROMIUM_PATH}:{})});
await mkdir('qa',{recursive:true});const errors=[];
try{
const page=await browser.newPage({viewport:{width:1280,height:940}});page.on('pageerror',e=>errors.push(e.message));
await page.goto(url);await page.waitForFunction(()=>window.preview);await page.evaluate(()=>window.preview.seek(4));
await page.screenshot({path:'qa/preview-desktop.png',fullPage:true});
for(const ad of Object.keys(ADS)){
 await page.locator(`[data-ad="${ad}"]`).click();assert.equal(await page.evaluate(()=>window.preview.state.ad),ad);
 await page.evaluate(()=>window.preview.seek(7));assert.equal(await page.evaluate(()=>window.preview.state.playing),false);
 await page.locator('#play').click();assert.equal(await page.evaluate(()=>window.preview.state.playing),true);
 await page.locator('#restart').click();assert.ok((await page.evaluate(()=>window.preview.state.time))<1);
}
await page.locator('#loop').click();await page.evaluate(()=>{window.preview.seek(14.9)});await page.locator('#play').click();await page.waitForTimeout(350);
assert.equal(await page.evaluate(()=>window.preview.state.playing),false);assert.equal(await page.evaluate(()=>window.preview.state.time),15);
await page.locator('#safe').click();assert.equal(await page.locator('#safe').getAttribute('aria-pressed'),'true');
await page.setViewportSize({width:390,height:844});await page.evaluate(()=>{window.preview.select('lighting');window.preview.seek(4);});await page.screenshot({path:'qa/preview-mobile.png',fullPage:true});
assert.ok(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth));
for(const width of [320,375,768]){await page.setViewportSize({width,height:900});assert.ok(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),`Overflow at ${width}`);}
await page.setViewportSize({width:1280,height:1040});
await page.evaluate(()=>{window.preview.select('side');window.preview.seek(4);});
assert.equal(await page.locator('#format-size').textContent(),'64 × 512');
assert.equal(await page.locator('#download').getAttribute('href'),'/output/bocna-reklama.mp4');
let box=await page.locator('#motion').boundingBox();assert.equal(box.width,64);assert.equal(box.height,512);
await page.screenshot({path:'qa/side-preview.png',fullPage:true});
await page.locator('#compare').click();assert.equal(await page.locator('#compare').getAttribute('aria-pressed'),'true');
for(const front of ['electro','lighting','lifts']){
 await page.locator('#front-ad').selectOption(front);await page.evaluate(()=>window.preview.seek(4));
 await page.screenshot({path:`qa/side-with-${front}.png`,fullPage:true});
}
for(const width of [320,390,768]){
 await page.setViewportSize({width,height:900});
 assert.ok(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),`Side comparison overflow at ${width}`);
 const side=await page.locator('#motion').boundingBox(),front=await page.locator('#companion').boundingBox();
 assert.ok(Math.abs(front.width/side.width-5)<.02);assert.ok(Math.abs(front.height-side.height)<.2);
}
await page.setViewportSize({width:390,height:900});await page.screenshot({path:'qa/side-mobile.png',fullPage:true});
await page.locator('#compare').click();box=await page.locator('#motion').boundingBox();assert.equal(box.width,64);assert.equal(box.height,512);
await page.locator('[data-ad="side"]').focus();await page.keyboard.press('ArrowDown');assert.equal(await page.evaluate(()=>window.preview.state.ad),'electro');
await page.keyboard.press('ArrowUp');assert.equal(await page.evaluate(()=>window.preview.state.ad),'side');
await page.goto(`${url}/render.html`);await page.waitForFunction(()=>window.ready);
for(const ad of Object.keys(ADS)){
 const result=await page.evaluate(async ({ad,width,height,safeX,safeY})=>{
  const slot=Math.max(160,width),src=document.querySelector('canvas'),sheet=document.createElement('canvas');sheet.width=slot*5;sheet.height=552;const c=sheet.getContext('2d');c.fillStyle='#edf1f6';c.fillRect(0,0,sheet.width,552);c.font='16px sans-serif';const violations=[];
  for(const [i,t] of [1.7,4,7.7,9.5,12.5].entries()){const bounds=window.renderFrame(ad,t);violations.push(...bounds.filter(b=>b.left<safeX-.5||b.right>width-safeX+.5||b.top<safeY-.5||b.bottom>height-safeY+.5));const snapshot=new Image();snapshot.src=src.toDataURL();await snapshot.decode();c.drawImage(snapshot,i*slot+(slot-width)/2,40,width,height);c.fillStyle='#102344';c.fillText(`${ad} · ${t.toFixed(1)}s`,i*slot+12,25);}
  window.renderFrame(ad,4);const first=src.toDataURL();window.renderFrame(ad,8);window.renderFrame(ad,4);const deterministic=first===src.toDataURL();
  window.renderFrame(ad,0);const opening=src.toDataURL();window.renderFrame(ad,15);const seamless=opening===src.toDataURL();
  return{png:sheet.toDataURL().split(',')[1],violations,deterministic,seamless};
 },{ad,...getFormat(ad)});
 await writeFile(`qa/${ad}-storyboard.png`,Buffer.from(result.png,'base64'));
 assert.ok(result.deterministic,`${ad} is not deterministic`);assert.ok(result.seamless,`${ad} loop mismatch`);assert.deepEqual(result.violations,[],`${ad} unsafe text`);
}
assert.deepEqual(errors,[]);await writeFile('qa/browser-checks.json',JSON.stringify({passed:true,checks:['Four film selectors and keyboard wrap','Play/pause/restart','Loop off stops at 15s','Safe-zone toggle','320/375/390/768px overflow','Exact 64×512 side preview','All three front/side comparisons and 5:1 width ratio','Side download link and metadata','Deterministic frame replay','Matching loop endpoints','Storyboard text safe zone','No browser errors']},null,2));
console.log('Browser QA passed. Storyboards and screenshots saved in qa/.');
}finally{await browser.close();await new Promise(resolve=>server.close(resolve));}
