/**
 * Regenerate enemy.<slug>.south sprites via PixelLab API v2 (POST /map-objects).
 * Uses local archetype PNG as init_image for style continuity.
 *
 * Requires: PIXELLAB_API_TOKEN in the environment (https://api.pixellab.ai/mcp).
 *
 * Usage:
 *   node scripts/pixellab-enemy-south-v2.mjs --limit=5
 *   node scripts/pixellab-enemy-south-v2.mjs --slug=boar_l5_5_canyon
 */

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.join(__dirname, '..');
const SQL = path.join(ROOT, 'backend/migrations/000006b_enemy_data.sql');
const MANIFEST = path.join(ROOT, 'frontend/public/assets/game/manifest.json');

const BASE = 'https://api.pixellab.ai/v2';

const ARCHETYPE_REF = {
  wolf: 'enemy.wolf',
  boar: 'enemy.boar',
  zombie: 'enemy.zombie',
  spider: 'enemy.spider',
  orc: 'enemy.orc',
  skeleton: 'enemy.skeleton_archer',
  battle_lizard: 'enemy.battle_lizard',
  element: 'enemy.water_element',
  demon: 'enemy.fire_demon',
  skeleton_king: 'enemy.skeleton_king',
  forest_warden: 'enemy.forest_warden',
  titan: 'enemy.lightning_titan',
  bandit: 'enemy.orc',
  cultist: 'enemy.skeleton_archer',
  golem: 'enemy.ice_guardian',
  wraith: 'enemy.zombie',
  treant: 'enemy.forest_warden',
  basilisk: 'enemy.battle_lizard',
  wyvern: 'enemy.battle_lizard',
  harpy: 'enemy.spider',
  manticore: 'enemy.battle_lizard',
  shade: 'enemy.zombie',
};

const BIOME_HINT = {
  meadow: 'sunlit meadow greens and wildflowers',
  forest: 'deep forest shadows, moss and bark tones',
  ruins: 'grey stone dust, cracked masonry palette',
  canyon: 'red rock, dust, harsh sun',
  swamp: 'murky teal, bog mist, rotting wood',
  volcanic: 'ember glow, black rock, lava highlights',
  astral: 'cosmic violet-blue, star specks, ethereal glow',
};

function parseArgs() {
  const a = process.argv.slice(2);
  let limit = Infinity;
  let slug = null;
  for (const x of a) {
    if (x.startsWith('--limit=')) limit = parseInt(x.slice(8), 10);
    if (x.startsWith('--slug=')) slug = x.slice(7);
  }
  return { limit, slug };
}

function parseEnemyRows(sqlText) {
  const re =
    /VALUES\s*\(\d+,\s*'([^']+)',\s*'([^']+)',\s*'([^']+)',\s*'([^']*)'/g;
  const rows = [];
  let m;
  while ((m = re.exec(sqlText))) {
    rows.push({ type: m[1], archetype: m[2], biome: m[3], name: m[4] });
  }
  return rows;
}

function buildPrompt(row) {
  const b = BIOME_HINT[row.biome] ?? row.biome;
  return (
    `Pixel RPG combat sprite: ${row.name}. ` +
    `${row.archetype.replace(/_/g, ' ')} monster, ${b}. ` +
    `Low top-down view, facing south toward camera, full body, transparent background, ` +
    `game-ready, single black outline, medium shading — match init image proportions and style.`
  );
}

async function pollObject(token, objectId) {
  const url = `${BASE}/objects/${objectId}`;
  for (let i = 0; i < 120; i++) {
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${token}` },
    });
    const j = await res.json().catch(() => ({}));
    const data = j.data ?? j;
    const status = data?.status ?? data?.generation_status;
    if (status === 'completed' || data?.image_url || data?.download_url) {
      return data;
    }
    if (status === 'failed' || data?.error) {
      throw new Error(JSON.stringify(data?.error ?? data));
    }
    await new Promise((r) => setTimeout(r, 3000));
  }
  throw new Error('Timeout waiting for object');
}

async function downloadMcpObject(objectId, destPath) {
  const url = `https://api.pixellab.ai/mcp/map-objects/${objectId}/download`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`download ${res.status}`);
  fs.mkdirSync(path.dirname(destPath), { recursive: true });
  fs.writeFileSync(destPath, Buffer.from(await res.arrayBuffer()));
}

async function main() {
  const token = process.env.PIXELLAB_API_TOKEN;
  if (!token) {
    console.error('Set PIXELLAB_API_TOKEN');
    process.exit(1);
  }

  const { limit, slug } = parseArgs();
  const sql = fs.readFileSync(SQL, 'utf8');
  let rows = parseEnemyRows(sql);
  if (slug) rows = rows.filter((r) => r.type === slug);
  rows = rows.slice(0, limit);

  const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8'));
  const textures = manifest.textures;
  let updated = 0;

  for (const row of rows) {
    const refKey = ARCHETYPE_REF[row.archetype];
    const refFile = textures[refKey].file;
    const refPath = path.join(ROOT, 'frontend/assets', refFile);
    const b64 = fs.readFileSync(refPath).toString('base64');

    const body = {
      description: buildPrompt(row),
      image_size: { width: 96, height: 96 },
      view: 'low top-down',
      outline: 'single color outline',
      shading: 'medium shading',
      detail: 'medium detail',
      init_image: { type: 'base64', base64: b64 },
      init_image_strength: 420,
      text_guidance_scale: 9,
    };

    const cr = await fetch(`${BASE}/map-objects`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
    });
    const cj = await cr.json().catch(() => ({}));
    if (!cr.ok) {
      console.error('Create failed', row.type, cj);
      continue;
    }
    const objectId = cj.data?.object_id ?? cj.object_id ?? cj.data?.id;
    if (!objectId) {
      console.error('No object_id', row.type, cj);
      continue;
    }

    await pollObject(token, objectId);
    const southFile = `enemies/enemy.${row.type}.south.png`;
    const southAbs = path.join(ROOT, 'frontend/assets', southFile);
    await downloadMcpObject(objectId, southAbs);

    const texKey = `enemy.${row.type}.south`;
    textures[texKey] = {
      file: southFile,
      kind: 'map_object',
      rotation: 'south',
      pixellabObjectId: objectId,
    };
    updated++;
    console.log('OK', row.type, objectId);
  }

  if (updated > 0) {
    manifest.version = (manifest.version ?? 0) + 1;
    fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
  }
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});
