import type { DeltaInsert } from '@inline/types.js';

import { expect } from '@playwright/test';

import {
  activeEmbed,
  captureHistory,
  dragBetweenCoords,
  dragBetweenIndices,
  enterPlaygroundRoom,
  focusRichText,
  focusTitle,
  getBoundingBox,
  getEditorHostLocator,
  getPageSnapshot,
  getSelectionRect,
  initEmptyParagraphState,
  initImageState,
  initThreeParagraphs,
  pasteByKeyboard,
  pressArrowDown,
  pressArrowUp,
  pressEnter,
  pressEscape,
  pressTab,
  scrollToBottom,
  scrollToTop,
  selectAllBlocksByKeyboard,
  selectAllByKeyboard,
  setInlineRangeInInlineEditor,
  setSelection,
  switchReadonly,
  type,
  undoByKeyboard,
  updateBlockType,
  waitNextFrame,
  withPressKey,
} from './utils/actions/index.js';
import {
  assertAlmostEqual,
  assertBlockChildrenIds,
  assertExists,
  assertLocatorVisible,
  assertRichImage,
  assertRichTextInlineRange,
  assertRichTexts,
} from './utils/asserts.js';
import { test } from './utils/playwright.js';
import { getFormatBar } from './utils/query.js';

test('should format quick bar show when select text', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await dragBetweenIndices(page, [0, 0], [2, 3]);
  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  const box = await formatBar.boundingBox();
  if (!box) {
    throw new Error("formatBar doesn't exist");
  }
  const rect = await getSelectionRect(page);
  assertAlmostEqual(box.x - rect.left, -98, 5);
  assertAlmostEqual(box.y - rect.bottom, 10, 5);

  // Click the edge of the format quick bar
  await page.mouse.click(box.x + 4, box.y + box.height / 2);
  // Even not any button is clicked, the format quick bar should't be hidden
  await expect(formatBar).toBeVisible();

  const noteEl = page.locator('affine-note');
  const { x, y } = await getBoundingBox(noteEl);
  await page.mouse.click(x + 100, y + 20);
  await expect(formatBar).not.toBeVisible();
});

test('should format quick bar show when clicking drag handle', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  const locator = page.locator('affine-paragraph').first();
  await locator.hover();
  const dragHandle = page.locator('.affine-drag-handle-grabber');
  const dragHandleRect = await dragHandle.boundingBox();
  assertExists(dragHandleRect);
  await dragHandle.click();

  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  const box = await formatBar.boundingBox();
  if (!box) {
    throw new Error("formatBar doesn't exist");
  }
  assertAlmostEqual(box.x, 251, 5);
  assertAlmostEqual(box.y - dragHandleRect.y, -55.5, 5);
});

test('should format quick bar show when select text by keyboard', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await focusRichText(page);
  await type(page, 'hello world');
  await withPressKey(page, 'Shift', async () => {
    let i = 10;
    while (i--) {
      await page.keyboard.press('ArrowLeft');
      await waitNextFrame(page);
    }
  });

  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  const formatBarBox = await formatBar.boundingBox();
  if (!formatBarBox) {
    throw new Error("formatBar doesn't exist");
  }
  let selectionRect = await getSelectionRect(page);
  assertAlmostEqual(formatBarBox.x - selectionRect.x, -107, 3);
  assertAlmostEqual(
    formatBarBox.y + formatBarBox.height - selectionRect.top,
    -10,
    3
  );

  await page.keyboard.press('ArrowLeft');
  await expect(formatBar).not.toBeVisible();

  await withPressKey(page, 'Shift', async () => {
    let i = 10;
    while (i--) {
      await page.keyboard.press('ArrowRight');
      await waitNextFrame(page);
    }
  });

  await expect(formatBar).toBeVisible();

  const rightBox = await formatBar.boundingBox();
  if (!rightBox) {
    throw new Error("formatBar doesn't exist");
  }
  // The x position of the format quick bar depends on the font size
  // so there are slight differences in different environments
  selectionRect = await getSelectionRect(page);
  assertAlmostEqual(formatBarBox.x - selectionRect.x, -107, 3);
  assertAlmostEqual(
    formatBarBox.y + formatBarBox.height - selectionRect.top,
    -10,
    3
  );
});

