// build-encounters.js — seed TTRPG encounters via REST API.
// Cross-harness: pi, claude, codex all call same script.
// No deps beyond node builtins. Reads spec JSON, writes batch, verifies.
//
// Usage:
//   node build-encounters.js <host> <campaignName> <specFile>
//   cat spec.json | node build-encounters.js <host> <campaignName> -
//
// specFile = JSON array of encounters:
// [
//   {
//     "name": "Barrowdeep 1 - Antechamber",
//     "participants": [
//       { "name": "Ankheg", "type": "monster", "maxHp": 45, "initMod": 0 },
//       { "name": "Boss",   "type": "npc",     "maxHp": 39, "initMod": 2, "initiative": 15 }
//     ]
//   }
// ]
//
// type: 'monster' | 'npc' | 'character'. Default 'monster'.
// initiative: optional manual override. If omitted, rolled d20 + initMod.
// initMod default 0. maxHp default 10.
//
// Output: written encounter ids + rolled initiatives. Exit 0 on success.

'use strict';
const crypto = require('crypto');

const genId = () =>
  (typeof crypto !== 'undefined' && crypto.randomUUID)
    ? crypto.randomUUID()
    : `id_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
const rollD20 = () => Math.floor(Math.random() * 20) + 1;

function makeParticipant(spec) {
  const type = spec.type || 'monster';
  const maxHp = spec.maxHp || 10;
  const initMod = spec.initMod || 0;
  const manual = spec.initiative !== undefined && spec.initiative !== null;
  return {
    id: genId(),
    name: spec.name,
    type,
    originalCharacterId: spec.originalCharacterId || null,
    initiative: manual ? Number(spec.initiative) : rollD20() + initMod,
    maxHp,
    currentHp: maxHp,
    conditions: [],
    isActive: true,
    status: 'conscious',
    deathSaveSuccesses: 0,
    deathSaveFailures: 0,
  };
}

async function getJson(url) {
  const r = await fetch(url);
  if (!r.ok) throw new Error(`GET ${url} -> ${r.status}: ${await r.text()}`);
  return r.json();
}
async function postJson(url, body) {
  const r = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  if (!r.ok) throw new Error(`POST ${url} -> ${r.status}: ${await r.text()}`);
  return r.json();
}

async function findCampaignId(host, name) {
  const list = await getJson(`${host}/api/collection?path=campaigns`);
  const matches = list.filter(c => c.name === name);
  if (matches.length === 0) {
    console.error(`No campaign named "${name}". Existing:`);
    list.forEach(c => console.error(`  ${c.name}`));
    process.exit(2);
  }
  if (matches.length > 1) {
    console.error(`Multiple campaigns named "${name}". Use unique name or pass id.`);
    matches.forEach(c => console.error(`  ${c.id}  ${c.name}`));
    process.exit(2);
  }
  return matches[0].id;
}

async function main() {
  const [host, campaignName, specArg] = process.argv.slice(2);
  if (!host || !campaignName || !specArg) {
    console.error('Usage: node build-encounters.js <host> <campaignName> <specFile|->');
    process.exit(1);
  }
  const specRaw = specArg === '-'
    ? require('fs').readFileSync(0, 'utf8')
    : require('fs').readFileSync(specArg, 'utf8');
  const encounters = JSON.parse(specRaw);
  if (!Array.isArray(encounters)) throw new Error('spec must be a JSON array of encounters');

  const cid = await findCampaignId(host, campaignName);
  console.log(`campaign: "${campaignName}" -> ${cid}`);

  const encPath = `campaigns/${cid}/encounters`;
  const now = new Date().toISOString();
  const ops = [];
  const summary = [];
  for (const enc of encounters) {
    const id = genId();
    const path = `${encPath}/${id}`;
    const parts = (enc.participants || []).map(makeParticipant);
    ops.push({
      type: 'set',
      path,
      data: {
        name: enc.name,
        createdAt: now,
        participants: parts,
        round: 0,
        currentTurnParticipantId: null,
        isStarted: false,
        isPaused: false,
        ruleset: enc.ruleset || '5e',
      },
    });
    summary.push({ name: enc.name, id, parts });
  }

  const res = await postJson(`${host}/api/batch`, { ops });
  if (!res.ok) throw new Error(`batch failed: ${JSON.stringify(res)}`);
  console.log(`batch: ${ops.length} encounter(s) written\n`);

  // Verify
  const written = await getJson(`${host}/api/collection?path=${encPath}`);
  const byId = new Map(written.map(e => [e.id, e]));

  for (const s of summary) {
    const got = byId.get(s.id);
    const ok = got && got.name === s.name && (got.participants || []).length === s.parts.length;
    console.log(`${ok ? '✓' : '✗'} ${s.name}`);
    console.log(`  id: ${s.id}`);
    if (!ok) { console.log(`  VERIFY FAILED: got ${JSON.stringify(got && { name: got.name, n: (got.participants || []).length })}`); continue; }
    s.parts.forEach(p => {
      const i = p.initiative >= 0 ? `+${p.initiative}` : `${p.initiative}`;
      console.log(`    - ${p.name} (${p.type}, hp ${p.maxHp}, init ${i})`);
    });
  }
  console.log('\nDone.');
}

main().catch(e => { console.error('FAIL:', e.message); process.exit(1); });
