import { execFileSync, spawn, spawnSync, type SpawnOptions } from 'node:child_process';
import { fileURLToPath, pathToFileURL } from 'node:url';

const packageRoot = fileURLToPath(new URL('..', import.meta.url));
const repoRoot = fileURLToPath(new URL('../../../..', import.meta.url));
const host = process.env.LEARNITIVE_E2E_HOST ?? '127.0.0.1';

function resolvePnpmInvocation(): { command: string; prefix: string[] } {
  const execPath = process.env.npm_execpath;
  if (execPath && /(?:^|[/\\])pnpm(?:\.c?js)?$/i.test(execPath)) {
    return { command: process.execPath, prefix: [execPath] };
  }
  if (spawnSync('sh', ['-c', 'command -v pnpm'], { stdio: 'ignore' }).status === 0) {
    return { command: 'pnpm', prefix: [] };
  }
  if (spawnSync('sh', ['-c', 'command -v corepack'], { stdio: 'ignore' }).status === 0) {
    return { command: 'corepack', prefix: ['pnpm'] };
  }
  throw new Error('Unable to resolve pnpm. Run "corepack enable" before the release E2E suite.');
}

const pnpm = resolvePnpmInvocation();
function spawnPnpm(args: string[], options: SpawnOptions) {
  return spawn(pnpm.command, [...pnpm.prefix, ...args], options);
}

const RELEASE_TEST_FILES = [
  'src/latex-source-editor.e2e.ts',
  'src/latex-session-r14-6.e2e.ts',
  'src/latex-compiler-r14-7.e2e.ts',
  'src/latex-mathlive-r14-8.e2e.ts',
  'src/latex-visual-r14-9.e2e.ts',
  'src/latex-visual-r14-11.e2e.ts',
  'src/latex-visual-r14-12-p0.e2e.ts',
  'src/latex-visual-r14-13.e2e.ts',
  'src/latex-core-recovery-r14-14.e2e.ts',
  'src/latex-split-scroll-r14-15.e2e.ts',
  'src/latex-source-r14-16.e2e.ts',
  'src/latex-session-r14-17.e2e.ts',
  'src/latex-controller-mathlive-r14-18.e2e.ts',
  'src/latex-source-r14-19.e2e.ts',
  'src/latex-certification-r14-20.e2e.ts',
  'src/latex-visual-editability-r14-22.e2e.ts',
  'src/latex-visual-structure-r14-23-2.e2e.ts',
  'src/latex-visual-interaction-r14-23-3.e2e.ts',
  'src/latex-table-r2.e2e.ts',
  'src/latex-live-draft-r4.e2e.ts',
  'src/latex-live-pdf-r5.e2e.ts',
  'src/latex-visual-bf2-integrity.e2e.ts',
  'src/latex-visual-bf4-interactive-native.e2e.ts',
  'src/latex-ui-r30.e2e.ts',
  'src/file-star-r14-22-1.e2e.ts',
  'src/latex-synctex-r14-22-2.e2e.ts',
  'src/latex-mathlive-r14-22-3.e2e.ts',
  'src/latex-compile-output-r14-22-4.e2e.ts',
  'src/latex-responsive-split-r14-22-5.e2e.ts',
  'src/latex-polish-r14-23-4.e2e.ts',
  'src/project-home-r14-23-5.e2e.ts',
  'src/latex-professional-r14-24-0.e2e.ts',
  'src/latex-certification-r14-24-1.e2e.ts',
  'src/latex-compiler-matrix-r14-24-1.e2e.ts',
  'src/latex-references-r14-25-0.e2e.ts',
  'src/latex-research-assistance-r14-25-1.e2e.ts',
  'src/latex-local-ai-r14-26-0.e2e.ts',
  'src/latex-ollama-bf51-1.e2e.ts',
  'src/latex-project-meaning-r14-26-1.e2e.ts',
  'src/latex-project-rag-r14-26-2.e2e.ts',
  'src/latex-paper-memory-pm3-r14-26-3.e2e.ts',
  'src/latex-paper-memory-pm5-r14-26-3.e2e.ts',
  'src/latex-paper-memory-pm6-r14-26-3.e2e.ts',
  'src/latex-paper-memory-pm7-r14-26-3.e2e.ts',
  'src/latex-paper-memory-pm8-r14-26-3.e2e.ts',
  'src/latex-paper-memory-pm9-r14-26-3.e2e.ts',
  'src/latex-papers-scale-r29.e2e.ts',
  'src/latex-comments-r14-27-0.e2e.ts',
  'src/latex-track-changes-r14-27-1.e2e.ts',
  'src/latex-review-history-r14-27-2.e2e.ts',
  'src/latex-research-terminal-r14-28-0.e2e.ts',
  'src/latex-scientific-runtime-certification-r14-28-12.e2e.ts',
  'src/latex-polish-r14-22-6.e2e.ts',
  'src/latex-visual-pdf-r14-4.e2e.ts',
  'src/browser-latex-compile.e2e.ts',
  'src/latex-compiler-r14-23-biber.e2e.ts',
  'src/project-zip.e2e.ts',
  'src/latex-integrated-r14-10.e2e.ts',
  'src/project-cross-browser-r14-10.e2e.ts',
  'src/learn-lms-release.e2e.ts',
] as const;

