import { expect, type Page, test } from '@playwright/test';
import { createBrowserWorkspace } from './common';

const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';

async function saveSource(page: Page, content: string) {
  const source = page.getByLabel('LaTeX source editor');
  await source.fill(content);
  await source.press(saveShortcut);
  await expect(page.getByText('Saved', { exact: true })).toBeVisible();
}

async function createTextFile(page: Page, fileName: string, content: string) {
  const explorer = page.getByTestId('interactive-file-explorer');
  await explorer.getByRole('button', { name: 'New File' }).click();
  const dialog = page.getByRole('dialog', { name: 'Create Project File' });
  await dialog.getByLabel('File name').fill(fileName);
  await dialog.getByRole('button', { name: 'Create' }).click();
  await saveSource(page, content);
}

async function setCompiler(page: Page, label: 'pdfLaTeX' | 'XeLaTeX' | 'LuaLaTeX') {
  await page.getByRole('button', { name: 'Compiler settings' }).click();
  const compiler = page.getByLabel('Compiler engine');
  await compiler.click();
  await page.getByRole('option', { name: label }).click();
  await page.getByRole('button', { name: 'Close', exact: true }).click();
}

async function setPdfMode(page: Page, mode: 'Fast Preview' | 'Full / SyncTeX') {
  await page.getByRole('button', { name: 'Compiler settings' }).click();
  const select = page.getByLabel('pdfLaTeX mode');
  await select.click();
  await page.getByRole('option', { name: mode }).click();
  await page.getByRole('button', { name: 'Close', exact: true }).click();
}

async function compileAndShowPdf(page: Page, timeout = 180_000) {
  await page.getByRole('button', { name: 'Compile', exact: true }).click();
  const preview = page.getByTestId('interactive-pdf-preview');
  await expect(preview.getByTestId('interactive-log-status-dot')).toHaveAttribute(
    'data-compile-status',
    'success',
    { timeout },
  );
  await preview.getByTestId('interactive-output-tabs').getByRole('button', { name: 'PDF' }).click();
  await expect(preview.locator('canvas').first()).toBeVisible({ timeout: 60_000 });
  return preview;
}

test('R14.24.1 full compiler matrix covers Fast/Full, Xe/Lua, BibTeX, bundled, remote and offline-cached packages', async ({
  page,
  browserName,
}) => {
  test.skip(
    browserName !== 'chromium',
    'The full compiler matrix is certified once in Chromium; the release browser matrix separately covers Edge, Firefox and WebKit.',
  );
  test.setTimeout(900_000);

  const workspaceName = `compiler-matrix-r14-24-1-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });

  // pdfLaTeX Fast + a non-core bundled package.
  await setPdfMode(page, 'Fast Preview');
  await saveSource(page, [
    '\\documentclass{article}',
    '\\usepackage{booktabs}',
    '\\begin{document}',
    '\\begin{tabular}{ll}',
    '\\toprule',
    'A & B \\\\',
    '\\bottomrule',
    '\\end{tabular}',
    '\\end{document}',
    '',
  ].join('\n'));
  await compileAndShowPdf(page);

  // A package intentionally absent from the bundled manifest must resolve via
  // the validated static/remote path, then compile again from browser cache
  // while the browser context is offline.
  await page.evaluate(async () => {
    const cache = await caches.open('interactive-texlive-swiftlatex-r14-7-v1');
    await cache.put(
      '/texlive/swiftlatex-r3-v1/physics.sty',
      new Response('<!doctype html><html><body>poisoned package cache</body></html>'),
    );
  });

  await saveSource(page, [
    '\\documentclass{article}',
    '\\usepackage{physics}',
    '\\begin{document}',
    '$\\qty{x}=1$',
    '\\end{document}',
    '',
  ].join('\n'));
  await compileAndShowPdf(page);
  await expect
    .poll(() =>
      page.evaluate(async () => {
        const cache = await caches.open('interactive-texlive-swiftlatex-r14-7-v1');
        const keys = await cache.keys();
        const physics = keys.find((request) =>
          decodeURIComponent(request.url).toLowerCase().includes('physics.sty'),
        );
        if (!physics) return false;
        const response = await cache.match(physics);
        const text = response ? await response.clone().text() : '';
        return Boolean(response) && !/<(?:!doctype\s+html|html)/iu.test(text);
      }),
    )
    .toBe(true);

  await page.context().setOffline(true);
  try {
    // SwiftLaTeX flushes its compile work area between runs, so this successful
    // offline recompile exercises the persistent service-worker package cache.
    await compileAndShowPdf(page);
  } finally {
    await page.context().setOffline(false);
  }

  // pdfLaTeX Full / SyncTeX.
  await setPdfMode(page, 'Full / SyncTeX');
  await saveSource(page, [
    '\\documentclass{article}',
    '\\begin{document}',
    'Full browser pdfLaTeX with SyncTeX.',
    '\\end{document}',
    '',
  ].join('\n'));
  let preview = await compileAndShowPdf(page);
  await expect(preview.getByText('SyncTeX ready', { exact: true })).toBeVisible();

  // XeLaTeX and LuaLaTeX are BusyTeX full-profile engines.
  await setCompiler(page, 'XeLaTeX');
  await saveSource(page, [
    '\\documentclass{article}',
    '\\begin{document}',
    'XeLaTeX browser matrix.',
    '\\end{document}',
    '',
  ].join('\n'));
  await compileAndShowPdf(page, 240_000);

  await setCompiler(page, 'LuaLaTeX');
  await saveSource(page, [
    '\\documentclass{article}',
    '\\begin{document}',
    'LuaLaTeX browser matrix.',
    '\\end{document}',
    '',
  ].join('\n'));
  await compileAndShowPdf(page, 240_000);

  // BibTeX path. Biber has its own dedicated release test.
  await setCompiler(page, 'pdfLaTeX');
  await setPdfMode(page, 'Full / SyncTeX');
  await createTextFile(
    page,
    'matrix-refs.bib',
    '@article{matrix2026,title={Matrix Reference},author={Researcher, Ada},year={2026}}\n',
  );
  await page.getByRole('link', { name: /^main\.tex$/ }).click();
  await saveSource(page, [
    '\\documentclass{article}',
    '\\begin{document}',
    'BibTeX citation: \\cite{matrix2026}.',
    '\\bibliographystyle{plain}',
    '\\bibliography{matrix-refs}',
    '\\end{document}',
    '',
  ].join('\n'));
  preview = await compileAndShowPdf(page, 240_000);
  await preview.getByTestId('interactive-output-tabs').getByRole('button', { name: /Log/ }).click();
  await expect(preview.getByTestId('interactive-compile-log')).not.toContainText(/Citation.*undefined/i);
});