test('should format quick bar can only display one at a time', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await dragBetweenIndices(page, [0, 3], [0, 0]);
  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  await dragBetweenIndices(page, [2, 0], [2, 3]);
  await expect(formatBar).toHaveCount(1);
});

test('should format quick bar hide when type text', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await dragBetweenIndices(page, [0, 0], [2, 3]);
  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();
  await type(page, '1');
  await expect(formatBar).not.toBeVisible();
});

test('should format quick bar be able to format text', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  // drag only the `456` paragraph
  await dragBetweenIndices(page, [1, 0], [1, 3]);

  const { boldBtn, italicBtn, underlineBtn, strikeBtn, codeBtn } =
    getFormatBar(page);

  await expect(boldBtn).not.toHaveAttribute('active', '');
  await expect(italicBtn).not.toHaveAttribute('active', '');
  await expect(underlineBtn).not.toHaveAttribute('active', '');
  await expect(strikeBtn).not.toHaveAttribute('active', '');
  await expect(codeBtn).not.toHaveAttribute('active', '');

  await boldBtn.click();
  await italicBtn.click();
  await underlineBtn.click();
  await strikeBtn.click();
  await codeBtn.click();

  // The button should be active after click
  await expect(boldBtn).toHaveAttribute('active', '');
  await expect(italicBtn).toHaveAttribute('active', '');
  await expect(underlineBtn).toHaveAttribute('active', '');
  await expect(strikeBtn).toHaveAttribute('active', '');
  await expect(codeBtn).toHaveAttribute('active', '');

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_init.json`
  );

  await boldBtn.click();
  await underlineBtn.click();
  await codeBtn.click();

  await waitNextFrame(page);

  // The bold button should be inactive after click again
  await expect(boldBtn).not.toHaveAttribute('active', '');
  await expect(italicBtn).toHaveAttribute('active', '');
  await expect(underlineBtn).not.toHaveAttribute('active', '');
  await expect(strikeBtn).toHaveAttribute('active', '');
  await expect(codeBtn).not.toHaveAttribute('active', '');

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_finial.json`
  );
});

