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

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

async function selectText(page: Page, selector: string, needle: string) {
  await page.locator(selector).evaluate((element, text) => {
    const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
    let node = walker.nextNode() as Text | null;
    while (node) {
      const index = node.data.indexOf(text);
      if (index >= 0) {
        const range = document.createRange();
        range.setStart(node, index);
        range.setEnd(node, index + text.length);
        const selection = window.getSelection();
        selection?.removeAllRanges();
        selection?.addRange(range);
        document.dispatchEvent(new Event('selectionchange'));
        return;
      }
      node = walker.nextNode() as Text | null;
    }
    throw new Error(`Unable to select ${text}`);
  }, needle);
}

async function openSlashFromParagraph(page: Page, text: string, query: string) {
  const visual = page.locator('.interactive-latex-visual-editor');
  const paragraph = visual.locator('.interactive-visual-paragraph').filter({ hasText: text }).first();
  await paragraph.click();
  await paragraph.press('/');
  await page.keyboard.type(query);
  await expect(page.getByTestId('latex-visual-slash-menu')).toBeVisible();
  await expect(page.getByTestId('latex-visual-slash-query')).toContainText(`/${query}`);
}

test('R14.13 Visual uses native inline slash suggestions and contextual academic/link actions', async ({ page }) => {
  const workspaceName = `visual-r14-13-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });

  const source = page.getByLabel('LaTeX source editor');
  await source.fill([
    '\\documentclass{article}',
    '\\usepackage{hyperref}',
    '\\begin{document}',
    '\\section{Introduction}\\label{sec:intro}',
    '',
    'Visual command target.',
    '',
    'Link this phrase.',
    '',
    '\\end{document}',
    '',
  ].join('\n'));
  await source.press(saveShortcut);

  // Add a real bibliography file so /citation uses project data, not a placeholder.
  const explorer = page.getByTestId('interactive-file-explorer');
  await explorer.getByRole('button', { name: 'New File' }).click();
  const createFile = page.getByRole('dialog', { name: 'Create Project File' });
  await createFile.getByLabel('File name').fill('refs.bib');
  await createFile.getByRole('button', { name: 'Create' }).click();
  const bibSource = page.getByLabel('LaTeX source editor');
  await bibSource.fill([
    '@article{smith2026,',
    '  author = {Smith, Jane},',
    '  title = {Native Slash Citation},',
    '  year = {2026}',
    '}',
    '',
  ].join('\n'));
  await bibSource.press(saveShortcut);
  await page.getByRole('link', { name: /^main\.tex$/ }).click();
  await page.getByRole('button', { name: 'Visual' }).click();
  const visual = page.locator('.interactive-latex-visual-editor');
  await expect(visual).toBeVisible();

  // `/equ` stays at the Visual caret, filters the native suggestion menu, and
  // Enter invokes the highlighted command without focusing a second search box.
  await openSlashFromParagraph(page, 'Visual command target.', 'equ');
  await expect(page.getByTestId('latex-visual-slash-menu').getByText('Equation', { exact: true })).toBeVisible();
  await page.keyboard.press('Enter');
  await expect(page.getByTestId('mathlive-equation-editor')).toBeVisible();
  await page.getByTestId('mathlive-equation-editor').getByRole('button', { name: 'Cancel' }).click();
  await page.keyboard.type('After equation.');
  await expect(
    visual.locator('.interactive-visual-paragraph').filter({ hasText: 'After equation.' }),
  ).toBeVisible();

  // Contextual citation uses the existing bibliography picker.
  await openSlashFromParagraph(page, 'Visual command target.', 'cit');
  await page.keyboard.press('Enter');
  const citationDialog = page.getByRole('dialog').filter({ hasText: 'Insert citation' });
  await expect(citationDialog.getByText('Native Slash Citation')).toBeVisible();
  await citationDialog.getByText('Native Slash Citation').click();
  await citationDialog.getByRole('button', { name: /Use 1 citation/ }).click();

  // Contextual reference lists real project labels.
  await openSlashFromParagraph(page, 'Visual command target.', 'ref');
  await page.keyboard.press('Enter');
  const referenceDialog = page.getByRole('dialog', { name: 'Insert reference' });
  await expect(referenceDialog.getByText('sec:intro', { exact: true })).toBeVisible();
  await referenceDialog.getByText('sec:intro', { exact: true }).click();

  // The selection toolbar now opens a floating link editor rather than window.prompt().
  await selectText(page, '.interactive-visual-paragraph', 'Link this phrase');
  const toolbar = page.getByRole('toolbar', { name: 'Text formatting' });
  await toolbar.getByRole('button', { name: 'Link' }).click();
  const linkEditor = page.getByTestId('latex-visual-link-editor');
  await expect(linkEditor).toBeVisible();
  await linkEditor.getByLabel('Link URL').fill('https://example.com/paper');
  await linkEditor.getByRole('button', { name: 'Apply' }).click();

  await page.getByRole('button', { name: 'Source' }).click();
  await expect(source).toContainText('\\cite{smith2026}');
  await expect(source).toContainText('\\ref{sec:intro}');
  await expect(source).toContainText('\\href{https://example.com/paper}{Link this phrase}');
  await expect(source).not.toContainText('reference-key');
});
