import { createRequire } from 'node:module';
import { access, mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { referenceNames, slug } from './catalog.mjs';

const require = createRequire(import.meta.url);
let sharp;
try {
  sharp = require('/Users/moto/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/sharp');
} catch {
  sharp = null;
}

const palettes = [
  ['#06b6d4','#123f57','#061019'],['#8b5cf6','#33215e','#090713'],['#ef7d32','#6e2f1b','#160905'],
  ['#49c98b','#174b38','#06110d'],['#f1c45a','#5f4314','#171005'],['#e65170','#5f192b','#18070b']
];

function hash(value) {
  let h = 2166136261;
  for (const c of value) h = Math.imul(h ^ c.charCodeAt(0), 16777619);
  return h >>> 0;
}

function esc(value) {
  return String(value).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'}[c]));
}

function symbolFor(name, accent) {
  const n = name.toLowerCase();
  const gold = 'url(#gold)';
  if (/wormhole|portal|stargate/.test(n)) return `<g fill="none" stroke="${gold}" stroke-width="22"><ellipse cx="512" cy="510" rx="240" ry="130"/><ellipse cx="512" cy="510" rx="162" ry="82"/><path d="M280 512c92-210 372-210 464 0M280 512c92 210 372 210 464 0" stroke="${accent}"/></g><circle cx="512" cy="510" r="46" fill="url(#core)"/>`;
  if (/grey|pleiadian|reptilian|mantis|starseed/.test(n)) return `<path d="M512 250c-128 0-210 84-194 205 18 135 109 249 194 249s176-114 194-249c16-121-66-205-194-205Z" fill="url(#enamel)" stroke="${gold}" stroke-width="18"/><ellipse cx="422" cy="458" rx="70" ry="42" fill="#02070b" transform="rotate(16 422 458)"/><ellipse cx="602" cy="458" rx="70" ry="42" fill="#02070b" transform="rotate(-16 602 458)"/><path d="M454 598q58 36 116 0" fill="none" stroke="${accent}" stroke-width="12"/>`;
  if (/loosh|harvest|energy/.test(n)) return `<path d="M512 200C690 318 718 472 512 744 306 472 334 318 512 200Z" fill="url(#enamel)" stroke="${gold}" stroke-width="20"/><path d="M372 580c92-54 188-54 280 0M404 488c70-42 146-42 216 0M444 396c44-25 92-25 136 0" fill="none" stroke="${accent}" stroke-width="18"/><circle cx="512" cy="588" r="46" fill="url(#core)"/>`;
  if (/panopticon|digital id|qanon|rabbit hole|chapel perilous|predictive/.test(n)) return `<path d="M220 512q292-270 584 0-292 270-584 0Z" fill="url(#enamel)" stroke="${gold}" stroke-width="20"/><circle cx="512" cy="512" r="130" fill="#04080d" stroke="${accent}" stroke-width="24"/><circle cx="512" cy="512" r="62" fill="url(#core)"/><path d="M512 236v112M512 676v112M236 512h112M676 512h112" stroke="${gold}" stroke-width="16"/>`;
  if (/memory|akashi|life review|near-death|mythic past|aspirational future/.test(n)) return `<path d="M512 230c-126 0-228 102-228 228 0 194 228 334 228 334s228-140 228-334c0-126-102-228-228-228Z" fill="url(#enamel)" stroke="${gold}" stroke-width="20"/><path d="M386 466c78-90 174-90 252 0-78 90-174 90-252 0Zm126-180v324" fill="none" stroke="${accent}" stroke-width="18"/><circle cx="512" cy="466" r="54" fill="url(#core)"/>`;
  if (/matrix|simulation|alignment|singularity|artificial intelligence|borg/.test(n)) return `<g fill="none" stroke="${gold}" stroke-width="18"><path d="M512 232 736 362v260L512 752 288 622V362Z" fill="url(#enamel)"/><path d="m512 232 0 260 224 130M512 492 288 622M288 362l224 130 224-130" stroke="${accent}"/><circle cx="512" cy="492" r="72" fill="url(#core)"/></g>`;
  if (/dune|spice|antarctica|atlantis|prison planet|area 51|fifteen/.test(n)) return `<path d="M214 700 388 350l124 130 124-220 174 440Z" fill="url(#enamel)" stroke="${gold}" stroke-width="20"/><path d="M214 700c146-72 248-72 306 0 78-84 174-84 290 0" fill="none" stroke="${accent}" stroke-width="24"/><circle cx="512" cy="292" r="42" fill="url(#core)"/>`;
  if (/disclosure|secret|breakaway|twenty|montauk|philadelphia/.test(n)) return `<path d="M300 252h424v510H300Z" rx="24" fill="url(#enamel)" stroke="${gold}" stroke-width="20"/><path d="M360 364h304M360 452h210M360 540h304M360 628h174" stroke="${accent}" stroke-width="22"/><path d="M512 182 660 298 512 414 364 298Z" fill="url(#core)" stroke="${gold}" stroke-width="18"/>`;
  if (/federation|prime directive|great awakening|hidden heir|bloodline/.test(n)) return `<circle cx="512" cy="500" r="250" fill="url(#enamel)" stroke="${gold}" stroke-width="22"/><path d="m512 240 66 176 188 8-148 116 50 182-156-104-156 104 50-182-148-116 188-8Z" fill="url(#core)" stroke="${gold}" stroke-width="18"/><circle cx="512" cy="500" r="82" fill="${accent}" opacity=".78"/>`;
  if (/black goo|structured water|dark matter/.test(n)) return `<path d="M260 650c86-218 130-306 252-430 122 124 166 212 252 430-78 104-426 104-504 0Z" fill="url(#enamel)" stroke="${gold}" stroke-width="20"/><path d="M338 588c78 62 270 62 348 0M390 488c68 42 176 42 244 0" fill="none" stroke="${accent}" stroke-width="20"/><circle cx="512" cy="376" r="48" fill="url(#core)"/>`;
  const sides = 5 + (hash(name) % 4);
  const pts = Array.from({length:sides},(_,i)=>{const a=-Math.PI/2+i*Math.PI*2/sides;return `${512+Math.cos(a)*250},${500+Math.sin(a)*250}`}).join(' ');
  return `<polygon points="${pts}" fill="url(#enamel)" stroke="${gold}" stroke-width="22"/><path d="M512 236v528M248 500h528M326 314l372 372M698 314 326 686" stroke="${accent}" stroke-width="18" opacity=".9"/><circle cx="512" cy="500" r="92" fill="url(#core)" stroke="${gold}" stroke-width="18"/>`;
}

export function referenceSvg(name, category = 'reference') {
  const [accent,dark,deep] = palettes[hash(name) % palettes.length];
  const stars = Array.from({length:28},(_,i)=>{const h=hash(`${name}-${i}`);const x=110+(h%804);const y=108+((h>>>9)%806);const r=2+((h>>>17)%5);return `<circle cx="${x}" cy="${y}" r="${r}" fill="#f8d680" opacity="${.28+((h>>>22)%60)/100}"/>`;}).join('');
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="title desc">
  <title id="title">${esc(name)}</title><desc id="desc">Original rounded-square enamel oracle reference for ${esc(name)}.</desc>
  <defs>
    <radialGradient id="ground"><stop stop-color="${dark}"/><stop offset=".58" stop-color="${deep}"/><stop offset="1" stop-color="#020305"/></radialGradient>
    <linearGradient id="gold" x2="0" y2="1"><stop stop-color="#fff1a6"/><stop offset=".22" stop-color="#e5b554"/><stop offset=".55" stop-color="#85511d"/><stop offset=".78" stop-color="#c78b34"/><stop offset="1" stop-color="#f4d17a"/></linearGradient>
    <radialGradient id="enamel"><stop stop-color="${accent}" stop-opacity=".95"/><stop offset=".42" stop-color="${dark}"/><stop offset="1" stop-color="#05070b"/></radialGradient>
    <radialGradient id="core"><stop stop-color="#fff9d6"/><stop offset=".16" stop-color="#ffd765"/><stop offset=".52" stop-color="${accent}"/><stop offset="1" stop-color="#070a0e"/></radialGradient>
    <filter id="shadow"><feGaussianBlur in="SourceAlpha" stdDeviation="18"/><feOffset dy="22"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .75 0"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter>
    <filter id="glow"><feGaussianBlur stdDeviation="14" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
    <clipPath id="clip"><rect x="34" y="34" width="956" height="956" rx="116"/></clipPath>
  </defs>
  <g clip-path="url(#clip)"><rect x="34" y="34" width="956" height="956" rx="116" fill="url(#ground)"/>${stars}<path d="M72 512h880M512 72v880" stroke="#dba843" stroke-width="2" opacity=".18"/><circle cx="512" cy="512" r="356" fill="none" stroke="#dba843" stroke-width="4" opacity=".28"/></g>
  <rect x="42" y="42" width="940" height="940" rx="108" fill="none" stroke="#2d1d0c" stroke-width="44" filter="url(#shadow)"/>
  <rect x="54" y="54" width="916" height="916" rx="98" fill="none" stroke="url(#gold)" stroke-width="24"/>
  <rect x="78" y="78" width="868" height="868" rx="78" fill="none" stroke="#221505" stroke-width="12"/>
  <rect x="92" y="92" width="840" height="840" rx="68" fill="none" stroke="url(#gold)" stroke-width="6"/>
  <g filter="url(#shadow)">${symbolFor(name,accent)}</g>
  <g fill="url(#gold)"><circle cx="512" cy="64" r="24"/><circle cx="960" cy="512" r="24"/><circle cx="512" cy="960" r="24"/><circle cx="64" cy="512" r="24"/></g>
  <circle cx="512" cy="512" r="374" fill="none" stroke="${accent}" stroke-width="6" opacity=".44"/>
  <metadata>{"name":"${esc(name)}","category":"${esc(category)}","system":"Ultimate Science Fiction Oracle","art":"original vector enamel reference","textInImage":false}</metadata>
</svg>`;
}

export async function buildReferenceArt(root, cards) {
  const svgDir = join(root,'reference-tiles','svg');
  const pngDir = join(root,'reference-tiles','png');
  const thumbsDir = join(root,'reference-tiles','thumbnails');
  await Promise.all([svgDir,pngDir,thumbsDir].map(p=>mkdir(p,{recursive:true})));
  const manifest=[];
  for (const name of referenceNames) {
    const card = cards.find(c => c.title === name || c.name === name);
    if (!card) throw new Error(`Missing reference card: ${name}`);
    const id = slug(name);
    const svg = referenceSvg(name, card.primaryClass ?? card.class ?? card.classId ?? 'reference');
    const svgPath = join(svgDir,`${id}.svg`);
    await writeFile(svgPath,svg);
    const formats={svg:`reference-tiles/svg/${id}.svg`};
    if (sharp) {
      const input=Buffer.from(svg);
      for (const size of [2048,1024,512,256]) {
        const folder=size===256?thumbsDir:pngDir;
        const out=join(folder,`${id}-${size}.png`);
        const exists=await access(out).then(()=>true).catch(()=>false);
        if(!exists) await sharp(input,{density:288}).resize(size,size).png({compressionLevel:9,palette:false}).toFile(out);
        formats[`png${size}`]=`${size===256?'reference-tiles/thumbnails':'reference-tiles/png'}/${id}-${size}.png`;
      }
    }
    manifest.push({id:`reference-${id}`,cardId:card.id,name,primaryClass:card.primaryClass ?? card.class ?? card.classId,formats,dimensions:[2048,1024,512,256],shape:'square-rounded-corner',materials:['fired dark enamel','warm gold cloisonné','luminous core'],textEmbedded:false,artStatus:'original-production-reference',generatedBy:'deterministic vector renderer',generatedAt:new Date().toISOString().slice(0,10),prompt:'Original, non-franchise rounded-square enamel oracle tile; one dominant symbolic silhouette; no lettering; high legibility at 96px; midnight field, warm metal, restrained colored enamel.'});
  }
  return manifest;
}
