import { chromium, type Page, type Locator } from 'playwright';
import { mkdir } from 'node:fs/promises';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { DEFAULT_MASK, shots, type Shot } from './shots.config.js';

const __dirname = dirname(fileURLToPath(import.meta.url));
const OUT_DIR = resolve(__dirname, '../../src/assets/screenshots/whp');

function envOrDie(name: string): string {
  const v = process.env[name];
  if (!v) {
    throw new Error(
      `Missing env var: ${name}. Put it in tools/screenshots/.env (gitignored).`,
    );
  }
  return v;
}

const WHP_BASE = envOrDie('WHP_BASE'); // e.g., https://whp01.cloud-hosting.io:8443
const WHP_USER = envOrDie('WHP_USER');
const WHP_PASS = envOrDie('WHP_PASS');

async function login(page: Page): Promise<void> {
  await page.goto(`${WHP_BASE}/login.php`);
  await page.fill('input[name="user"]', WHP_USER);
  await page.fill('input[name="password"]', WHP_PASS);
  await page.click('button[type="submit"]');
  await page.waitForLoadState('networkidle');
}

/**
 * Neutralise server-identifying and account-specific text/labels before the
 * screenshot — we run a multi-server fleet, so docs shouldn't bake in a single
 * host's name or IP.
 *
 * Runs in the page context. Self-contained: no external bindings, no eval.
 */
async function redactSensitive(page: Page): Promise<void> {
  await page.addStyleTag({
    content: `
      .navbar-text { visibility: hidden !important; }
      .brand-full { visibility: hidden !important; }
    `,
  });

  // String swaps for inline text that mask-by-selector can't cover.
  await page.evaluate(() => {
    const root = document.body;
    const swaps: [RegExp, string][] = [
      // server / mail / nameserver hostnames (with possible -s3 etc. suffix)
      [/whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, '<your-server>.cloud-hosting.io'],
      [/mail\d+\.cloud-hosting\.io/gi, '<your-mail-server>.cloud-hosting.io'],
      [/ns[12]\.whp\d+\.cloud-hosting\.io/gi, 'ns<n>.your-server.cloud-hosting.io'],
      // backup target / bucket names that bake in server number
      [/whp\d+(-[a-z0-9]+)?\b/gi, '<your-server>'],
      [/WHP\d+(-[A-Z0-9]+)?\b/g, '<YOUR-SERVER>'],
      // IP addresses
      [/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, '<your-server-IP>'],
      // home dir + demo user
      [/\/docker\/users\/[a-z0-9-]+/g, '/docker/users/<your-username>'],
      [/demo-user/g, 'your-username'],
    ];
    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
    const nodes: Text[] = [];
    let n: Node | null = walker.nextNode();
    while (n) { nodes.push(n as Text); n = walker.nextNode(); }
    for (const node of nodes) {
      let v = node.nodeValue ?? '';
      for (const [re, replacement] of swaps) v = v.replace(re, replacement);
      if (v !== node.nodeValue) node.nodeValue = v;
    }
  });
}

async function captureShot(page: Page, shot: Shot): Promise<void> {
  const viewport = shot.viewport ?? { width: 1440, height: 900 };
  await page.setViewportSize(viewport);

  await page.goto(`${WHP_BASE}${shot.path}`);
  await page.waitForLoadState('networkidle');
  if (shot.waitFor) await page.waitForSelector(shot.waitFor);
  await redactSensitive(page);

  const maskSelectors = [...DEFAULT_MASK, ...(shot.mask ?? [])];
  const maskLocators: Locator[] = maskSelectors.map((sel) => page.locator(sel));
  const outPath = resolve(OUT_DIR, `${shot.id}.png`);

  if (shot.selector) {
    await page.locator(shot.selector).screenshot({
      path: outPath,
      mask: maskLocators,
    });
  } else {
    // Viewport-only — Playwright never includes browser chrome
    await page.screenshot({
      path: outPath,
      fullPage: false,
      mask: maskLocators,
    });
  }
  console.log(`captured ${shot.id} -> ${outPath}`);
}

async function main(): Promise<void> {
  await mkdir(OUT_DIR, { recursive: true });
  const browser = await chromium.launch({ headless: true });
  const ctx = await browser.newContext({
    ignoreHTTPSErrors: true,
    viewport: { width: 1440, height: 900 },
    deviceScaleFactor: 2,
  });
  const page = await ctx.newPage();

  try {
    await login(page);
    for (const shot of shots) {
      await captureShot(page, shot);
    }
  } finally {
    await browser.close();
  }
}

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