#!/usr/bin/env node
/*
 * Import the public 40k RPG Tools master bestiary index into a staging file.
 *
 * The source page is an index of names, type, affiliation, setting, book, and
 * page numbers. It is not a statblock source, so this script intentionally does
 * not add rows to the live bestiary JSON. Use the staged output to decide which
 * local PDFs/pages to hydrate into compact verified entries.
 */
const fs = require('fs');
const path = require('path');
const https = require('https');
if (typeof global.File === 'undefined') {
  global.File = class File {};
}
const cheerio = require('cheerio');

const ROOT = path.join(__dirname, '..');
const SOURCE_URL = 'https://www.40krpgtools.com/bestiary/';
const OUT_PATH = path.join(ROOT, 'database', 'remote-extract', '40krpgtools-bestiary-index.json');
const ACTIVE_BESTIARY = path.join(ROOT, 'public', 'deathwatch-bestiary-extracted.json');

function argValue(name, fallback = null) {
  const prefix = `--${name}=`;
  const found = process.argv.find(arg => arg.startsWith(prefix));
  return found ? found.slice(prefix.length) : fallback;
}

function hasFlag(name) {
  return process.argv.includes(`--${name}`);
}

function norm(value) {
  return String(value || '').trim().toLowerCase().replace(/\s+/g, ' ');
}

function fetchText(url) {
  return new Promise((resolve, reject) => {
    https.get(url, response => {
      if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
        fetchText(new URL(response.headers.location, url).toString()).then(resolve, reject);
        return;
      }
      if (response.statusCode !== 200) {
        reject(new Error(`GET ${url} failed with HTTP ${response.statusCode}`));
        return;
      }
      let body = '';
      response.setEncoding('utf8');
      response.on('data', chunk => { body += chunk; });
      response.on('end', () => resolve(body));
    }).on('error', reject);
  });
}

function loadActiveNames() {
  try {
    const raw = JSON.parse(fs.readFileSync(ACTIVE_BESTIARY, 'utf8'));
    const arr = Array.isArray(raw) ? raw : (raw.results || raw.entries || raw.items || []);
    return new Set(arr.map(entry => norm(entry.bestiaryName || entry.name)).filter(Boolean));
  } catch {
    return new Set();
  }
}

function parseRows(html, activeNames) {
  const $ = cheerio.load(html);
  const rows = [];
  $('#bestiaryTable tr').each((_, tr) => {
    const cells = $(tr).find('td');
    if (cells.length < 6) return;
    const link = $(cells[0]).find('a').first();
    const href = link.attr('href') || '';
    const entry = {
      name: link.text().trim() || $(cells[0]).text().trim(),
      type: $(cells[1]).text().trim(),
      affiliation: $(cells[2]).text().trim(),
      setting: $(cells[3]).text().trim(),
      book: $(cells[4]).text().trim(),
      page: $(cells[5]).text().trim(),
      sourceUrl: href ? new URL(href, SOURCE_URL).toString() : SOURCE_URL,
    };
    entry.key = norm(entry.name);
    entry.inActiveBestiary = activeNames.has(entry.key);
    rows.push(entry);
  });
  return rows;
}

async function main() {
  const setting = argValue('setting', 'Deathwatch');
  const includeExisting = hasFlag('include-existing');
  const allSettings = hasFlag('all-settings');
  const activeNames = loadActiveNames();
  const html = await fetchText(SOURCE_URL);
  let rows = parseRows(html, activeNames);

  if (!allSettings) rows = rows.filter(row => norm(row.setting) === norm(setting));
  if (!includeExisting) rows = rows.filter(row => !row.inActiveBestiary);

  rows.sort((a, b) => (
    a.book.localeCompare(b.book) ||
    Number(a.page || 0) - Number(b.page || 0) ||
    a.name.localeCompare(b.name)
  ));

  const byBook = rows.reduce((acc, row) => {
    acc[row.book] = (acc[row.book] || 0) + 1;
    return acc;
  }, {});

  const output = {
    source: SOURCE_URL,
    importedAt: new Date().toISOString(),
    mode: {
      setting: allSettings ? 'all' : setting,
      includeExisting,
    },
    count: rows.length,
    byBook,
    entries: rows,
  };

  fs.mkdirSync(path.dirname(OUT_PATH), { recursive: true });
  fs.writeFileSync(OUT_PATH, JSON.stringify(output, null, 2));

  console.log(`Wrote ${rows.length} staged index rows -> ${path.relative(ROOT, OUT_PATH)}`);
  Object.entries(byBook)
    .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
    .forEach(([book, count]) => console.log(`  ${book}: ${count}`));
}

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