/*
 * Does every seeded template's `fields` actually reach the screen?
 *
 *   node scripts/motion-fields-check.mjs        # PIG_WEB_URL=http://127.0.0.1:8975
 *
 * Three of the twelve starter templates shipped with `fields` shapes no renderer
 * in `FieldsView` read — `decisions`, `blockingSet`, `checks`, `steps` and the
 * rest — so about forty authored records rendered as no DOM at all, in the
 * library and again on the engagement that instantiated them. Nothing failed:
 * each renderer returned null for a key set it did not recognise, and a template
 * page that is header-plus-body looks like a template that was written that way.
 *
 * So the check is not "does the page load". It samples the actual authored
 * strings out of the seed JSON and asserts they are present in the rendered
 * text, which is the only claim that distinguishes a rendered field from a
 * dropped one.
 */
import { chromium } from 'playwright';
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';

const BASE = process.env.PIG_WEB_URL ?? 'http://127.0.0.1:8975';
const SEEDS = 'packages/db/src/seed/motion';

/** Every string of real prose in a fields tree, longest first. */
function strings(node, out = []) {
  if (typeof node === 'string') out.push(node);
  else if (Array.isArray(node)) node.forEach((n) => strings(n, out));
  else if (node && typeof node === 'object') Object.values(node).forEach((n) => strings(n, out));
  return out;
}

const templates = readdirSync(SEEDS)
  .filter((f) => f.endsWith('.json'))
  .map((f) => JSON.parse(readFileSync(join(SEEDS, f), 'utf8')))
  .filter((t) => t.fields && Object.keys(t.fields).length > 0);

const live = await fetch(`${BASE}/api/motion/templates?all=1`).then((r) => r.json());
const bySlug = new Map(live.templates.map((t) => [t.slug, t]));

const browser = await chromium.launch({ channel: 'chrome' });
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } });
let failures = 0;

for (const template of templates) {
  const row = bySlug.get(template.slug);
  if (!row) {
    console.log(`SKIP ${template.slug}: not in the seeded library`);
    continue;
  }

  // Long strings only: a short one ("Owner", "Yes") can appear in the chrome by
  // coincidence and would make this pass on a page that dropped every field.
  const sample = strings(template.fields)
    .filter((s) => s.length > 45)
    .sort((a, b) => b.length - a.length)
    .slice(0, 6);
  if (sample.length === 0) {
    console.log(`SKIP ${template.slug}: no prose long enough to sample`);
    continue;
  }

  const page = await ctx.newPage();
  await page.goto(`${BASE}/motion/library/${row.id}`, { waitUntil: 'networkidle' });
  await page.waitForTimeout(900);
  const text = await page.evaluate(() => document.body.innerText);
  // The markdown body is rendered too, so a string that also appears there
  // would not prove the FIELDS reached the screen. Those are excluded.
  const fieldsOnly = sample.filter((s) => !template.body.includes(s));
  const checking = fieldsOnly.length ? fieldsOnly : sample;
  const missing = checking.filter((s) => !text.includes(s));

  if (missing.length) {
    failures++;
    console.log(`FAIL ${template.slug} (${template.kind}): ${missing.length}/${checking.length} authored strings absent`);
    console.log(`     e.g. "${missing[0].slice(0, 90)}…"`);
  } else {
    console.log(`ok   ${template.slug} (${template.kind}): ${checking.length} authored strings on screen`);
  }
  await page.close();
}

await browser.close();
console.log(failures === 0 ? '\nevery seeded fields tree reaches the screen' : `\n${failures} template(s) render authored content nowhere`);
process.exit(failures === 0 ? 0 : 1);
