#!/usr/bin/env node
// Reuse-only actor translator: fills in an Actors pack's "items" (Skills/Talents/Traits/
// Trappings) from whatever is already translated elsewhere in compendium/, and leaves
// everything else (NSC name, description/gmnotes, effects) exactly as in the English
// source. Items with no existing translation anywhere in the corpus are left in English
// too - this script never invents new translations, it only splices in prior work.
//
// Usage:
//   node tools/translate_actor_items_from_corpus.js <file.actors.json> "<German label>"
//   node tools/translate_actor_items_from_corpus.js --list-untranslated <file1.actors.json> [file2 ...]
//
// Examples:
//   node tools/translate_actor_items_from_corpus.js wfrp4e-pbtt.actors.json "Akteure (Power Behind the Throne)"
//   node tools/translate_actor_items_from_corpus.js --list-untranslated wfrp4e-pbtt.actors.json wfrp4e-dotr.actors.json
//
// Run from anywhere; paths are resolved relative to the repo root (parent of tools/).

const fs = require('fs');
const path = require('path');

const REPO = path.join(__dirname, '..');
const COMPENDIUM = path.join(REPO, 'compendium');
const TODO = path.join(COMPENDIUM, 'To Do');

// Actor packs that were translated in "items-only reuse" mode: they still contain
// English-language leftovers under their original keys/names for items with no
// existing translation anywhere else. They must NEVER be trusted as a lookup source
// (their own untranslated leftovers would otherwise be mistaken for "already translated").
const PARTIAL_PACKS = new Set([
  'wfrp4e-pbtt.actors.json',
  'wfrp4e-dotr.actors.json',
  'wfrp4e-horned-rat.actors.json',
  'wfrp4e-empire-ruins.actors.json'
]);

function buildLookups(extraExclude = []) {
  const exclude = new Set([...PARTIAL_PACKS, ...extraExclude]);
  const itemsLookup = {};   // key -> translated item, from already-translated *.items.json packs
  const actorLookup = {};   // key -> translated item, from nested items inside already-translated *.actors.json/*.bestiary.json
  const byName = {};        // English name -> translated item (fallback for actor-embedded items with random Foundry IDs as keys)

  for (const f of fs.readdirSync(COMPENDIUM)) {
    if (exclude.has(f)) continue;
    const full = path.join(COMPENDIUM, f);
    if (!fs.statSync(full).isFile() || !f.endsWith('.json')) continue;
    let data;
    try { data = JSON.parse(fs.readFileSync(full, 'utf8')); } catch { continue; }
    if (!data.entries) continue;

    if (f.endsWith('.items.json')) {
      for (const [k, v] of Object.entries(data.entries)) {
        if (!itemsLookup[k]) itemsLookup[k] = v;
      }
    } else if (f.endsWith('.actors.json') || f.endsWith('.bestiary.json')) {
      for (const actor of Object.values(data.entries)) {
        for (const [k, v] of Object.entries(actor.items || {})) {
          if (!actorLookup[k]) actorLookup[k] = v;
        }
      }
    }
  }

  for (const [k, v] of Object.entries(itemsLookup)) if (!byName[k]) byName[k] = v;
  for (const [k, v] of Object.entries(actorLookup)) if (!byName[k]) byName[k] = v;

  return { itemsLookup, actorLookup, byName };
}

function translateItems(items, lookups) {
  const { itemsLookup, actorLookup, byName } = lookups;
  const out = {};
  let translated = 0;
  for (const [k, v] of Object.entries(items)) {
    if (itemsLookup[k]) { out[k] = JSON.parse(JSON.stringify(itemsLookup[k])); translated++; }
    else if (actorLookup[k]) { out[k] = JSON.parse(JSON.stringify(actorLookup[k])); translated++; }
    else if (byName[v.name]) { out[k] = JSON.parse(JSON.stringify(byName[v.name])); translated++; }
    else { out[k] = v; } // leave untranslated (English original) - never invent new translations
  }
  return { out, translated };
}

function runTranslate(srcFile, labelDE) {
  const lookups = buildLookups();
  const src = JSON.parse(fs.readFileSync(path.join(TODO, srcFile), 'utf8'));

  const out = { label: labelDE, entries: {} };
  if (src.folders) out.folders = src.folders;

  let totalItems = 0, translatedItems = 0;
  for (const [key, actor] of Object.entries(src.entries)) {
    const entry = { name: actor.name };
    if (actor.items) {
      const { out: items, translated } = translateItems(actor.items, lookups);
      entry.items = items;
      totalItems += Object.keys(actor.items).length;
      translatedItems += translated;
    }
    if (actor.description !== undefined) entry.description = actor.description;
    if (actor.gmnotes !== undefined) entry.gmnotes = actor.gmnotes;
    if (actor.effects !== undefined) entry.effects = actor.effects;
    out.entries[key] = entry;
  }

  // structural safety check: item key sets must match exactly
  for (const k of Object.keys(src.entries)) {
    const srcKeys = Object.keys(src.entries[k].items || {}).sort();
    const outKeys = Object.keys(out.entries[k].items || {}).sort();
    if (JSON.stringify(srcKeys) !== JSON.stringify(outKeys)) {
      throw new Error('item key mismatch in ' + k);
    }
  }

  const destPath = path.join(COMPENDIUM, srcFile);
  fs.writeFileSync(destPath, JSON.stringify(out, null, 1) + '\n');
  console.log(`OK ${srcFile} | items total: ${totalItems} | reused: ${translatedItems} | left English: ${totalItems - translatedItems}`);
  console.log(`-> ${destPath}`);
  console.log('Note: source file was intentionally NOT removed from "To Do/" since names/descriptions/some items remain English by design.');
}

function runListUntranslated(files) {
  const lookups = buildLookups();
  const { itemsLookup, actorLookup, byName } = lookups;
  const missing = {}; // itemName -> { files: Set, actorCount: Set }

  for (const f of files) {
    const src = JSON.parse(fs.readFileSync(path.join(TODO, f), 'utf8'));
    for (const [an, actor] of Object.entries(src.entries)) {
      for (const [ik, item] of Object.entries(actor.items || {})) {
        if (itemsLookup[ik] || actorLookup[ik] || byName[item.name]) continue;
        const label = f.replace(/^wfrp4e-/, '').replace(/\.actors\.json$/, '');
        if (!missing[item.name]) missing[item.name] = { files: new Set(), actors: new Set() };
        missing[item.name].files.add(label);
        missing[item.name].actors.add(an);
      }
    }
  }

  const rows = Object.entries(missing)
    .map(([name, v]) => ({ name, files: [...v.files].sort(), actorCount: v.actors.size }))
    .sort((a, b) => a.name.localeCompare(b.name));

  const outPath = path.join(REPO, 'untranslated_ew_items.txt');
  fs.writeFileSync(outPath, rows.map(r => `${r.name}\t[${r.files.join(', ')}]\t(${r.actorCount} NSC)`).join('\n') + '\n');
  console.log('Unique untranslated item names:', rows.length);
  console.log('->', outPath);
}

// --- CLI entry point ---
const argv = process.argv.slice(2);
if (argv[0] === '--list-untranslated') {
  runListUntranslated(argv.slice(1));
} else if (argv.length === 2) {
  runTranslate(argv[0], argv[1]);
} else {
  console.error('Usage:');
  console.error('  node tools/translate_actor_items_from_corpus.js <file.actors.json> "<German label>"');
  console.error('  node tools/translate_actor_items_from_corpus.js --list-untranslated <file1.actors.json> [file2 ...]');
  process.exit(1);
}
