// Re-verify harness AFTER fixes. Mirrors the modified generators.
const assert = require('assert');
const HOSTILE = 'a < b & "c" \'d\', e\n<script>alert(1)</script> ✺😀';
const PAGE = 'BlogyAdvanced';
const raters = [
  { key:'h', emoji:'👂', n:3, max:5 }, { key:'f', emoji:'🖐️', n:0, max:5 },
  { key:'l', emoji:'👁️', n:5, max:5 }, { key:'t', emoji:'👅', n:0, max:5 },
  { key:'s', emoji:'👃', n:2, max:5 },
];
const phrase = HOSTILE;
const picked = ['🌶️', HOSTILE];

const esc = s => String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
const escAttr = esc;
const safeName = s => String(s).toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');
const SUMMARY_WORD={en:'summary',es:'resumen'};
const activeLang=()=>'es'; // <html lang="es">
const fileBase=()=>`${safeName(PAGE)}-${SUMMARY_WORD[activeLang()]||'summary'}`;

function toTxt(){ const l=[phrase,'']; raters.filter(r=>r.n>0).forEach(r=>l.push(`${r.emoji} ${r.n}/${r.max}`)); if(picked.length)l.push('',picked.join(' ')); return l.join('\n'); }
function toJson(){ return JSON.stringify({phrase,raters,picked},null,2); }
const csvQ=v=>{const s=String(v);return /[",\n]/.test(s)?`"${s.replace(/"/g,'""')}"`:s;};
function toCsv(){ const head='emoji,key,n,max'; const rows=raters.map(r=>[r.emoji,r.key,r.n,r.max].map(csvQ).join(',')); return [head].concat(rows).join('\n'); }
function toXml(){ let s=`<?xml version="1.0" encoding="UTF-8"?>\n<blogyadvanced>\n  <phrase>${esc(phrase)}</phrase>\n  <raters>\n`; raters.forEach(x=>s+=`    <rater key="${x.key}" emoji="${escAttr(x.emoji)}" n="${x.n}" max="${x.max}"/>\n`); s+=`  </raters>\n  <picked>${esc(picked.join(' '))}</picked>\n</blogyadvanced>\n`; return s; }
function toHtml(){ const lang=activeLang(),word=SUMMARY_WORD[lang]||'summary'; return `<!doctype html>\n<html lang="${escAttr(lang)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${esc(PAGE)} · ${esc(word)}</title>\n<style>body{}</style></head>\n<body><h1>${esc(PAGE)}</h1><pre>${esc(phrase)}</pre>\n<pre>${esc(toTxt())}</pre></body></html>`; }

const out=[]; const ok=(n,f)=>{try{f();out.push('PASS  '+n);}catch(e){out.push('FAIL  '+n+' :: '+e.message);}};

ok('JSON round-trip + parity + raters n=[3,0,5,0,2] (Views read same array)',()=>{
  const j=JSON.parse(toJson());
  assert.strictEqual(j.phrase,phrase);
  assert.deepStrictEqual(j.raters.map(r=>r.n),[3,0,5,0,2]);
  assert.strictEqual(j.picked.join('|'),picked.join('|'));
});
ok('XML well-formed + hostile escaped (no raw script/quote)',()=>{
  const x=toXml();
  assert.ok(x.includes('&lt;script&gt;'),'script must be escaped');
  assert.ok(!/<phrase>[^]*?<script>/.test(x),'raw script leaked');
  // emoji attr (no quote here, but escAttr guards future) — ensure no stray "
  const attrSeg=x.match(/emoji="[^"]*"/g); assert.ok(attrSeg.length===5,'attrs intact');
});
ok('XML DOMParser parse-back (xmldom)',()=>{
  let DOMParser; try{({DOMParser}=require('@xmldom/xmldom'));}catch(_){throw new Error('xmldom not installed — skip');}
  let err=null; const doc=new DOMParser({onError:(l,m)=>err=err||m}).parseFromString(toXml(),'text/xml');
  assert.ok(doc && !err, 'parse error: '+err);
  const items=doc.getElementsByTagName('rater'); assert.strictEqual(items.length,5);
});
ok('HTML no injection — script inert, escaped',()=>{
  const h=toHtml(); const body=h.split('<body>')[1];
  assert.ok(!/<script>alert\(1\)<\/script>/.test(body),'INJECTION');
  assert.ok(body.includes('&lt;script&gt;'));
});
ok('HTML i18n — lang=es, title carries localized word "resumen"',()=>{
  const h=toHtml(); assert.ok(h.includes('lang="es"')); assert.ok(h.includes('· resumen'));
});
ok('CSV RFC-4180 — comma/quote/newline field survives in one column',()=>{
  // simulate a rater emoji that contains a comma (hostile)
  const r=[{key:'k',emoji:'a, "b"\nc',n:1,max:5}];
  const line=[r[0].emoji,r[0].key,r[0].n,r[0].max].map(csvQ).join(',');
  assert.ok(line.startsWith('"a, ""b""\nc"'),'field not quoted: '+JSON.stringify(line));
  // current real raters (comma-free) stay byte-identical
  assert.strictEqual(csvQ('🖐️'),'🖐️');
});
ok('Filename localized to blogyadvanced-resumen.<ext>',()=>{
  assert.strictEqual(fileBase(),'blogyadvanced-resumen');
});

console.log(out.join('\n'));