test('should format quick bar be able to change background color', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  // select `456` paragraph by dragging
  await dragBetweenIndices(page, [1, 0], [1, 3]);

  const { highlight } = getFormatBar(page);

  await highlight.highlightBtn.hover();
  await expect(highlight.redForegroundBtn).toBeVisible();
  await expect(highlight.highlightBtn).toHaveAttribute(
    'data-last-used',
    'unset'
  );
  await highlight.redForegroundBtn.click();
  await expect(highlight.highlightBtn).toHaveAttribute(
    'data-last-used',
    'var(--affine-text-highlight-foreground-red)'
  );

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_init.json`
  );

  // select `123` paragraph by ctrl + a
  await focusRichText(page);
  await selectAllByKeyboard(page);
  // use last used color
  await highlight.highlightBtn.click();

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_select_all.json`
  );

  await expect(highlight.defaultColorBtn).toBeVisible();
  await highlight.defaultColorBtn.click();

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_default_color.json`
  );
});

test('should format quick bar be able to format text when select multiple line', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await dragBetweenIndices(page, [0, 0], [2, 3]);

  const { boldBtn } = getFormatBar(page);
  await expect(boldBtn).not.toHaveAttribute('active', '');
  await boldBtn.click();

  // The bold button should be active after click
  await expect(boldBtn).toHaveAttribute('active', '');
  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_init.json`
  );

  await boldBtn.click();
  await expect(boldBtn).not.toHaveAttribute('active', '');
  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_finial.json`
  );
});

test('should format quick bar be able to link text', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  // drag only the `456` paragraph
  await dragBetweenIndices(page, [1, 0], [1, 3]);

  const { linkBtn } = getFormatBar(page);
  await expect(linkBtn).not.toHaveAttribute('active', '');
  await linkBtn.click();

  const linkPopoverInput = page.locator('.affine-link-popover-input');
  await expect(linkPopoverInput).toBeVisible();

  await type(page, 'https://www.example.com');
  await pressEnter(page);

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_init.json`
  );

  // FIXME: remove this
  await focusRichText(page);
  await setSelection(page, 3, 0, 3, 3);
  // The link button should be active after click
  await expect(linkBtn).toHaveAttribute('active', '');
  await linkBtn.click();
  await waitNextFrame(page);
  await expect(linkBtn).not.toHaveAttribute('active', '');

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_finial.json`
  );
});

test('should format quick bar be able to change to heading paragraph type', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  // drag only the `456` paragraph
  await dragBetweenIndices(page, [0, 0], [0, 3]);

  const { openParagraphMenu, h1Btn, bulletedBtn } = getFormatBar(page);
  await openParagraphMenu();

  await expect(h1Btn).toBeVisible();
  await h1Btn.click();

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_init.json`
  );

  await bulletedBtn.click();
  await openParagraphMenu();

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_bulleted.json`
  );

  const { textBtn } = getFormatBar(page);
  await textBtn.click();

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_finial.json`
  );
  await page.waitForTimeout(10);
  // The paragraph button should prevent selection after click
  await assertRichTextInlineRange(page, 0, 0, 3);
});

test('should format quick bar show when double click text', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  const editorHost = getEditorHostLocator(page);
  const richText = editorHost.locator('rich-text').nth(0);
  await richText.dblclick({
    position: { x: 10, y: 10 },
  });
  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();
});

test('should format quick bar not show at readonly mode', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await switchReadonly(page);

  await dragBetweenIndices(page, [0, 0], [2, 3]);
  const { formatBar } = getFormatBar(page);
  await expect(formatBar).not.toBeVisible();

  const editorHost = getEditorHostLocator(page);
  const richText = editorHost.locator('rich-text').nth(0);
  await richText.dblclick({
    position: { x: 10, y: 10 },
  });
  await expect(formatBar).not.toBeVisible();
});

test('should format bar follow scroll', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  for (let i = 0; i < 30; i++) {
    await pressEnter(page);
  }

  await scrollToTop(page);

  await dragBetweenIndices(page, [0, 0], [2, 3]);
  const { formatBar, boldBtn } = getFormatBar(page);
  await assertLocatorVisible(page, formatBar);

  await scrollToBottom(page);

  await assertLocatorVisible(page, formatBar, false);

  // should format bar follow scroll after click bold button
  await scrollToTop(page);
  await assertLocatorVisible(page, formatBar);
  await boldBtn.click();
  await scrollToBottom(page);
  await assertLocatorVisible(page, formatBar, false);

  // should format bar follow scroll after transform text type
  await scrollToTop(page);
  await assertLocatorVisible(page, formatBar);
  await updateBlockType(page, 'affine:list', 'bulleted');
  await scrollToBottom(page);
  await assertLocatorVisible(page, formatBar, false);
});

