import {access,readFile,readdir} from 'node:fs/promises';
import {join,extname} from 'node:path';
const root=process.cwd();
const required=['index.html','README.md','USER_GUIDE.md','AI_GUIDE.md','AI_CONTEXT.json','404.html','license.html','LICENSE.md','assets/css/tokens.css','assets/css/palettes.css','assets/css/base.css','assets/css/layout.css','assets/css/components.css','assets/css/motion.css','assets/css/print.css','assets/js/state.js','assets/js/theme.js','assets/js/i18n.js','assets/js/ui-demo.js','assets/js/projects.js','assets/js/posts.js','assets/js/comments.js','assets/js/forms.js','assets/js/company.js','assets/js/socials.js','assets/js/motion.js','assets/img/social-preview.png','edition.json'];
for(const file of required)await access(join(root,file));
const edition=JSON.parse(await readFile(join(root,'edition.json'),'utf8'));
if(edition.edition!=='commercial-fixed-theme'||edition.version!=='3.3.0'||edition.palettes.length!==1||edition.appearances.length!==1)throw new Error('Fixed-theme edition manifest is invalid.');
const palette=edition.palettes[0],appearance=edition.appearances[0],variant=`${palette}-${appearance}`;
const html=await readFile(join(root,'index.html'),'utf8');
for(const token of ['id="services"','id="work"','id="posts"','id="process"','id="about"','id="faq"','id="contact"','data-locale-toggle','data-company-dialog','data-social-dialog','data-form-dialog','data-post-dialog','data-comment-form'])if(!html.includes(token))throw new Error(`Missing required system: ${token}`);
for(const forbidden of ['data-palette-toggle','data-palette-option','data-appearance-toggle','ius-template-theme-v1'])if(html.includes(forbidden))throw new Error(`Theme switcher leaked into fixed edition: ${forbidden}`);
if(!html.includes(`data-palette="${palette}"`)||!html.includes(`data-appearance="${appearance}"`)||!html.includes(`data-theme-edition="${variant}"`))throw new Error('Fixed theme attributes are missing.');
const paletteCss=await readFile(join(root,'assets/css/palettes.css'),'utf8');
const allPalettes=['signal-noir','monolith','court-contrast'];
for(const other of allPalettes.filter(x=>x!==palette))if(paletteCss.includes(other))throw new Error(`Other palette shipped in fixed edition: ${other}`);
const themeJs=await readFile(join(root,'assets/js/theme.js'),'utf8');
if(!themeJs.includes(`const FIXED_PALETTE="${palette}"`)||!themeJs.includes(`const FIXED_APPEARANCE="${appearance}"`))throw new Error('Fixed theme module does not match edition.');
for(const forbidden of ['data-palette-toggle','data-appearance-toggle','localStorage.setItem(storageKey'])if(themeJs.includes(forbidden))throw new Error(`Dynamic theme code remains: ${forbidden}`);
const site=JSON.parse(await readFile(join(root,'content/site-config.json'),'utf8'));
if(site.theme.palette!==palette||site.theme.appearance!==appearance||site.theme.respectSystemAppearance!==false||site.theme.locked!==true)throw new Error('Site configuration does not lock the edition theme.');
const ai=JSON.parse(await readFile(join(root,'AI_CONTEXT.json'),'utf8'));
if(ai.theme?.palette!==palette||ai.theme?.appearance!==appearance||ai.theme?.locked!==true)throw new Error('AI context does not describe the fixed theme.');
const docs=(await Promise.all(['README.md','USER_GUIDE.md','AI_GUIDE.md','SUBMISSION.md'].map(f=>readFile(join(root,f),'utf8')))).join('\n');
if(!docs.includes(edition.displayName)||!docs.includes('fixed theme'))throw new Error('Fixed-theme documentation is incomplete.');
for(const other of allPalettes.filter(x=>x!==palette))if(docs.includes(other==='signal-noir'?'Signal Noir':other==='monolith'?'Monolith':'Court Contrast'))throw new Error(`Documentation mentions another sale edition: ${other}`);
if(!html.includes('href="https://iusmusic.com"')||!html.includes('I/US brand'))throw new Error('Visible linked attribution is missing.');
const licence=await readFile(join(root,'LICENSE.md'),'utf8');
if(!licence.includes('Designed by I/US brand.')||!licence.includes('https://iusmusic.com'))throw new Error('Licence attribution is incomplete.');
const translations=JSON.parse(await readFile(join(root,'content/translations.json'),'utf8'));
const enKeys=Object.keys(translations.en).sort(),jaKeys=Object.keys(translations.ja).sort();
if(enKeys.join('|')!==jaKeys.join('|'))throw new Error('Translation keys do not match.');
for(const key of enKeys)if(key.startsWith('palette.')||key.startsWith('appearance.'))throw new Error(`Unused theme-switch translation remains: ${key}`);
const posts=JSON.parse(await readFile(join(root,'content/posts.json'),'utf8')),projects=JSON.parse(await readFile(join(root,'content/projects.json'),'utf8'));
const slugs=new Set();for(const p of posts){if(slugs.has(p.slug))throw new Error(`Duplicate post slug: ${p.slug}`);slugs.add(p.slug);await access(join(root,p.image));for(const size of ['social-1200x630.png','square-1200.png','landscape-1200x900.png','wide-1600x900.png'])await access(join(root,`assets/img/calling-cards/${p.slug}/${size}`))}
for(const p of projects)await access(join(root,p.image));
function rgbToHsl(r,g,b){r/=255;g/=255;b/=255;const max=Math.max(r,g,b),min=Math.min(r,g,b),d=max-min,l=(max+min)/2;let h=0,s=0;if(d){s=d/(1-Math.abs(2*l-1));if(max===r)h=60*(((g-b)/d)%6);else if(max===g)h=60*((b-r)/d+2);else h=60*((r-g)/d+4);if(h<0)h+=360}return{h,s,l}}
function parseHex(hex){let v=hex.slice(1);if(v.length===3||v.length===4)v=v.slice(0,3).split('').map(c=>c+c).join('');if(v.length===8)v=v.slice(0,6);if(v.length!==6)return null;return[parseInt(v.slice(0,2),16),parseInt(v.slice(2,4),16),parseInt(v.slice(4,6),16)]}
async function walk(dir){const out=[];for(const e of await readdir(dir,{withFileTypes:true})){const p=join(dir,e.name);if(e.isDirectory())out.push(...await walk(p));else out.push(p)}return out}
const colourFiles=(await walk(join(root,'assets'))).filter(p=>['.css','.svg','.js'].includes(extname(p)));for(const file of colourFiles){const text=await readFile(file,'utf8');for(const match of text.matchAll(/#[0-9a-fA-F]{3,8}\b/g)){const rgb=parseHex(match[0]);if(!rgb)continue;const {h,s,l}=rgbToHsl(...rgb);if(s>.28&&l>.12&&l<.92&&h>=15&&h<=50)throw new Error(`Forbidden orange or amber UI colour ${match[0]} in ${file}`)}}
console.log(`Validated fixed commercial edition ${edition.displayName}, ${posts.length} posts, ${projects.length} projects and ${enKeys.length} bilingual strings.`);
