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

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

test('R1 Visual newline/Enter/delete semantics round-trip through lexically safe canonical LaTeX', async ({ page }) => {
  const workspaceName = `visual-structure-r1-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });

  const source = page.getByLabel('LaTeX source editor');
  await source.fill([
    '\\documentclass{article}',
    '\\begin{document}',
    'First paragraph.',
    '\\begin{itemize}',
    '\\item First item',
    '\\end{itemize}',
    'Line one.',
    'Formatted \\textbf{bold text} tail.',
    'Existing\\newline{}break, legacy\\newline break and legacy\\\\break.',
    'DeleteMe\\newline{}KeepMe.',
    '\\end{document}',
    '',
  ].join('\n'));
  await source.press(saveShortcut);

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

  const firstParagraph = visual
    .locator('.interactive-visual-paragraph')
    .filter({ hasText: 'First paragraph.' })
    .first();
  await firstParagraph.click();
  await page.keyboard.press('End');
  await page.keyboard.press('Enter');
  await expect(visual.locator('.interactive-visual-synthetic-paragraph')).toHaveCount(0);
  await page.keyboard.type('Second paragraph.');
  await expect(
    visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Second paragraph.' }),
  ).toBeVisible();

  const firstItem = visual.locator('.interactive-visual-list-item').filter({ hasText: 'First item' }).first();
  await firstItem.click();
  await page.keyboard.press('End');
  await page.keyboard.press('Enter');
  await expect(visual.locator('.interactive-visual-list-item')).toHaveCount(2);
  await page.keyboard.press('Enter');
  await expect(visual.locator('.interactive-visual-synthetic-paragraph')).toHaveCount(0);
  await page.keyboard.type('After list.');
  await expect(
    visual.locator('.interactive-visual-paragraph').filter({ hasText: 'After list.' }),
  ).toBeVisible();

  // Repeated Shift+Enter must continue to work even before any text is typed
  // after the previous hard break.
  const lineOne = visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Line one.' }).first();
  await lineOne.click();
  await page.keyboard.press('End');
  await page.keyboard.press('Shift+Enter');
  await expect(visual.locator('.interactive-visual-synthetic-inline')).toHaveCount(0);
  await page.keyboard.press('Shift+Enter');
  await expect(visual.locator('.interactive-visual-hard-break')).toHaveCount(6);
  await expect(visual.locator('.interactive-visual-synthetic-inline')).toHaveCount(0);
  await page.keyboard.type('Line two.');
  await expect(visual).toContainText('Line two.');

  // Normal Enter after a hard break must create a genuine source paragraph.
  await page.keyboard.press('Enter');
  await page.keyboard.type('Line three.');
  await expect(
    visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Line three.' }),
  ).toBeVisible();

  // /newline resolves through the same command registry/mutation as
  // Shift+Enter, including inside a formatted rich inline.
  const bold = visual.locator('.interactive-visual-inline-bold').filter({ hasText: 'bold text' }).first();
  await bold.click();
  await page.keyboard.press('Home');
  for (let index = 0; index < 4; index += 1) await page.keyboard.press('ArrowRight');
  await page.keyboard.type('/newline');
  await expect(page.getByTestId('latex-visual-slash-menu')).toBeVisible();
  await page.keyboard.press('Enter');
  await expect(visual.locator('.interactive-visual-hard-break')).toHaveCount(7);

  // Backspace immediately after a hard-break atom removes exactly the break,
  // not either neighboring text run.
  const keepMe = visual.filter({ hasText: 'KeepMe.' }).first();
  await keepMe.click();
  await page.keyboard.press('Home');
  await page.keyboard.press('Backspace');
  await expect(visual).toContainText('DeleteMeKeepMe.');

  await page.getByRole('button', { name: 'Source' }).click();
  await expect(source).toContainText('First paragraph.');
  await expect(source).toContainText('Second paragraph.');
  await expect(source).toContainText('\\item First item');
  await expect(source).not.toContainText('\\item \n\\end{itemize}');
  await expect(source).toContainText('After list.');
  await expect(source).toContainText('Line one.\\newline{}\\newline{}Line two.');
  await expect(source).toContainText('Line three.');
  await expect(source).toContainText('\\textbf{bold}\\newline{}\\textbf{ text}');
  await expect(source).toContainText(
    'Existing\\newline{}break, legacy\\newline break and legacy\\\\break.',
  );
  await expect(source).toContainText('DeleteMeKeepMe.');
  await expect(source).not.toContainText('DeleteMe\\newline{}KeepMe.');
  await expect(source).not.toContainText('\\newlinef');
  await expect(source).not.toContainText('\\par');
});

test('R31 BF3 Phase 4 deletes a Visual list canonically and keeps typing at the source-backed gap', async ({ page }) => {
  const workspaceName = `visual-list-delete-bf3-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });
  const source = page.getByLabel('LaTeX source editor');
  await source.fill([
    '\\documentclass{article}',
    '\\begin{document}',
    'Before list.',
    '\\begin{itemize}',
    '\\item Alpha',
    '\\item Beta',
    '\\end{itemize}',
    'After list.',
    '\\end{document}',
    '',
  ].join('\n'));
  await source.press(saveShortcut);
  await page.getByRole('button', { name: 'Visual' }).click();

  const visual = page.locator('.interactive-latex-visual-editor');
  await visual.locator('.interactive-visual-list-item').first().click();
  await visual.locator('.interactive-visual-list-actions-summary').click();
  await page.getByRole('button', { name: 'Delete list', exact: true }).click();
  await expect(visual.locator('.interactive-visual-list-item')).toHaveCount(0);
  await page.keyboard.type('Replacement paragraph.');
  await expect(visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Replacement paragraph.' })).toBeVisible();

  await page.getByRole('button', { name: 'Source' }).click();
  await expect(source).not.toContainText('\\begin{itemize}');
  await expect(source).not.toContainText('\\item Alpha');
  await expect(source).toContainText('Replacement paragraph.');
  await expect(source).toContainText('After list.');
});
