/**
 * Build-time sprite generation via OpenAI gpt-image-2 for HEARTHROOT HOLLOW.
 *
 * - Writes PNGs into public/sprites/
 * - CACHED: skips any sprite whose file already exists on disk (no re-billing).
 * - CHROMA KEY: gameplay sprites are generated on SOLID MAGENTA (#FF00FF) and
 *   keyed out at runtime by src/assets.ts. The title background plate is
 *   full-bleed and NOT keyed.
 *
 * Run: node scripts/gen-sprites.ts   (Node 22+ strips types natively)
 * Cost: $0.04 per image, 10 images = $0.40 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 =
  'painterly pixel-art for a cozy 2D farming game, chunky dark outlines, saturated cozy palette of ' +
  'warm earth browns, deep lush greens and soft amber, gentle gouache shading, warm storybook charm';

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.';

// keep the farmer description IDENTICAL across the three views so gpt-image-2
// drifts as little as possible between separate generations
const FARMER =
  'a small cheerful farmer kid with a round wide-brim straw hat, short messy auburn hair, ' +
  'a sun-yellow shirt, brown overalls with one brass button, and little leather boots, ' +
  'cute big-head small-body chibi proportions';

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

const cropSheet = (name: string, sprout: string, mid: string, mature: string): string =>
  `${STYLE}. A horizontal sprite sheet of exactly three growth stages of a ${name} crop plant, ` +
  `side by side in one row with equal spacing, each stage centered in its own third of the image: ` +
  `LEFT third: ${sprout}. MIDDLE third: ${mid}. RIGHT third: ${mature}. ` +
  `All three rooted at the same baseline height. No soil mound wider than the plant, no labels, no text. ${CHROMA}`;

const SPRITES: SpriteSpec[] = [
  {
    file: 'farmer-down.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single video game character: ${FARMER}. Full body, FRONT view facing the viewer, ` +
      `standing relaxed with arms at sides, friendly smile, feet slightly apart. ` +
      `One character only, centered. ${CHROMA}`,
  },
  {
    file: 'farmer-up.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single video game character: ${FARMER}. Full body, BACK view seen from behind ` +
      `(we see the back of the straw hat, back of overalls, heels of boots), standing relaxed with ` +
      `arms at sides. One character only, centered. ${CHROMA}`,
  },
  {
    file: 'farmer-side.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single video game character: ${FARMER}. Full body, SIDE profile view facing right, ` +
      `standing relaxed, both arms visible at side, profile of nose and hat brim pointing right. ` +
      `One character only, centered. ${CHROMA}`,
  },
  {
    file: 'crop-turnip.png',
    size: '1536x1024',
    prompt: cropSheet(
      'plump rosy turnip',
      'a tiny pale-green two-leaf sprout poking from a small soil mound',
      'a leafy young turnip plant with a hint of round pink-white root bulging from the soil',
      'a proud plump rosy-pink turnip half out of the ground with big lush green leaves on top',
    ),
  },
  {
    file: 'crop-tomato.png',
    size: '1536x1024',
    prompt: cropSheet(
      'cherry tomato',
      'a tiny green seedling with two small leaves on a thin stem',
      'a leafy green tomato bush on a small wooden stake with tiny green unripe tomatoes',
      'a lush staked tomato bush heavy with bright glossy red ripe tomatoes',
    ),
  },
  {
    file: 'crop-pumpkin.png',
    size: '1536x1024',
    prompt: cropSheet(
      'pumpkin',
      'a small green sprout with curly tendril vines on the soil',
      'a spreading pumpkin vine with big leaves and one small green baby pumpkin',
      'a huge round ribbed bright-orange pumpkin with a curly stem and big leaves behind it',
    ),
  },
  {
    file: 'crop-sunflower.png',
    size: '1536x1024',
    prompt: cropSheet(
      'sunflower',
      'a small green sprout with two rounded leaves',
      'a knee-high young sunflower stalk with broad leaves and a small closed green bud',
      'a tall glorious blooming sunflower with a big warm golden flower head and brown center, facing the viewer',
    ),
  },
  {
    file: 'tools.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A 2x2 grid of four separate farming tool icons for a game hotbar, each icon fully ` +
      `contained within its own quadrant of the image with generous empty margin between them: ` +
      `TOP-LEFT: a sturdy hoe with wooden handle and iron blade, held diagonally. ` +
      `TOP-RIGHT: a round copper watering can with a wide sprinkler spout. ` +
      `BOTTOM-LEFT: a small burlap seed pouch tied with twine, a few seeds spilling out. ` +
      `BOTTOM-RIGHT: a curved harvest scythe with wooden handle, blade pointing left. ` +
      `Four objects only, no labels, no text. ${CHROMA}`,
  },
  {
    file: 'tree.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single cozy fluffy deciduous tree for a top-down farming game: a round billowy ` +
      `deep-green canopy with painterly leaf clusters and warm light catching the top, short sturdy ` +
      `brown trunk visible at the bottom, a few tiny red fruits tucked in the leaves. ` +
      `One tree only, centered. ${CHROMA}`,
  },
  // ---- polish pass (round 2) ----
  {
    file: 'farmhouse.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single small cozy wooden farmhouse for a top-down farming game, seen from the ` +
      `front: warm red shingled roof, cream plaster walls with brown timber framing, a round-top ` +
      `wooden door, two little square windows with amber light, a stone chimney on the roof, a few ` +
      `flower pots by the door. One building only, centered. ${CHROMA}`,
  },
  {
    file: 'chicken.png',
    size: '1536x1024',
    prompt:
      `${STYLE}. A horizontal sprite sheet of exactly two poses of the SAME plump cute white hen ` +
      `with a red comb and little orange beak, side view facing right, each pose centered in its own ` +
      `half of the image: LEFT half: standing upright, alert. RIGHT half: pecking the ground, head ` +
      `lowered, tail up. Same hen, same size, same baseline. No labels, no text. ${CHROMA}`,
  },
  {
    file: 'duck.png',
    size: '1536x1024',
    prompt:
      `${STYLE}. A horizontal sprite sheet of exactly two poses of the SAME cute mallard duck ` +
      `swimming, side view facing right, only the body above the waterline (flat bottom edge, no water ` +
      `drawn), each pose centered in its own half: LEFT half: gliding calmly, head up. RIGHT half: ` +
      `dabbling, head dipped forward and down. Same duck, same size, same baseline. No labels, no text. ${CHROMA}`,
  },
  {
    file: 'scarecrow.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A single friendly scarecrow for a cozy farming game: a wooden cross-pole frame, a ` +
      `plump burlap sack head with stitched smile and button eyes, a floppy pointed straw hat, a ` +
      `patched blue shirt stuffed with straw poking out of the sleeves, a tiny bird perched on one ` +
      `arm. One scarecrow only, centered. ${CHROMA}`,
  },
  {
    file: 'props.png',
    size: '1024x1024',
    prompt:
      `${STYLE}. A 2x2 grid of four separate nature props for a top-down farming game, each fully ` +
      `contained within its own quadrant with generous empty margin between them: ` +
      `TOP-LEFT: a cluster of three rounded grey mossy boulders. ` +
      `TOP-RIGHT: a cluster of cute red-capped white-spotted toadstool mushrooms in grass. ` +
      `BOTTOM-LEFT: a fallen mossy log with a small orange mushroom growing on it. ` +
      `BOTTOM-RIGHT: a rustic wooden lantern post with a warm glowing amber lantern hanging from it. ` +
      `Four objects only, no labels, no text. ${CHROMA}`,
  },
  {
    file: 'crop-strawberry.png',
    size: '1536x1024',
    prompt: cropSheet(
      'strawberry',
      'a tiny green sprout with three small serrated leaves',
      'a low leafy strawberry plant with white blossoms and tiny green berries',
      'a lush strawberry plant heavy with big glossy red ripe strawberries and white blossoms',
    ),
  },
  {
    file: 'crop-corn.png',
    size: '1536x1024',
    prompt: cropSheet(
      'sweet corn',
      'a small green corn sprout with two thin blade leaves',
      'a waist-high young corn stalk with broad arching green leaves',
      'a tall proud corn stalk with golden tassels and two plump ears of corn with peeled-back husks',
    ),
  },
  {
    file: 'background.png',
    size: '1536x1024',
    prompt:
      `${STYLE}. Full-bleed title screen background painting for a cozy farming game: a warm sunlit ` +
      `farm valley at golden hour, gentle rolling green hills, a small red-roofed wooden farmhouse with ` +
      `a smoking chimney, neat little crop fields in tidy rows, a round glittering pond, a winding dirt ` +
      `path, wooden fences, fluffy clouds in a peach-and-gold sky, distant soft blue hills. ` +
      `Lush, warm, inviting, like a storybook summer evening. NO characters, NO animals, NO text. ` +
      `Edge-to-edge painting.`,
  },
];

async function generate(spec: SpriteSpec): 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 sprites.');
    process.exit(2);
  }
  mkdirSync(OUT_DIR, { recursive: true });
  console.log(`Generating ${SPRITES.length} sprites with ${MODEL} -> ${OUT_DIR}`);

  let generated = 0;
  const failures: string[] = [];
  // modest concurrency: 2 at a time
  const queue = [...SPRITES];
  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()]);

  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(', ')} — game will fall back to procgen for these.`);
    process.exit(1);
  }
}

main();