test('should format quick bar position correct at the start of second line', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await page.evaluate(() => {
    const { doc } = window;
    const rootId = doc.addBlock('affine:page', {
      title: new doc.Text(),
    });
    const note = doc.addBlock('affine:note', {}, rootId);
    const text = new doc.Text('a'.repeat(100));
    const paragraphId = doc.addBlock('affine:paragraph', { text }, note);
    return paragraphId;
  });
  // await focusRichText(page);
  const editorHost = getEditorHostLocator(page);
  const locator = editorHost.locator('.inline-editor').nth(0);
  const textBox = await locator.boundingBox();
  if (!textBox) {
    throw new Error("Can't get bounding box");
  }
  // Drag to the start of the second line
  await dragBetweenCoords(
    page,
    { x: textBox.x + textBox.width - 1, y: textBox.y + textBox.height - 1 },
    { x: textBox.x, y: textBox.y + textBox.height - 1 }
  );

  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();
  await waitNextFrame(page);

  const formatBox = await formatBar.boundingBox();
  if (!formatBox) {
    throw new Error("formatBar doesn't exist");
  }
  const selectionRect = await getSelectionRect(page);
  assertAlmostEqual(formatBox.x - selectionRect.x, -99, 5);
  assertAlmostEqual(formatBox.y + formatBox.height - selectionRect.top, 68, 5);
});

test('should format quick bar action status updated while undo', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await focusRichText(page);
  await type(page, 'helloworld');
  await captureHistory(page);
  await dragBetweenIndices(page, [0, 1], [0, 6]);

  const { formatBar, boldBtn } = getFormatBar(page);
  await expect(formatBar).toBeVisible();
  await expect(boldBtn).toBeVisible();

  await expect(boldBtn).not.toHaveAttribute('active', '');
  await boldBtn.click();
  await expect(boldBtn).toHaveAttribute('active', '');

  await undoByKeyboard(page);
  await expect(formatBar).toBeVisible();
  await expect(boldBtn).not.toHaveAttribute('active', '');
});

test('should format quick bar work in single block selection', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  await dragBetweenIndices(
    page,
    [1, 0],
    [1, 3],
    { x: -26 - 24, y: -10 },
    { x: 0, y: 0 }
  );
  const blockSelections = page
    .locator('affine-block-selection')
    .locator('visible=true');
  await expect(blockSelections).toHaveCount(1);

  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  const formatRect = await formatBar.boundingBox();
  const selectionRect = await blockSelections.boundingBox();
  assertExists(formatRect);
  assertExists(selectionRect);
  assertAlmostEqual(formatRect.x - selectionRect.x, 147.5, 10);
  assertAlmostEqual(formatRect.y - selectionRect.y, 33, 10);

  const boldBtn = formatBar.getByTestId('bold');
  await boldBtn.click();
  const italicBtn = formatBar.getByTestId('italic');
  await italicBtn.click();
  const underlineBtn = formatBar.getByTestId('underline');
  await underlineBtn.click();
  //FIXME: trt to cancel italic
  // Cancel italic
  // await italicBtn.click();

  await expect(blockSelections).toHaveCount(1);

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}.json`
  );

  const noteEl = page.locator('affine-note');
  const { x, y, width, height } = await getBoundingBox(noteEl);
  await page.mouse.click(x + width / 2, y + height / 2);
  await expect(formatBar).not.toBeVisible();
});

test('should format quick bar work in multiple block selection', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  await dragBetweenIndices(
    page,
    [2, 3],
    [0, 0],
    { x: 20, y: 20 },
    { x: 0, y: 0 }
  );
  const blockSelections = page
    .locator('affine-block-selection')
    .locator('visible=true');
  await expect(blockSelections).toHaveCount(3);

  const formatBarController = getFormatBar(page);
  await expect(formatBarController.formatBar).toBeVisible();

  const box = await formatBarController.formatBar.boundingBox();
  if (!box) {
    throw new Error("formatBar doesn't exist");
  }
  const rect = await blockSelections.first().boundingBox();
  assertExists(rect);
  assertAlmostEqual(box.x - rect.x, 147.5, 10);
  assertAlmostEqual(box.y - rect.y, 99, 10);

  await formatBarController.boldBtn.click();
  await formatBarController.italicBtn.click();
  await formatBarController.underlineBtn.click();
  // Cancel italic
  await formatBarController.italicBtn.click();

  await expect(blockSelections).toHaveCount(3);

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}.json`
  );

  const noteEl = page.locator('affine-note');
  const { x, y, width, height } = await getBoundingBox(noteEl);
  await page.mouse.click(x + width / 2, y + height / 2);
  await expect(formatBarController.formatBar).not.toBeVisible();
});

