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

import {
  addBasicConnectorElement,
  changeConnectorStrokeColor,
  changeConnectorStrokeStyle,
  changeConnectorStrokeWidth,
  edgelessCommonSetup as commonSetup,
  createConnectorElement,
  createShapeElement,
  dragBetweenViewCoords,
  pickColorAtPoints,
  rotateElementByHandle,
  Shape,
  toModelCoord,
  toViewCoord,
  triggerComponentToolbarAction,
} from '../../utils/actions/edgeless.js';
import { pressBackspace, waitNextFrame } from '../../utils/actions/index.js';
import {
  assertConnectorPath,
  assertEdgelessNonSelectedRect,
  assertEdgelessSelectedRect,
  assertExists,
} from '../../utils/asserts.js';
import { test } from '../../utils/playwright.js';

test('path #1, the upper line is parallel with the lower line of antoher, and anchor from top to bottom of another', async ({
  page,
}) => {
  await commonSetup(page);

  await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
  await createShapeElement(page, [200, -100], [300, 0], Shape.Square);
  await createConnectorElement(page, [50, 0], [250, 0]);

  await waitNextFrame(page);
  await assertConnectorPath(page, [
    [50, 0],
    [50, -20],
    [150, -20],
    [150, 20],
    [250, 20],
    [250, 0],
  ]);
});

test('path #2, the top-right point is overlapped with the bottom-left point of another, and anchor from top to bottom of another', async ({
  page,
}) => {
  await commonSetup(page);
  await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
  await createShapeElement(page, [100, -100], [200, 0], Shape.Square);
  await createConnectorElement(page, [50, 0], [150, 0]);

  await assertConnectorPath(page, [
    [50, 0],
    [50, -120],
    [220, -120],
    [220, 20],
    [150, 20],
    [150, 0],
  ]);
});

test('path #3, the two shape are parallel in x axis, the anchor from the right to right', async ({
  page,
}) => {
  await commonSetup(page);

  await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
  await createShapeElement(page, [200, 0], [300, 100], Shape.Square);
  await createConnectorElement(page, [100, 50], [300, 50]);
  await assertConnectorPath(page, [
    [100, 50],
    [150, 50],
    [150, 120],
    [320, 120],
    [320, 50],
    [300, 50],
  ]);
});

test('when element is removed, connector should be deleted too', async ({
  page,
}) => {
  await commonSetup(page);
  await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
  await createConnectorElement(page, [100, 50], [200, 0]);

  //select
  await dragBetweenViewCoords(page, [10, -10], [20, 20]);
  await pressBackspace(page);
  await dragBetweenViewCoords(page, [100, 50], [0, 50]);
  await assertEdgelessNonSelectedRect(page);
});

test('connector connects triangle shape', async ({ page }) => {
  await commonSetup(page);
  await createShapeElement(page, [0, 0], [100, 100], Shape.Triangle);
  await createConnectorElement(page, [75, 50], [100, 50]);

  await assertConnectorPath(page, [
    [75, 50],
    [100, 50],
  ]);
});

test('connector connects diamond shape', async ({ page }) => {
  await commonSetup(page);
  await createShapeElement(page, [0, 0], [100, 100], Shape.Diamond);
  await createConnectorElement(page, [100, 50], [200, 50]);

  await assertConnectorPath(page, [
    [100, 50],
    [200, 50],
  ]);
});

test('connector connects rotated Square shape', async ({ page }) => {
  await commonSetup(page);
  await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
  await createConnectorElement(page, [50, 0], [50, -100]);
  await dragBetweenViewCoords(page, [-10, 50], [60, 60]);
  await rotateElementByHandle(page, 30, 'top-left');
  await assertConnectorPath(page, [
    [75, 6.7],
    [75, -46.65],
    [50, -46.65],
    [50, -100],
  ]);
  await rotateElementByHandle(page, 30, 'top-left');
  await assertConnectorPath(page, [
    [93.3, 25],
    [138.3, 25],
    [138.3, -38.3],
    [50, -38.3],
    [50, -100],
  ]);
});

test('change connector line width', async ({ page }) => {
  await commonSetup(page);

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

  await page.mouse.click(start.x + 5, start.y);
  await triggerComponentToolbarAction(page, 'changeConnectorStrokeColor');
  await changeConnectorStrokeColor(page, '--affine-palette-line-teal');

  await triggerComponentToolbarAction(page, 'changeConnectorStrokeStyles');
  await changeConnectorStrokeWidth(page, 5);

  await waitNextFrame(page);

  await triggerComponentToolbarAction(page, 'changeConnectorStrokeStyles');

  const pickedColor = await pickColorAtPoints(page, [
    [start.x + 5, start.y],
    [start.x + 10, start.y],
  ]);
  expect(pickedColor[0]).toBe(pickedColor[1]);
});

