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

import {
  addBasicRectShapeElement,
  assertEdgelessShapeType,
  createShapeElement,
  edgelessCommonSetup,
  getEdgelessSelectedRect,
  getZoomLevel,
  locatorEdgelessToolButton,
  setEdgelessTool,
  type ShapeName,
  switchEditorMode,
  zoomFitByKeyboard,
  zoomInByKeyboard,
  zoomOutByKeyboard,
  zoomResetByKeyboard,
} from '../utils/actions/edgeless.js';
import {
  clickView,
  enterPlaygroundRoom,
  focusRichText,
  initEmptyEdgelessState,
  pressBackspace,
  pressEscape,
  pressForwardDelete,
  selectAllByKeyboard,
  selectNoteInEdgeless,
  type,
  waitNextFrame,
} from '../utils/actions/index.js';
import {
  assertBlockCount,
  assertEdgelessNonSelectedRect,
  assertEdgelessSelectedModelRect,
  assertEdgelessSelectedRect,
} from '../utils/asserts.js';
import { test } from '../utils/playwright.js';

test('shortcut', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyEdgelessState(page);
  await switchEditorMode(page);
  await page.mouse.click(100, 100);

  // text is removed temporarily
  // await page.keyboard.press('t');
  // const textButton = await locatorEdgelessToolButton(page, 'text');
  // await expect(textButton).toHaveAttribute('active', '');

  await page.keyboard.press('s');
  const shapeButton = await locatorEdgelessToolButton(page, 'shape');
  await expect(shapeButton).toHaveAttribute('active', '');

  await page.keyboard.press('p');
  const penButton = await locatorEdgelessToolButton(page, 'brush');
  await expect(penButton).toHaveAttribute('active', '');

  await page.keyboard.press('h');
  const panButton = await locatorEdgelessToolButton(page, 'pan');
  await expect(panButton).toHaveAttribute('active', '');

  await page.keyboard.press('c');
  const connectorButton = await locatorEdgelessToolButton(page, 'connector');
  await expect(connectorButton).toHaveAttribute('active', '');

  // await page.keyboard.press('l');
  // const lassoButton = await locatorEdgelessToolButton(page, 'lasso');
  // await expect(lassoButton).toHaveAttribute('active', '');
});

test.skip('toggle lasso tool modes', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyEdgelessState(page);
  await switchEditorMode(page);
  await page.mouse.click(100, 100);

  const lassoButton = await locatorEdgelessToolButton(page, 'lasso', false);

  const isLassoMode = async (type: 'freehand' | 'polygonal') => {
    const classes = (await lassoButton.getAttribute('class'))?.split(' ') ?? [];
    return classes.includes(type);
  };

  await page.keyboard.press('Shift+l');
  expect(await isLassoMode('freehand')).toBe(true);

  await page.keyboard.press('Shift+l');
  expect(await isLassoMode('polygonal')).toBe(true);

  await page.keyboard.press('Shift+l');
  expect(await isLassoMode('freehand')).toBe(true);
});

test('toggle shapes shortcut', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyEdgelessState(page);
  await switchEditorMode(page);
  await page.mouse.click(100, 100);
  await setEdgelessTool(page, 'shape');

  const shapesInOrder = [
    'ellipse',
    'diamond',
    'triangle',
    'roundedRect',
    'rect',
    'ellipse',
    'diamond',
    'triangle',
    'roundedRect',
  ] as ShapeName[];
  for (const shape of shapesInOrder) {
    await page.keyboard.press('Shift+s');
    await assertEdgelessShapeType(page, shape);
  }
});

test('should not switch shapes in editing', async ({ page }) => {
  await enterPlaygroundRoom(page);
  await initEmptyEdgelessState(page);
  await switchEditorMode(page);

  await setEdgelessTool(page, 'shape');
  await waitNextFrame(page);
  await assertEdgelessShapeType(page, 'rect');

  await page.mouse.click(200, 150);
  await waitNextFrame(page);
  await page.mouse.dblclick(250, 200);
  await waitNextFrame(page);

  await type(page, 'hello');
  await page.keyboard.press('Shift+s');
  await page.keyboard.press('Escape');
  await waitNextFrame(page);
  await setEdgelessTool(page, 'shape');
  await assertEdgelessShapeType(page, 'rect');

  await setEdgelessTool(page, 'default');
  await page.mouse.dblclick(250, 200);
  await waitNextFrame(page);
  await page.keyboard.press('Shift+S');
  await page.keyboard.press('Escape');
  await waitNextFrame(page);
  await setEdgelessTool(page, 'shape');
  await assertEdgelessShapeType(page, 'rect');
});

