/*
 * Re-shoot the documentation screenshots.
 *
 *   pnpm run dev:api                      # :8920, against a seeded database
 *   pnpm run dev:web                      # :5173, proxies /api to the above
 *   node scripts/screenshots.mjs          # writes docs/screenshots/*.webp
 *
 * Every page, at 1440×900 and at 393×852, in light and in dark — forty images.
 * The README section that these feed went stale once already, when the shell
 * was rebuilt and nobody could cheaply re-shoot; that is why this is a script
 * in the tree rather than a note about what to capture.
 *
 * Three things are forced rather than clicked:
 *
 *  - The theme. Seeding localStorage is not enough on its own: the appearance
 *    preference is stored server-side and adopted after hydration (see
 *    apps/web/src/lib/theme.tsx), so every dark shot would snap back to light
 *    a beat after first paint. The response is rewritten instead.
 *  - The sidebar, expanded, and the Piggy dock, closed — both are per-device
 *    states in localStorage, so whatever the last human left behind would
 *    otherwise leak into the captures.
 *  - The capacity matcher's results, by submitting the form. An empty form is
 *    not a picture of a matcher.
 *
 * Captures are the viewport rather than the full scroll height: a README image
 * three thousand pixels tall is unreadable at the width a README renders in.
 *
 * The PNGs Playwright produces are downscaled from their 2×/3× capture and
 * re-encoded as WebP by scripts/screenshots-encode.py, which runs last. Forty
 * PNGs at capture scale are 16MB; the WebP set is under 2MB, which is the
 * difference between a repository people clone and one they do not.
 */
import { chromium } from 'playwright';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const BASE = process.env.PIG_WEB_URL ?? 'http://127.0.0.1:5173';
const OUT = process.env.PIG_SHOTS_OUT ?? 'docs/screenshots';
const raw = mkdtempSync(join(tmpdir(), 'pig-shots-'));

const PAGES = [
  { slug: 'overview', path: '/' },
  { slug: 'margin', path: '/margin' },
  { slug: 'capacity', path: '/capacity' },
  {
    slug: 'capacity-match',
    path: '/capacity',
    tab: 'Match a requirement',
    submit: 'Find capacity',
  },
  { slug: 'growth', path: '/growth' },
  { slug: 'calendar', path: '/calendar' },
  { slug: 'demand', path: '/demand' },
  { slug: 'supply', path: '/supply' },
  { slug: 'accounts', path: '/accounts' },
  { slug: 'contracts', path: '/contracts' },
];

const VIEWPORTS = [
  { name: 'desktop', width: 1440, height: 900, scale: 2 },
  { name: 'mobile', width: 393, height: 852, scale: 3 },
];

// channel:'chrome' reuses the system browser rather than downloading one.
const browser = await chromium.launch({ channel: 'chrome' });
let failures = 0;

for (const theme of ['light', 'dark']) {
  for (const vp of VIEWPORTS) {
    const ctx = await browser.newContext({
      viewport: { width: vp.width, height: vp.height },
      deviceScaleFactor: vp.scale,
      colorScheme: theme,
      isMobile: vp.name === 'mobile',
      hasTouch: vp.name === 'mobile',
    });

    await ctx.addInitScript(`
      try {
        localStorage.setItem('pig.themeMode', ${JSON.stringify(theme)});
        localStorage.setItem('pig.accent', 'pig');
        localStorage.setItem('pig.sidebarOpen', 'true');
        localStorage.setItem('pig.piggyDockOpen', 'false');
      } catch {}
    `);

    await ctx.route('**/api/me/profile', async (route) => {
      const response = await route.fetch();
      let body = {};
      try {
        body = await response.json();
      } catch {
        /* An error body is fine to discard; the theme fields are what matter. */
      }
      await route.fulfill({ response, json: { ...body, themeMode: theme, accentColor: 'pig' } });
    });

    const page = await ctx.newPage();
    const problems = [];
    page.on('pageerror', (e) => problems.push(`pageerror: ${e.message}`));
    page.on('console', (m) => {
      if (m.type() === 'error') problems.push(`console: ${m.text().slice(0, 140)}`);
    });

    for (const p of PAGES) {
      await page.goto(BASE + p.path, { waitUntil: 'networkidle' });

      if (p.tab) {
        await page
          .getByRole('tab', { name: p.tab })
          .or(page.getByText(p.tab, { exact: true }))
          .first()
          .click();
        await page.waitForTimeout(600);
      }
      if (p.submit) {
        await page.getByRole('button', { name: p.submit }).first().click();
        await page.waitForLoadState('networkidle').catch(() => {});
        await page.waitForTimeout(900);
      }
      await page.waitForTimeout(1200);

      await page.screenshot({ path: `${raw}/${p.slug}-${vp.name}-${theme}.png` });

      /*
       * Two checks a status code cannot make: a horizontal scrollbar (the
       * 393px failure mode), and the theme actually landing. Both would
       * otherwise be discovered by a human squinting at forty images.
       */
      const [overflow, applied] = await page.evaluate(() => [
        document.documentElement.scrollWidth - document.documentElement.clientWidth,
        document.documentElement.dataset.theme,
      ]);
      const ok = applied === theme && overflow === 0;
      if (!ok) failures += 1;
      console.log(
        `${ok ? '  ' : '!!'} ${p.slug.padEnd(15)} ${vp.name.padEnd(7)} ` +
          `want=${theme.padEnd(5)} got=${applied} overflow=${overflow}`,
      );
    }

    if (problems.length) {
      failures += 1;
      console.log('   !! ' + [...new Set(problems)].slice(0, 4).join('\n   !! '));
    }
    await ctx.close();
  }
}

await browser.close();

const encode = spawnSync('python3', ['scripts/screenshots-encode.py', raw, OUT], {
  stdio: 'inherit',
});
rmSync(raw, { recursive: true, force: true });

if (encode.status !== 0) {
  console.error('encoding failed — is Pillow installed? (python3 -m pip install Pillow)');
  process.exit(1);
}
if (failures) {
  console.error(`\n${failures} capture(s) had a theme, overflow or console problem — see above.`);
  process.exit(1);
}
console.log(`\nWrote ${PAGES.length * 4} images to ${OUT}.`);