test('change connector stroke style', async ({ page }) => {
  await commonSetup(page);

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

  await page.mouse.click(start.x + 5, start.y);
  await triggerComponentToolbarAction(page, 'changeConnectorStrokeColor');
  await changeConnectorStrokeColor(page, '--affine-palette-line-teal');

  await triggerComponentToolbarAction(page, 'changeConnectorStrokeStyles');
  await changeConnectorStrokeStyle(page, 'dash');
  await waitNextFrame(page);

  await triggerComponentToolbarAction(page, 'changeConnectorStrokeStyles');

  const pickedColor = await pickColorAtPoints(page, [[start.x + 20, start.y]]);
  expect(pickedColor[0]).toBe('#000000');
});

test.describe('quick connect', () => {
  test('should create a connector when clicking on button', async ({
    page,
  }) => {
    await commonSetup(page);

    await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
    const [x, y] = await toViewCoord(page, [50, 50]);
    await page.mouse.click(x, y);

    const quickConnectBtn = page.getByRole('button', {
      name: 'Draw connector',
    });

    await expect(quickConnectBtn).toBeVisible();
    await quickConnectBtn.click();
    await expect(quickConnectBtn).toBeHidden();

    await assertConnectorPath(page, [
      [100, 50],
      [x, y],
    ]);
  });

  test('should be uncreated if the target is not found after clicking', async ({
    page,
  }) => {
    await commonSetup(page);

    await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
    const [x, y] = await toViewCoord(page, [50, 50]);
    await page.mouse.click(x, y);

    const quickConnectBtn = page.getByRole('button', {
      name: 'Draw connector',
    });

    const bounds = await quickConnectBtn.boundingBox();
    assertExists(bounds);

    await quickConnectBtn.click();

    await page.mouse.click(bounds.x, bounds.y);
    await assertEdgelessSelectedRect(page, [x - 50, y - 50, 100, 100]);
  });

  test('should be uncreated if the target is not found after pressing ESC', async ({
    page,
  }) => {
    await commonSetup(page);

    await createShapeElement(page, [0, 0], [100, 100], Shape.Square);

    // select shape
    const [x, y] = await toViewCoord(page, [50, 50]);
    await page.mouse.click(x, y);

    // click button
    await triggerComponentToolbarAction(page, 'quickConnect');

    await page.keyboard.press('Escape');

    await assertEdgelessNonSelectedRect(page);
  });

  test('should be connected if the target is found', async ({ page }) => {
    await commonSetup(page);

    await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
    await createShapeElement(page, [200, 0], [300, 100], Shape.Square);

    // select shape
    const [x, y] = await toViewCoord(page, [50, 50]);
    await page.mouse.click(x, y);

    // click button
    await triggerComponentToolbarAction(page, 'quickConnect');

    // click target
    const [tx, ty] = await toViewCoord(page, [200, 50]);
    await page.mouse.click(tx, ty);

    await assertConnectorPath(page, [
      [100, 50],
      [200, 50],
    ]);
  });

  test('should follow the mouse to automatically select the starting point', async ({
    page,
  }) => {
    await commonSetup(page);

    await createShapeElement(page, [0, 0], [100, 100], Shape.Square);
    const shapeBounds = await toViewCoord(page, [0, 0]);

    // select shape
    const [x, y] = await toViewCoord(page, [50, 50]);
    await page.mouse.click(x, y);

    // click button
    const quickConnectBtn = page.getByRole('button', {
      name: 'Draw connector',
    });
    const bounds = await quickConnectBtn.boundingBox();
    assertExists(bounds);
    await quickConnectBtn.click();

    // at right
    let point: [number, number] = [bounds.x, bounds.y];
    let endpoint = await toModelCoord(page, point);
    await assertConnectorPath(page, [[100, 50], endpoint]);

    // at top
    point = [shapeBounds[0] + 50, shapeBounds[1] - 50];
    endpoint = await toModelCoord(page, point);
    await page.mouse.move(...point);
    await waitNextFrame(page);
    await assertConnectorPath(page, [[50, 0], endpoint]);

    // at left
    point = [shapeBounds[0] - 50, shapeBounds[1] + 50];
    endpoint = await toModelCoord(page, point);
    await page.mouse.move(...point);
    await assertConnectorPath(page, [[0, 50], endpoint]);

    // at bottom
    point = [shapeBounds[0] + 50, shapeBounds[1] + 100 + 50];
    endpoint = await toModelCoord(page, point);
    await page.mouse.move(...point);
    await assertConnectorPath(page, [[50, 100], endpoint]);
  });
});
