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

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

async function selectCodeMirrorText(page: import('@playwright/test').Page, needle: string) {
  await page.locator('.cm-content').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);
        element.dispatchEvent(new Event('selectionchange', { bubbles: true }));
        return;
      }
      node = walker.nextNode() as Text | null;
    }
    throw new Error(`Unable to select ${text}`);
  }, needle);
}

test('R14.27.1 shares one tracked change across Source and Visual accept/reject flows', async ({ page }) => {
  const workspaceName = `track-r14-27-1-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });

  const source = page.getByLabel('LaTeX source editor');
  const original = [
    '\\documentclass{article}',
    '\\begin{document}',
    'A robust local claim.',
    '',
    'Second visual paragraph.',
    '\\end{document}',
    '',
  ].join('\n');
  await source.fill(original);
  await source.press(saveShortcut);

  const track = page.getByTestId('track-changes-toggle');
  await track.click();
  await expect(track).toHaveAttribute('aria-pressed', 'true');
  await expect(page.getByTestId('track-changes-status')).toContainText('Changes tracked');

  // Source-created replacement stays a review projection until accepted.
  await selectCodeMirrorText(page, 'robust');
  await page.keyboard.type('resilient');
  const sidebar = page.getByTestId('review-sidebar');
  await expect(sidebar).toContainText('replace');
  await expect(sidebar).toContainText('robust');
  await expect(sidebar).toContainText('resilient');
  await expect(page.locator('.cm-interactive-change-replace')).toHaveCount(1);

  await page.getByRole('button', { name: 'Visual', exact: true }).click();
  const visual = page.locator('.interactive-latex-visual-editor');
  await expect(visual.locator('.interactive-visual-paragraph').filter({ hasText: 'A resilient local claim.' })).toBeVisible();
  await expect(page.locator('.interactive-visual-change-anchor')).toHaveCount(1);

  // Accept in Visual via the shared review sidebar, then verify canonical Source.
  await sidebar.getByRole('button', { name: 'Accept', exact: true }).click();
  await sidebar.getByRole('button', { name: 'Resolved', exact: true }).click();
  await expect(sidebar).toContainText('accepted');
  await sidebar.getByRole('button', { name: 'Open', exact: true }).click();
  await page.getByRole('button', { name: 'Source', exact: true }).click();
  await expect(source).toContainText('A resilient local claim.');

  // Visual-created insertion becomes the same ReviewChange in Source.
  await page.getByRole('button', { name: 'Visual', exact: true }).click();
  const second = visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Second visual paragraph.' }).first();
  await second.click();
  await page.keyboard.press('End');
  await page.keyboard.type(' Tracked');
  await expect(second).toContainText('Second visual paragraph. Tracked');

  await page.getByRole('button', { name: 'Source', exact: true }).click();
  await expect(source).toContainText('Second visual paragraph. Tracked');
  await expect(page.locator('.cm-interactive-change-insert')).toHaveCount(1);

  // Reject in Source and prove Visual returns to the original canonical text.
  await sidebar.getByRole('button', { name: 'Reject', exact: true }).click();
  await expect(source).not.toContainText('Second visual paragraph. Tracked');
  await page.getByRole('button', { name: 'Visual', exact: true }).click();
  await expect(visual.locator('.interactive-visual-paragraph').filter({ hasText: 'Second visual paragraph.' })).toBeVisible();
  await expect(visual).not.toContainText('Second visual paragraph. Tracked');

  // Review history stays in Learnitive review storage and never writes metadata into canonical LaTeX.
  await page.getByRole('button', { name: 'Source', exact: true }).click();
  const reviewedSource = await source.inputValue();
  expect(reviewedSource).not.toContain('LEARNITIVE-REVIEW');
  expect(reviewedSource).not.toContain('<###');
});

test('BF4.1 keeps native Visual deletions visible and rejectable as pending review content', async ({ page }) => {
  const workspaceName = `track-bf41-delete-${Date.now()}`;
  await createBrowserWorkspace(page, { workspaceName });
  const source = page.getByLabel('LaTeX source editor');
  await source.fill([
    '\\documentclass{article}',
    '\\begin{document}',
    'Delete this word.',
    '\\end{document}',
    '',
  ].join('\n'));
  await source.press(saveShortcut);

  const track = page.getByTestId('track-changes-toggle');
  await track.click();
  await page.getByRole('button', { name: 'Visual', exact: true }).click();
  const paragraph = page
    .locator('.interactive-latex-visual-editor .interactive-visual-paragraph')
    .filter({ hasText: 'Delete this word.' })
    .first();
  await paragraph.click();
  await page.keyboard.press('Home');
  for (let index = 0; index < 'Delete'.length; index += 1) {
    await page.keyboard.press('Shift+ArrowRight');
  }
  await page.keyboard.press('Backspace');

  const deletion = page.locator('.interactive-visual-change-deletion-widget').first();
  await expect(deletion).toHaveText('Delete');
  await expect(deletion).toHaveCSS('text-decoration-line', 'line-through');
  const sidebar = page.getByTestId('review-sidebar');
  await expect(sidebar).toContainText('delete');
  await expect(sidebar).toContainText('Delete');

  await page.getByRole('button', { name: 'Source', exact: true }).click();
  await expect(source).not.toContainText('Delete this word.');
  await page.getByRole('button', { name: 'Visual', exact: true }).click();
  await expect(page.locator('.interactive-visual-change-deletion-widget')).toContainText('Delete');

  await sidebar.getByRole('button', { name: 'Reject', exact: true }).click();
  await expect(page.locator('.interactive-visual-change-deletion-widget')).toHaveCount(0);
  await expect(
    page.locator('.interactive-latex-visual-editor .interactive-visual-paragraph').filter({ hasText: 'Delete this word.' }),
  ).toBeVisible();
});
