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

async function selectText(page: import('@playwright/test').Page, selector: string, needle: string) {
  await page.locator(selector).evaluate((element, text) => {
    const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
    let node: Text | null = 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);
}

test('R14.11 Visual mode restores Learnitive slash menu, plus handle, drag grip and selection toolbar', async ({ page }) => {
  const workspaceName = `visual-r14-11-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });
  await page.getByRole('link', { name: /^main\.tex$/ }).click();

  const source = page.getByLabel('LaTeX source editor');
  await source.fill([
    '\\documentclass{article}',
    '\\usepackage{hyperref}',
    '\\begin{document}',
    '\\section{Original}',
    '',
    'First block.',
    '',
    'Visual formatting target.',
    '',
    '\\end{document}',
    '',
  ].join('\n'));
  await source.press(process.platform === 'darwin' ? 'Meta+s' : 'Control+s');

  await page.getByRole('button', { name: 'Visual' }).click();
  const visual = page.locator('.interactive-latex-visual-editor');
  await expect(visual).toBeVisible();

  // Original Learnitive-style left + opens the same Visual slash menu.
  const firstBlock = visual.locator('.interactive-visual-paragraph').filter({ hasText: 'First block.' });
  await firstBlock.hover();
  const addBlock = page.getByRole('button', { name: 'Add LaTeX block' });
  await expect(addBlock).toBeVisible();
  await addBlock.click();
  const slash = page.getByTestId('latex-visual-slash-menu');
  await expect(slash).toBeVisible();
  await slash.getByText('Section', { exact: true }).click();
  await expect(visual.locator('.interactive-visual-heading').filter({ hasText: 'Section title' })).toBeVisible();

  // Typing / in Visual mode opens the same source-backed command palette.
  const formattingParagraph = visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Visual formatting target.' });
  await formattingParagraph.click();
  await formattingParagraph.press('/');
  await expect(page.getByTestId('latex-visual-slash-menu')).toBeVisible();
  await page.getByTestId('latex-visual-slash-menu').getByText('Itemize', { exact: true }).click();
  await expect(visual.locator('.interactive-visual-list-item')).toBeVisible();

  // Text selection gets the Learnitive-style floating formatting bubble, but the
  // operation wraps only the exact canonical source range.
  await selectText(page, '.interactive-visual-paragraph', 'formatting');
  const toolbar = page.getByRole('toolbar', { name: 'Text formatting' });
  await expect(toolbar).toBeVisible();
  await toolbar.getByRole('button', { name: 'Bold' }).click();
  await expect(visual.locator('.interactive-visual-inline-bold')).toContainText('formatting');

  // The adjacent grip is functional: move the first paragraph after the
  // formatted paragraph and prove canonical source order changed.
  const refreshedFirst = visual.locator('.interactive-visual-paragraph').filter({ hasText: 'First block.' });
  const refreshedFormatting = visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Visual' });
  await refreshedFirst.hover();
  const dragHandle = page.getByRole('button', { name: 'Drag LaTeX block to move' });
  await expect(dragHandle).toBeVisible();
  await dragHandle.dragTo(refreshedFormatting);

  await page.getByRole('button', { name: 'Source' }).click();
  await expect(source).toContainText('\\section{Section title}');
  await expect(source).toContainText('\\begin{itemize}');
  await expect(source).toContainText('Visual \\textbf{formatting} target.');
  const canonical = (await source.textContent()) ?? '';
  expect(canonical.indexOf('Visual \\textbf{formatting} target.')).toBeLessThan(canonical.indexOf('First block.'));
});
