import type { Page } from '@playwright/test';
import type { BlockSnapshot } from '@store/index.js';

import { expect } from '@playwright/test';
import { ignoreSnapshotId } from 'utils/ignore.js';
import { getEmbedCardToolbar } from 'utils/query.js';

import {
  activeNoteInEdgeless,
  copyByKeyboard,
  dragBlockToPoint,
  enterPlaygroundRoom,
  expectConsoleMessage,
  focusRichText,
  getPageSnapshot,
  initEmptyEdgelessState,
  initEmptyParagraphState,
  pasteByKeyboard,
  pressArrowDown,
  pressArrowRight,
  pressArrowUp,
  pressBackspace,
  pressEnter,
  pressShiftTab,
  pressTab,
  selectAllByKeyboard,
  setInlineRangeInSelectedRichText,
  SHORT_KEY,
  switchEditorMode,
  type,
  waitForInlineEditorStateUpdated,
  waitNextFrame,
} from './utils/actions/index.js';
import {
  assertAlmostEqual,
  assertBlockChildrenIds,
  assertBlockCount,
  assertBlockFlavour,
  assertBlockSelections,
  assertExists,
  assertParentBlockFlavour,
  assertRichTextInlineRange,
} from './utils/asserts.js';
import './utils/declare-test-window.js';
import { scoped, test } from './utils/playwright.js';

const LOCAL_HOST_URL = 'http://localhost';

const YOUTUBE_URL = 'https://www.youtube.com/watch?v=fakeid';

const FIGMA_URL = 'https://www.figma.com/design/JuXs6uOAICwf4I4tps0xKZ123';

test.beforeEach(async ({ page }) => {
  await page.route(
    'https://affine-worker.toeverything.workers.dev/api/worker/link-preview',
    async route => {
      await route.fulfill({
        json: {},
      });
    }
  );
});

const createBookmarkBlockBySlashMenu = async (
  page: Page,
  url = LOCAL_HOST_URL
) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await focusRichText(page);
  await page.waitForTimeout(100);
  await type(page, '/link', 100);
  await pressEnter(page);
  await page.waitForTimeout(100);
  await type(page, url);
  await pressEnter(page);
};