test('should format quick bar with block selection works when update block type', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  await dragBetweenIndices(
    page,
    [2, 3],
    [0, 0],
    { x: 20, y: 20 },
    { x: 0, y: 0 }
  );
  const blockSelections = page
    .locator('affine-block-selection')
    .locator('visible=true');
  await expect(blockSelections).toHaveCount(3);

  const formatBarController = getFormatBar(page);
  await expect(formatBarController.formatBar).toBeVisible();

  await formatBarController.openParagraphMenu();
  await formatBarController.bulletedBtn.click();
  await expect(blockSelections).toHaveCount(3);

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_init.json`
  );

  await expect(formatBarController.formatBar).toBeVisible();
  await formatBarController.h1Btn.click();
  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_final.json`
  );
  await expect(formatBarController.formatBar).toBeVisible();
  await expect(blockSelections).toHaveCount(3);

  const noteEl = page.locator('affine-note');
  const { x, y, width, height } = await getBoundingBox(noteEl);
  await page.mouse.click(x + width / 2, y + height / 2);
  await expect(formatBarController.formatBar).not.toBeVisible();
});

test('should format quick bar show after convert to code block', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  const formatBarController = getFormatBar(page);
  await dragBetweenIndices(
    page,
    [2, 3],
    [0, 0],
    { x: 20, y: 20 },
    { x: 0, y: 0 }
  );
  await expect(formatBarController.formatBar).toBeVisible();
  await expect(formatBarController.formatBar).toBeInViewport();

  await formatBarController.openParagraphMenu();
  await formatBarController.codeBlockBtn.click();
  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}.json`
  );
});

test('buttons in format quick bar should have correct active styles', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  // `45`
  await setInlineRangeInInlineEditor(
    page,
    {
      index: 0,
      length: 2,
    },
    2
  );
  const { codeBtn } = getFormatBar(page);
  await codeBtn.click();
  await expect(codeBtn).toHaveAttribute('active', '');

  // `456`
  await setInlineRangeInInlineEditor(
    page,
    {
      index: 0,
      length: 3,
    },
    2
  );
  await expect(codeBtn).not.toHaveAttribute('active', '');
});

test('should format bar style active correctly', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await page.evaluate(() => {
    const { doc } = window;
    const rootId = doc.addBlock('affine:page', {
      title: new doc.Text(),
    });
    const note = doc.addBlock('affine:note', {}, rootId);
    const delta = [
      { insert: '1', attributes: { bold: true, italic: true } },
      { insert: '2', attributes: { bold: true, underline: true } },
      { insert: '3', attributes: { bold: true, code: true } },
    ];
    const text = new doc.Text(delta as DeltaInsert[]);
    doc.addBlock('affine:paragraph', { text }, note);
  });

  const { boldBtn, codeBtn, underlineBtn } = getFormatBar(page);
  await dragBetweenIndices(page, [0, 0], [0, 3]);
  await expect(boldBtn).toHaveAttribute('active', '');
  await expect(underlineBtn).not.toHaveAttribute('active', '');
  await expect(codeBtn).not.toHaveAttribute('active', '');

  await underlineBtn.click();
  await expect(underlineBtn).toHaveAttribute('active', '');
  await expect(boldBtn).toHaveAttribute('active', '');
  await expect(codeBtn).not.toHaveAttribute('active', '');
});

test('should format quick bar show when double click button', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await dragBetweenIndices(page, [0, 0], [2, 3]);
  const { formatBar, boldBtn } = getFormatBar(page);
  await expect(formatBar).toBeVisible();
  await boldBtn.dblclick({
    delay: 100,
  });
  await expect(formatBar).toBeVisible();
});

test('should the database action icon show correctly', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  const databaseAction = page.getByTestId('convert-to-database');

  await focusRichText(page);
  await dragBetweenIndices(
    page,
    [2, 3],
    [0, 0],
    { x: 20, y: 20 },
    { x: 0, y: 0 }
  );
  await expect(databaseAction).toBeVisible();

  await focusRichText(page, 2);
  await pressEnter(page);
  await updateBlockType(page, 'affine:code');
  const codeBlock = page.locator('affine-code');
  const codeBox = await codeBlock.boundingBox();
  if (!codeBox) throw new Error('Missing code block box');

  await page.keyboard.type('hello world');
  const position = {
    startX: codeBox.x,
    startY: codeBox.y + codeBox.height / 2,
    endX: codeBox.x + codeBox.width,
    endY: codeBox.y + codeBox.height / 2,
  };
  await page.mouse.click(position.endX + 150, position.endY + 150);
  await dragBetweenCoords(
    page,
    { x: position.startX + 10, y: position.startY - 10 },
    { x: position.endX, y: position.endY },
    { steps: 20 }
  );
  await expect(databaseAction).not.toBeVisible();
});

test('should convert to database work', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  await dragBetweenIndices(
    page,
    [2, 3],
    [0, 0],
    { x: 20, y: 20 },
    { x: 0, y: 0 }
  );
  const databaseAction = page.getByTestId('convert-to-database');
  await databaseAction.click();
  const database = page.locator('affine-database');
  await expect(database).toBeVisible();
  const rows = page.locator('.affine-database-block-row');
  expect(await rows.count()).toBe(3);
});

test('should show format-quick-bar and select all text of the block when triple clicking on text', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await focusRichText(page);
  await type(page, 'hello world');

  const editorHost = getEditorHostLocator(page);
  const locator = editorHost.locator('.inline-editor').nth(0);
  const textBox = await locator.boundingBox();
  if (!textBox) {
    throw new Error("Can't get bounding box");
  }

  await page.mouse.dblclick(textBox.x + 10, textBox.y + textBox.height / 2);

  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  await assertRichTextInlineRange(page, 0, 0, 5);

  const noteEl = page.locator('affine-note');
  const { x, y, width, height } = await getBoundingBox(noteEl);
  await page.mouse.click(x + width / 2, y + height / 2);

  await expect(formatBar).toBeHidden();

  await page.mouse.move(textBox.x + 10, textBox.y + textBox.height / 2);

  const options = {
    clickCount: 1,
  };
  await page.mouse.down(options);
  await page.mouse.up(options);

  options.clickCount++;
  await page.mouse.down(options);
  await page.mouse.up(options);

  options.clickCount++;
  await page.mouse.down(options);
  await page.mouse.up(options);

  await assertRichTextInlineRange(page, 0, 0, 'hello world'.length);
});

test('should update the format quick bar state when there is a change in keyboard selection', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await page.evaluate(() => {
    const { doc } = window;
    const rootId = doc.addBlock('affine:page', {
      title: new doc.Text(),
    });
    const note = doc.addBlock('affine:note', {}, rootId);
    const delta = [
      { insert: '1', attributes: { bold: true } },
      { insert: '2', attributes: { bold: true } },
      { insert: '3', attributes: { bold: false } },
    ];
    const text = new doc.Text(delta as DeltaInsert[]);
    doc.addBlock('affine:paragraph', { text }, note);
  });
  await focusTitle(page);
  await pressArrowDown(page);

  const formatBar = getFormatBar(page);
  await withPressKey(page, 'Shift', async () => {
    await page.keyboard.press('ArrowRight');
    await page.keyboard.press('ArrowRight');
    await expect(formatBar.boldBtn).toHaveAttribute('active', '');
    await page.keyboard.press('ArrowRight');
    await expect(formatBar.boldBtn).not.toHaveAttribute('active', '');
  });
});

test('format quick bar should not break cursor jumping', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);
  await dragBetweenIndices(page, [1, 3], [1, 2]);

  const { formatBar } = getFormatBar(page);
  await expect(formatBar).toBeVisible();

  await pressArrowUp(page);
  await type(page, '0');
  await assertRichTexts(page, ['1203', '456', '789']);

  await dragBetweenIndices(page, [1, 3], [1, 2]);
  await pressArrowDown(page);
  await type(page, '0');
  await assertRichTexts(page, ['1203', '456', '7809']);
});

test('selecting image should not show format bar', async ({ page }) => {
  test.info().annotations.push({
    type: 'issue',
    description: 'https://github.com/toeverything/blocksuite/issues/4535',
  });
  await enterPlaygroundRoom(page);
  await initImageState(page);
  await assertRichImage(page, 1);
  await activeEmbed(page);
  await waitNextFrame(page);
  const { formatBar } = getFormatBar(page);
  await expect(formatBar).not.toBeVisible();
});

test('create linked doc from block selection with format bar', async ({
  page,
}, testInfo) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await initThreeParagraphs(page);

  await focusRichText(page, 1);
  await pressTab(page);
  await assertRichTexts(page, ['123', '456', '789']);
  await assertBlockChildrenIds(page, '1', ['2', '4']);
  await assertBlockChildrenIds(page, '2', ['3']);

  await selectAllBlocksByKeyboard(page);
  await waitNextFrame(page, 200);

  const blockSelections = page
    .locator('affine-block-selection')
    .locator('visible=true');
  await expect(blockSelections).toHaveCount(2);

  const { createLinkedDocBtn } = getFormatBar(page);
  expect(await createLinkedDocBtn.isVisible()).toBe(true);
  await createLinkedDocBtn.click();

  const linkedDocBlock = page.locator('affine-embed-linked-doc-block');
  await expect(linkedDocBlock).toHaveCount(1);

  const linkedDocBox = await linkedDocBlock.boundingBox();
  assertExists(linkedDocBox);
  await page.mouse.dblclick(
    linkedDocBox.x + linkedDocBox.width / 2,
    linkedDocBox.y + linkedDocBox.height / 2
  );
  await waitNextFrame(page, 200);

  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}.json`
  );
});

