/**
 * SET 42e — permanent identity scrub guard.
 *
 * The developer's personal name and LinkedIn must not appear on any
 * user-facing surface shipped via web/dist or the extension zip.
 * Fails the web build on match.
 */
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const DIST = path.join(ROOT, 'web', 'dist');
const EXT_ZIP = path.join(DIST, 'api', 'extension.zip');

/** Patterns that must never ship in user-facing build artifacts. */
export const FORBIDDEN = [
  /Andy\s+Funke/i,
  /linkedin\.com\/in\//i,
];

export interface IdentityCheckResult {
  ok: boolean;
  violations: string[];
}

function walkFiles(dir: string): string[] {
  const out: string[] = [];
  if (!fs.existsSync(dir)) return out;
  for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
    const p = path.join(dir, ent.name);
    if (ent.isDirectory()) out.push(...walkFiles(p));
    else if (ent.isFile()) out.push(p);
  }
  return out;
}

function scanText(label: string, text: string, violations: string[]): void {
  for (const re of FORBIDDEN) {
    if (re.test(text)) {
      violations.push(`${label}: matched ${re}`);
    }
  }
}

/** Scan web/dist text-ish files + extension.zip listing/contents. */
export function checkWebIdentity(
  distDir: string = DIST,
  extZip: string = EXT_ZIP,
): IdentityCheckResult {
  const violations: string[] = [];
  if (!fs.existsSync(distDir)) {
    return { ok: false, violations: [`missing dist: ${distDir}`] };
  }

  for (const file of walkFiles(distDir)) {
    // Skip binary zips here — extension.zip is scanned separately via unzip -l/-p.
    if (/\.(zip|png|jpg|jpeg|gif|webp|woff2?|ttf|otf|ico)$/i.test(file)) continue;
    let text: string;
    try {
      text = fs.readFileSync(file, 'utf8');
    } catch {
      continue;
    }
    // Skip obvious binary garbage.
    if (text.includes('\u0000')) continue;
    scanText(path.relative(ROOT, file), text, violations);
  }

  if (fs.existsSync(extZip)) {
    try {
      const listing = execFileSync('unzip', ['-l', extZip], {
        encoding: 'utf8',
        timeout: 30_000,
        maxBuffer: 8 * 1024 * 1024,
      });
      scanText('extension.zip listing', listing, violations);
      // Sample text entries inside the zip.
      const names = listing
        .split('\n')
        .map(l => l.trim().split(/\s+/).pop() || '')
        .filter(n => /\.(html?|js|css|json|txt|md|svg)$/i.test(n));
      for (const name of names) {
        try {
          const body = execFileSync('unzip', ['-p', extZip, name], {
            encoding: 'utf8',
            timeout: 15_000,
            maxBuffer: 16 * 1024 * 1024,
          });
          scanText(`extension.zip:${name}`, body, violations);
        } catch {
          /* missing member — ignore */
        }
      }
    } catch (e) {
      violations.push(`extension.zip unreadable: ${e instanceof Error ? e.message : String(e)}`);
    }
  }

  return { ok: violations.length === 0, violations };
}

function main(): void {
  const r = checkWebIdentity();
  if (!r.ok) {
    console.error('SET 42e identity scrub FAILED — personal name / LinkedIn in ship artifacts:');
    for (const v of r.violations) console.error(`  ${v}`);
    process.exit(1);
  }
  console.log('SET 42e identity scrub ok (web/dist + extension.zip)');
}

if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
  main();
}