test(scoped`create bookmark by slash menu`, async ({ page }, testInfo) => {
  await createBookmarkBlockBySlashMenu(page);
  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_final.json`
  );
});

test(scoped`covert bookmark block to link text`, async ({ page }, testInfo) => {
  await createBookmarkBlockBySlashMenu(page);
  const bookmark = page.locator('affine-bookmark');
  await bookmark.click();
  await page.waitForTimeout(100);
  await page.getByRole('button', { name: 'Switch view' }).click();
  await page.getByRole('button', { name: 'Inline view' }).click();
  expect(await getPageSnapshot(page, true)).toMatchSnapshot(
    `${testInfo.title}_final.json`
  );
});

test(
  scoped`copy url to create bookmark in page mode`,
  async ({ page }, testInfo) => {
    await enterPlaygroundRoom(page);
    await initEmptyParagraphState(page);
    await focusRichText(page);

    await type(page, LOCAL_HOST_URL);
    await setInlineRangeInSelectedRichText(page, 0, LOCAL_HOST_URL.length);
    await copyByKeyboard(page);
    await focusRichText(page);
    await type(page, '/link');
    await pressEnter(page);
    await page.keyboard.press(`${SHORT_KEY}+v`);
    await pressEnter(page);
    expect(await getPageSnapshot(page, true)).toMatchSnapshot(
      `${testInfo.title}_final.json`
    );
  }
);

test(
  scoped`copy url to create bookmark in edgeless mode`,
  async ({ page }, testInfo) => {
    await enterPlaygroundRoom(page);
    const ids = await initEmptyEdgelessState(page);
    await focusRichText(page);
    await type(page, LOCAL_HOST_URL);

    await switchEditorMode(page);

    await activeNoteInEdgeless(page, ids.noteId);
    await waitForInlineEditorStateUpdated(page);
    await selectAllByKeyboard(page);
    await copyByKeyboard(page);
    await pressArrowRight(page);
    await waitNextFrame(page);
    await type(page, '/link', 100);
    await pressEnter(page);
    await page.waitForTimeout(100);
    await waitNextFrame(page);
    await page.keyboard.press(`${SHORT_KEY}+v`);
    await pressEnter(page);
    expect(await getPageSnapshot(page, true)).toMatchSnapshot(
      `${testInfo.title}_final.json`
    );
  }
);

test.fixme(
  scoped`support dragging bookmark block directly`,
  async ({ page }, testInfo) => {
    await createBookmarkBlockBySlashMenu(page);
    expect(await getPageSnapshot(page, true)).toMatchSnapshot(
      `${testInfo.title}_init.json`
    );

    const bookmark = page.locator('affine-bookmark');
    const rect = await bookmark.boundingBox();
    if (!rect) {
      throw new Error('image not found');
    }

    // add new paragraph blocks
    await page.mouse.click(rect.x + 20, rect.y + rect.height + 20);
    await focusRichText(page);
    await type(page, '111');
    await page.waitForTimeout(200);
    await pressEnter(page);

    await type(page, '222');
    await page.waitForTimeout(200);
    await pressEnter(page);

    await type(page, '333');
    await page.waitForTimeout(200);

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

    // drag bookmark block
    await page.mouse.move(rect.x + 20, rect.y + 20);
    await page.mouse.down();
    await page.waitForTimeout(200);

    await page.mouse.move(rect.x + 40, rect.y + rect.height + 80, {
      steps: 5,
    });
    await page.waitForTimeout(200);

    await page.mouse.up();
    await page.waitForTimeout(200);

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

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

test('press backspace after bookmark block can select bookmark block', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);
  await focusRichText(page);

  await pressEnter(page);
  await pressArrowUp(page);
  await type(page, '/link');
  await pressEnter(page);
  await page.waitForTimeout(100);
  await type(page, LOCAL_HOST_URL);
  await pressEnter(page);

  await focusRichText(page);
  await assertBlockCount(page, 'paragraph', 1);
  await assertRichTextInlineRange(page, 0, 0);
  await pressBackspace(page);
  await assertBlockSelections(page, ['4']);
  await assertBlockCount(page, 'paragraph', 0);
});

test.describe('embed card toolbar', () => {
  async function showEmbedCardToolbar(page: Page) {
    await createBookmarkBlockBySlashMenu(page);
    const bookmark = page.locator('affine-bookmark');
    await bookmark.click();
    await page.waitForTimeout(100);
    const { embedCardToolbar } = getEmbedCardToolbar(page);
    await expect(embedCardToolbar).toBeVisible();
  }

  test('show toolbar when bookmark selected', async ({ page }) => {
    await showEmbedCardToolbar(page);
  });

  test('copy bookmark url by copy button', async ({ page }, testInfo) => {
    await showEmbedCardToolbar(page);
    const { copyButton } = getEmbedCardToolbar(page);
    await copyButton.click();
    await page.mouse.click(600, 600);
    await waitNextFrame(page);

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

  test('change card style', async ({ page }) => {
    await showEmbedCardToolbar(page);
    const bookmark = page.locator('affine-bookmark');
    const { openCardStyleMenu } = getEmbedCardToolbar(page);
    await openCardStyleMenu();
    const { cardStyleHorizontalButton, cardStyleListButton } =
      getEmbedCardToolbar(page);
    await cardStyleListButton.click();
    await waitNextFrame(page);
    const listStyleBookmarkBox = await bookmark.boundingBox();
    assertExists(listStyleBookmarkBox);
    assertAlmostEqual(listStyleBookmarkBox.width, 752, 2);
    assertAlmostEqual(listStyleBookmarkBox.height, 46, 2);

    await openCardStyleMenu();
    await cardStyleHorizontalButton.click();
    await waitNextFrame(page);
    const horizontalStyleBookmarkBox = await bookmark.boundingBox();
    assertExists(horizontalStyleBookmarkBox);
    assertAlmostEqual(horizontalStyleBookmarkBox.width, 752, 2);
    assertAlmostEqual(horizontalStyleBookmarkBox.height, 116, 2);
  });
});

test('indent bookmark block to paragraph', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);

  await focusRichText(page);
  await pressEnter(page);
  await type(page, '/link', 100);
  await pressEnter(page);
  await type(page, LOCAL_HOST_URL);
  await pressEnter(page);

  await assertBlockChildrenIds(page, '1', ['2', '4']);
  await assertBlockFlavour(page, '1', 'affine:note');
  await assertBlockFlavour(page, '2', 'affine:paragraph');
  await assertBlockFlavour(page, '4', 'affine:bookmark');

  await focusRichText(page);
  await pressArrowDown(page);
  await assertBlockSelections(page, ['4']);
  await pressTab(page);
  await assertBlockChildrenIds(page, '1', ['2']);
  await assertBlockChildrenIds(page, '2', ['4']);

  await pressShiftTab(page);
  await assertBlockChildrenIds(page, '1', ['2', '4']);
});

test('indent bookmark block to list', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyParagraphState(page);

  await focusRichText(page);
  await type(page, '- a');
  await pressEnter(page);
  await type(page, '/link', 100);
  await pressEnter(page);
  await type(page, LOCAL_HOST_URL);
  await pressEnter(page);

  await assertBlockChildrenIds(page, '1', ['3', '5']);
  await assertBlockFlavour(page, '1', 'affine:note');
  await assertBlockFlavour(page, '3', 'affine:list');
  await assertBlockFlavour(page, '5', 'affine:bookmark');

  await focusRichText(page);
  await pressArrowDown(page);
  await assertBlockSelections(page, ['5']);
  await pressTab(page);
  await assertBlockChildrenIds(page, '1', ['3']);
  await assertBlockChildrenIds(page, '3', ['5']);

  await pressShiftTab(page);
  await assertBlockChildrenIds(page, '1', ['3', '5']);
});

test('bookmark can be dragged from note to surface top level block', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyEdgelessState(page);
  await focusRichText(page);
  await page.waitForTimeout(100);
  await type(page, '/link', 100);
  await pressEnter(page);
  await page.waitForTimeout(100);
  await type(page, LOCAL_HOST_URL);
  await pressEnter(page);

  await switchEditorMode(page);
  await page.mouse.dblclick(450, 450);

  await dragBlockToPoint(page, '4', { x: 200, y: 200 });

  await waitNextFrame(page);
  await assertParentBlockFlavour(page, '4', 'affine:surface');
});

test.describe('embed youtube card', () => {
  test(scoped`create youtube card by slash menu`, async ({ page }) => {
    expectConsoleMessage(page, /Unrecognized feature/, 'warning');
    expectConsoleMessage(page, /Failed to load resource/);
    await createBookmarkBlockBySlashMenu(page, YOUTUBE_URL);
    const snapshot = (await getPageSnapshot(page)) as BlockSnapshot;
    expect(ignoreSnapshotId(snapshot)).toMatchSnapshot('embed-youtube.json');
  });

  test(scoped`change youtube card style`, async ({ page }) => {
    expectConsoleMessage(page, /Unrecognized feature/, 'warning');
    expectConsoleMessage(page, /Failed to load resource/);

    await createBookmarkBlockBySlashMenu(page, YOUTUBE_URL);
    const youtube = page.locator('affine-embed-youtube-block');
    await youtube.click();
    await page.waitForTimeout(100);

    // change to card view
    const embedToolbar = page.locator('affine-embed-card-toolbar');
    await expect(embedToolbar).toBeVisible();
    const embedView = page.locator('editor-menu-button', {
      hasText: 'embed view',
    });
    await expect(embedView).toBeVisible();
    await embedView.click();
    const cardView = page.locator('editor-menu-action', {
      hasText: 'card view',
    });
    await expect(cardView).toBeVisible();
    await cardView.click();
    const snapshot = (await getPageSnapshot(page)) as BlockSnapshot;
    expect(ignoreSnapshotId(snapshot)).toMatchSnapshot(
      'horizontal-youtube.json'
    );

    // change to embed view
    const bookmark = page.locator('affine-bookmark');
    await bookmark.click();
    await page.waitForTimeout(100);
    const cardView2 = page.locator('editor-icon-button', {
      hasText: 'card view',
    });
    await expect(cardView2).toBeVisible();
    await cardView2.click();
    const embedView2 = page.locator('editor-menu-action', {
      hasText: 'embed view',
    });
    await expect(embedView2).toBeVisible();
    await embedView2.click();
    const snapshot2 = (await getPageSnapshot(page)) as BlockSnapshot;
    expect(ignoreSnapshotId(snapshot2)).toMatchSnapshot('embed-youtube.json');
  });
});

test.describe('embed figma card', () => {
  test(scoped`create figma card by slash menu`, async ({ page }) => {
    expectConsoleMessage(page, /Failed to load resource/);
    expectConsoleMessage(page, /Refused to frame/);
    await createBookmarkBlockBySlashMenu(page, FIGMA_URL);
    const snapshot = (await getPageSnapshot(page)) as BlockSnapshot;
    expect(ignoreSnapshotId(snapshot)).toMatchSnapshot('embed-figma.json');
  });

  test(scoped`change figma card style`, async ({ page }) => {
    expectConsoleMessage(page, /Failed to load resource/);
    expectConsoleMessage(page, /Refused to frame/);
    expectConsoleMessage(page, /Running frontend commit/, 'log');
    await createBookmarkBlockBySlashMenu(page, FIGMA_URL);
    const youtube = page.locator('affine-embed-figma-block');
    await youtube.click();
    await page.waitForTimeout(100);

    // change to card view
    const embedToolbar = page.locator('affine-embed-card-toolbar');
    await expect(embedToolbar).toBeVisible();
    const embedView = page.locator('editor-menu-button', {
      hasText: 'embed view',
    });
    await expect(embedView).toBeVisible();
    await embedView.click();
    const cardView = page.locator('editor-menu-action', {
      hasText: 'card view',
    });
    await expect(cardView).toBeVisible();
    await cardView.click();
    const snapshot = (await getPageSnapshot(page)) as BlockSnapshot;
    expect(ignoreSnapshotId(snapshot)).toMatchSnapshot('horizontal-figma.json');

    // change to embed view
    const bookmark = page.locator('affine-bookmark');
    await bookmark.click();
    await page.waitForTimeout(100);
    const cardView2 = page.locator('editor-icon-button', {
      hasText: 'card view',
    });
    await expect(cardView2).toBeVisible();
    await cardView2.click();
    const embedView2 = page.locator('editor-menu-action', {
      hasText: 'embed view',
    });
    await expect(embedView2).toBeVisible();
    await embedView2.click();
    const snapshot2 = (await getPageSnapshot(page)) as BlockSnapshot;
    expect(ignoreSnapshotId(snapshot2)).toMatchSnapshot('embed-figma.json');
  });
});