test.describe('more menu button', () => {
  test('should be able to perform the copy action', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyParagraphState(page);
    await initThreeParagraphs(page);
    // drag only the `456` paragraph
    await dragBetweenIndices(page, [1, 0], [1, 3]);

    const { openMoreMenu, copyBtn } = getFormatBar(page);
    await openMoreMenu();
    await expect(copyBtn).toBeVisible();
    await assertRichTextInlineRange(page, 1, 0, 3);
    await copyBtn.click();
    await assertRichTextInlineRange(page, 1, 0, 3);

    await focusRichText(page, 1);
    await pasteByKeyboard(page);
    await waitNextFrame(page);

    await assertRichTexts(page, ['123', '456456', '789']);
  });

  test('should be able to perform the duplicate action', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyParagraphState(page);
    await initThreeParagraphs(page);

    await focusRichText(page, 1);
    await pressEscape(page);

    const { openMoreMenu, duplicateBtn } = getFormatBar(page);
    await openMoreMenu();
    await expect(duplicateBtn).toBeVisible();
    await duplicateBtn.click();

    await waitNextFrame(page);

    await assertRichTexts(page, ['123', '456', '456', '789']);
  });

  test('should be able to perform the delete action', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyParagraphState(page);
    await initThreeParagraphs(page);

    await focusRichText(page, 1);
    await pressEscape(page);

    const { openMoreMenu, deleteBtn } = getFormatBar(page);
    await openMoreMenu();
    await expect(deleteBtn).toBeVisible();
    await deleteBtn.click();

    await waitNextFrame(page);

    await assertRichTexts(page, ['123', '789']);
  });
});
