/**
 * Build-time art generation via OpenAI gpt-image-2 for HULLROT.
 *
 * - Writes PNGs into public/sprites/
 * - CACHED: skips any image whose file already exists on disk (no re-billing).
 * - Wall/door/backdrop panels are FULL-BLEED (used as textures, no keying).
 * - Enemy/gun/pickup sprites are on SOLID MAGENTA (#FF00FF) and chroma-keyed
 *   at load time by src/assets.ts.
 *
 * Run: node scripts/gen-sprites.ts   (Node 22+ strips types natively)
 * Cost: $0.04 per image, 11 images = $0.44 max for a cold run.
 */
import { writeFileSync, existsSync, mkdirSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const OUT_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'public', 'sprites');
const MODEL = 'gpt-image-2';
const COST_PER_IMAGE = 0.04;

const STYLE =
  'gritty retro 90s first-person-shooter game art, dark industrial sci-fi horror aboard a derelict ' +
  'cargo freighter, heavily rusted steel, oxidized grime, desaturated rust-brown and steel-gray palette ' +
  'with hot ember-red accents and sodium-amber work light, painterly but sharp, heavy shadow';

const FLAT =
  'Perfectly flat head-on orthographic view of a wall surface filling the entire frame edge-to-edge, ' +
  'no perspective, no vanishing point, no floor or ceiling visible, even diffuse lighting with no strong ' +
  'directional shadow, video game wall texture.';

const CHROMA =
  'isolated on a completely flat, uniform, solid pure magenta background (#FF00FF). ' +
  'The background must be one single flat magenta color with no gradients, no vignette, ' +
  'no shadows cast on the background, no outlines or glow bleeding into the background.';

interface Spec {
  file: string;
  size: '1024x1024' | '1024x1536';
  prompt: string;
}

const SPECS: Spec[] = [
  {
    file: 'wall-hull.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A heavily rusted ship bulkhead wall panel: riveted steel plates, long streaks of ` +
      `orange oxidation, dents and scratches, patches of flaking dark olive-green paint, a faded ` +
      `worn hazard stripe near one edge. ${FLAT} No text.`,
  },
  {
    file: 'wall-cable.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. An industrial utility wall panel covered in cable runs: bundled black and rust-red ` +
      `cables in corroded steel trays running horizontally, grimy pipes with valves, a small dead ` +
      `gauge, junction boxes, grimy riveted steel backing. ${FLAT} No text.`,
  },
  {
    file: 'wall-grate.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A demon-infested ship wall panel: dark steel grating and ribs overgrown with fibrous ` +
      `red-black flesh pushing through the gaps, wet glistening veins, faint hot ember glow deep ` +
      `between the strands, like the ship itself is infected. ${FLAT} No text.`,
  },
  {
    file: 'door.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A massive sealed blast door filling the entire frame: thick rusted steel slab with a ` +
      `vertical central seam, yellow-black hazard striping along both edges, heavy bolts, a small dark ` +
      `indicator light, scraped and dented surface. ${FLAT}`,
  },
  {
    file: 'enemy-husk.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single shambling dead crewman husk: gaunt gray-green corpse in a torn rust-orange ` +
      `hazard jumpsuit, slack jaw, faint ember-glowing eyes, both arms reaching forward toward the ` +
      `viewer, full body from head to boots, facing directly at the viewer, standing. ` +
      `One creature only, centered. ${CHROMA}`,
  },
  {
    file: 'enemy-stalker.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single lean demonic stalker creature: hairless gray-red hide stretched over wiry ` +
      `muscle, elongated eyeless skull with too many needle teeth, hunched forward on two clawed legs, ` +
      `long-clawed hands raised, full body, facing directly at the viewer. ` +
      `One creature only, centered. ${CHROMA}`,
  },
  {
    file: 'enemy-brute.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single massive demonic brute: slab-muscled hulk twice the width of a man, charred ` +
      `black-red hide cracked with veins of inner fire, tiny burning eyes in a heavy skull, enormous ` +
      `fists hanging low, full body, facing directly at the viewer, standing. ` +
      `One creature only, centered. ${CHROMA}`,
  },
  {
    file: 'gun.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. First-person shooter viewmodel: two gloved hands gripping a chunky retro sci-fi pump ` +
      `shotgun, seen from the shooter's own eyes, weapon centered at the bottom of the frame pointing ` +
      `up and away from the viewer toward the center, worn steel receiver, scratched rubber grip, ` +
      `arms entering the frame from the bottom edge. The muzzle does NOT fire, no muzzle flash. ${CHROMA}`,
  },
  {
    file: 'backdrop.png',
    size: '1024x1536',
    prompt:
      `${STYLE}. Full-bleed vertical atmospheric painting: view down the long central corridor of a ` +
      `derelict cargo freighter, rusted bulkheads and ribs receding into darkness, hanging severed ` +
      `cables, sparse red emergency lights, thin haze, a faint ember glow far at the end. Moody, ` +
      `oppressive, lots of dark negative space. NO characters, NO creatures, NO text. Edge-to-edge.`,
  },
  {
    file: 'pickup-medkit.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single small military medkit pickup item: dented olive-drab steel box with a worn ` +
      `red cross painted on the front face, slightly ajar lid, faint clean white inner glow. Game item, ` +
      `one object only, centered. ${CHROMA}`,
  },
  {
    file: 'pickup-ammo.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single box of shotgun shells pickup item: open rusted olive steel ammo box packed ` +
      `with red plastic shotgun shells with brass heads, one shell leaning against the side. Game item, ` +
      `one object only, centered. ${CHROMA}`,
  },
];