function resolvePort(): string {
  return process.env.LEARNITIVE_E2E_PORT ?? execFileSync(process.execPath, ['scripts/dev-ports.js', 'e2e'], { cwd: repoRoot, encoding: 'utf8' }).trim();
}
async function waitFor(url: string, exited: () => boolean) {
  const deadline = Date.now() + 60_000;
  while (Date.now() < deadline) {
    if (exited()) throw new Error('Production preview exited before becoming ready.');
    try { if ((await fetch(url)).ok) return; } catch { /* retry */ }
    await new Promise((resolve) => setTimeout(resolve, 250));
  }
  throw new Error(`Timed out waiting for production preview at ${url}`);
}
async function main() {
  const port = resolvePort();
  const baseUrl = `http://${host}:${port}`;
  let exited = false;
  const preview = spawnPnpm(['--dir', '../browser-entry', 'exec', 'vite', 'preview', '--configLoader', 'runner', '--host', host, '--port', port, '--strictPort'], {
    cwd: packageRoot, env: process.env, stdio: 'inherit',
  });
  preview.once('exit', () => { exited = true; });
  try {
    await waitFor(baseUrl, () => exited);
    const runPlaywright = async (args: string[]) => {
      const test = spawnPnpm([
        'exec',
        'playwright',
        'test',
        '-c',
        './playwright.config.ts',
        ...args,
      ], {
        cwd: packageRoot,
        env: { ...process.env, CI: '1', LEARNITIVE_RELEASE_QUALITY: '1', LEARNITIVE_E2E_BASE_URL: baseUrl, LEARNITIVE_E2E_EXTERNAL_SERVER: '1' },
        stdio: 'inherit',
      });
      return new Promise<number>((resolve, reject) => {
        test.once('error', reject);
        test.once('exit', (value, signal) => resolve(signal ? 1 : value ?? 1));
      });
    };

    const desktopCode = await runPlaywright([
      '--project=chromium',
      '--project=firefox',
      '--project=edge',
      '--project=webkit',
      ...RELEASE_TEST_FILES,
    ]);
    if (desktopCode !== 0) {
      process.exitCode = desktopCode;
      return;
    }

    const mobileCode = await runPlaywright([
      '--project=mobile-chromium',
      '--project=mobile-webkit',
      'src/latex-mobile-pdf-r14-23-1.e2e.ts',
      'src/learn-lms-release.e2e.ts',
    ]);
    process.exitCode = mobileCode;
  } finally {
    if (!preview.killed) preview.kill('SIGTERM');
  }
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) await main();