test('pressing the ESC key will return to the default state', async ({
  page,
}) => {
  await enterPlaygroundRoom(page);
  await initEmptyEdgelessState(page);
  await switchEditorMode(page);

  const start = { x: 100, y: 100 };
  const end = { x: 200, y: 200 };
  await addBasicRectShapeElement(page, start, end);

  await page.mouse.click(start.x + 5, start.y + 5);
  await assertEdgelessSelectedRect(page, [100, 100, 100, 100]);

  await pressEscape(page);
  await assertEdgelessNonSelectedRect(page);
});

test.describe('zooming', () => {
  test('zoom fit to screen', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyEdgelessState(page);
    await switchEditorMode(page);
    await zoomResetByKeyboard(page);

    const start = { x: 0, y: 0 };
    const end = { x: 900, y: 200 };
    await addBasicRectShapeElement(page, start, end);
    await zoomFitByKeyboard(page);

    const zoom = await getZoomLevel(page);
    expect(zoom).not.toBe(100);
  });
  test('zoom out', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyEdgelessState(page);
    await switchEditorMode(page);

    await clickView(page, [0, 0]);
    await zoomResetByKeyboard(page);

    await zoomOutByKeyboard(page);

    let zoom = await getZoomLevel(page);
    expect(zoom).toBe(75);

    await zoomOutByKeyboard(page);

    zoom = await getZoomLevel(page);
    expect(zoom).toBe(50);
  });
  test('zoom reset', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyEdgelessState(page);
    await switchEditorMode(page);

    await clickView(page, [0, 0]);
    await zoomResetByKeyboard(page);
    let zoom = await getZoomLevel(page);
    expect(zoom).toBe(100);

    await zoomOutByKeyboard(page);

    zoom = await getZoomLevel(page);
    expect(zoom).toBe(75);

    await zoomResetByKeyboard(page);

    zoom = await getZoomLevel(page);
    expect(zoom).toBe(100);
  });
  test('zoom in', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyEdgelessState(page);
    await switchEditorMode(page);

    await clickView(page, [0, 0]);
    await zoomResetByKeyboard(page);

    await zoomInByKeyboard(page);

    let zoom = await getZoomLevel(page);
    expect(zoom).toBe(125);

    await zoomInByKeyboard(page);

    zoom = await getZoomLevel(page);
    expect(zoom).toBe(150);
  });
});

test('cmd + A should select all elements by default', async ({ page }) => {
  await edgelessCommonSetup(page);
  await createShapeElement(page, [0, 0], [100, 100]);
  await createShapeElement(page, [100, 0], [200, 100]);
  await selectAllByKeyboard(page);
  await assertEdgelessSelectedModelRect(page, [0, 0, 200, 100]);
});

test('cmd + A should not fire inside active note', async ({ page }) => {
  await enterPlaygroundRoom(page);
  const { noteId } = await initEmptyEdgelessState(page);
  await focusRichText(page);
  await type(page, 'hello');
  await switchEditorMode(page);

  await selectNoteInEdgeless(page, noteId);
  // second click become active
  await selectNoteInEdgeless(page, noteId);
  await selectAllByKeyboard(page);

  // should not have selected rect
  let error = null;
  try {
    await getEdgelessSelectedRect(page);
  } catch (e) {
    error = e;
  }
  expect(error).not.toBeNull();
});

test.describe('delete', () => {
  test('do not delete element when active', async ({ page }) => {
    await enterPlaygroundRoom(page);
    const { noteId } = await initEmptyEdgelessState(page);
    await focusRichText(page);
    await type(page, 'hello');
    await switchEditorMode(page);
    await selectNoteInEdgeless(page, noteId);
    const box1 = await getEdgelessSelectedRect(page);
    await page.mouse.click(box1.x + 10, box1.y + 10);
    await pressBackspace(page);
    await assertBlockCount(page, 'edgeless-note', 1);
    await pressForwardDelete(page);
    await assertBlockCount(page, 'edgeless-note', 1);
  });
});

test.describe('Arrow Keys should move selection', () => {
  test('with shift increment by 10px', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyEdgelessState(page);
    await switchEditorMode(page);

    await addBasicRectShapeElement(
      page,
      { x: 100, y: 100 },
      { x: 200, y: 200 }
    );

    await page.keyboard.down('Shift');

    for (let i = 0; i < 10; i++) await page.keyboard.press('ArrowLeft');
    for (let i = 0; i < 10; i++) await page.keyboard.press('ArrowDown');

    await assertEdgelessSelectedRect(page, [0, 200, 100, 100]);
  });

  test('without shift increment by 1px', async ({ page }) => {
    await enterPlaygroundRoom(page);
    await initEmptyEdgelessState(page);
    await switchEditorMode(page);

    await addBasicRectShapeElement(
      page,
      { x: 100, y: 100 },
      { x: 200, y: 200 }
    );

    for (let i = 0; i < 10; i++) await page.keyboard.press('ArrowRight');
    for (let i = 0; i < 10; i++) await page.keyboard.press('ArrowUp');

    await assertEdgelessSelectedRect(page, [110, 90, 100, 100]);
  });
});