async function generate(spec: Spec): Promise<boolean> {
  const outPath = join(OUT_DIR, spec.file);
  if (existsSync(outPath)) {
    console.log(`  [cache] ${spec.file} already exists, skipping ($0.00)`);
    return false;
  }
  console.log(`  [gen]   ${spec.file} (${spec.size}) ...`);
  const res = await fetch('https://api.openai.com/v1/images/generations', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
    },
    body: JSON.stringify({ model: MODEL, prompt: spec.prompt, size: spec.size, n: 1 }),
  });
  if (!res.ok) {
    const body = await res.text();
    throw new Error(`API ${res.status} for ${spec.file}: ${body.slice(0, 400)}`);
  }
  const json = (await res.json()) as { data: Array<{ b64_json?: string; url?: string }> };
  const item = json.data[0];
  let buf: Buffer;
  if (item?.b64_json) {
    buf = Buffer.from(item.b64_json, 'base64');
  } else if (item?.url) {
    const imgRes = await fetch(item.url);
    buf = Buffer.from(await imgRes.arrayBuffer());
  } else {
    throw new Error(`No image payload for ${spec.file}`);
  }
  writeFileSync(outPath, buf);
  console.log(`  [done]  ${spec.file} (${(buf.length / 1024).toFixed(0)} KB)`);
  return true;
}

async function main() {
  if (!process.env.OPENAI_API_KEY) {
    console.error('OPENAI_API_KEY is not set — cannot generate art.');
    process.exit(2);
  }
  mkdirSync(OUT_DIR, { recursive: true });
  console.log(`Generating ${SPECS.length} images with ${MODEL} -> ${OUT_DIR}`);

  let generated = 0;
  const failures: string[] = [];
  const queue = [...SPECS];
  async function worker() {
    for (let spec = queue.shift(); spec; spec = queue.shift()) {
      try {
        if (await generate(spec)) generated++;
      } catch (err) {
        failures.push(spec.file);
        console.error(`  [FAIL]  ${spec.file}: ${(err as Error).message}`);
      }
    }
  }
  await Promise.all([worker(), worker(), worker()]);

  console.log('---');
  console.log(`Generated ${generated} new image(s), cost this run: $${(generated * COST_PER_IMAGE).toFixed(2)}`);
  if (failures.length) {
    console.error(`FAILED: ${failures.join(', ')}`);
    process.exit(1);
  }
}

main();
