import {
	MANUAL_TRIGGER_NODE_NAME,
	MANUAL_TRIGGER_NODE_DISPLAY_NAME,
	SWITCH_NODE_NAME,
	EDIT_FIELDS_SET_NODE_NAME,
	MERGE_NODE_NAME,
	CODE_NODE_NAME,
	SCHEDULE_TRIGGER_NODE_NAME,
	CODE_NODE_DISPLAY_NAME,
} from '../../../../../config/constants';
import { test, expect } from '../../../../../fixtures/base';

test.describe('Canvas Node Manipulation and Navigation', () => {
	test.beforeEach(async ({ n8n }) => {
		await n8n.start.fromBlankCanvas();
	});

	test('should add switch node and test connections', async ({ n8n }) => {
		const desiredOutputs = 4;
		await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
		await n8n.canvas.addNode(SWITCH_NODE_NAME);

		for (let i = 0; i < desiredOutputs; i++) {
			await n8n.page.getByText('Add Routing Rule').click();
		}

		await n8n.ndv.close();

		for (let i = 0; i < desiredOutputs; i++) {
			await n8n.canvas.clickNodePlusEndpoint(SWITCH_NODE_NAME);
			await expect(n8n.canvas.nodeCreatorSearchBar()).toBeVisible();
			await n8n.canvas.fillNodeCreatorSearchBar(EDIT_FIELDS_SET_NODE_NAME);
			await n8n.canvas.clickNodeCreatorItemName(EDIT_FIELDS_SET_NODE_NAME);
			await n8n.page.keyboard.press('Escape');
			await n8n.canvas.clickZoomToFitButton();
		}

		await n8n.canvas.canvasPane().click({ position: { x: 10, y: 10 } });

		await n8n.canvas.clickNodePlusEndpoint('Edit Fields3');
		await n8n.canvas.fillNodeCreatorSearchBar(SWITCH_NODE_NAME);
		await n8n.canvas.clickNodeCreatorItemName(SWITCH_NODE_NAME);
		await n8n.page.keyboard.press('Escape');

		await n8n.canvasComposer.saveWorkflowAndWaitForUrl();
		await expect(n8n.canvas.getWorkflowSaveButton()).toContainText('Saved');

		await n8n.canvasComposer.reloadAndWaitForCanvas();

		await expect(
			n8n.canvas.connectionBetweenNodes('Edit Fields3', `${SWITCH_NODE_NAME}1`),
		).toBeAttached();

		const editFieldsNodes = ['Edit Fields', 'Edit Fields1', 'Edit Fields2', 'Edit Fields3'];
		for (const nodeName of editFieldsNodes) {
			await expect(n8n.canvas.connectionBetweenNodes(SWITCH_NODE_NAME, nodeName)).toBeAttached();
		}
	});

	test('should add merge node and test connections', async ({ n8n }) => {
		const editFieldsNodeCount = 2;

		const checkConnections = async () => {
			await expect(
				n8n.canvas.connectionBetweenNodes(MANUAL_TRIGGER_NODE_DISPLAY_NAME, 'Edit Fields1').first(),
			).toBeAttached();
			await expect(
				n8n.canvas.connectionBetweenNodes('Edit Fields', MERGE_NODE_NAME).first(),
			).toBeAttached();
			await expect(
				n8n.canvas.connectionBetweenNodes('Edit Fields1', MERGE_NODE_NAME).first(),
			).toBeAttached();
		};

		await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
		await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();

		for (let i = 0; i < editFieldsNodeCount; i++) {
			await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });
			await n8n.canvas.canvasPane().click({
				position: { x: (i + 1) * 200, y: (i + 1) * 200 },
				// eslint-disable-next-line playwright/no-force-option
				force: true,
			});
		}
		await n8n.canvas.clickZoomToFitButton();

		await n8n.canvas.addNode(MERGE_NODE_NAME, { closeNDV: true });
		await n8n.canvas.clickZoomToFitButton();

		await n8n.canvas.connectNodesByDrag(MANUAL_TRIGGER_NODE_DISPLAY_NAME, 'Edit Fields1', 0, 0);
		await expect(
			n8n.canvas.connectionBetweenNodes(MANUAL_TRIGGER_NODE_DISPLAY_NAME, 'Edit Fields1').first(),
		).toBeAttached();

		await n8n.canvas.connectNodesByDrag('Edit Fields', MERGE_NODE_NAME, 0, 0);
		await expect(
			n8n.canvas.connectionBetweenNodes('Edit Fields', MERGE_NODE_NAME).first(),
		).toBeAttached();

		await n8n.canvas.connectNodesByDrag('Edit Fields1', MERGE_NODE_NAME, 0, 1);
		await expect(
			n8n.canvas.connectionBetweenNodes('Edit Fields1', MERGE_NODE_NAME).first(),
		).toBeAttached();

		await n8n.canvasComposer.saveWorkflowAndWaitForUrl();
		await expect(n8n.canvas.getWorkflowSaveButton()).toContainText('Saved');

		await n8n.canvasComposer.reloadAndWaitForCanvas();

		await checkConnections();

		await n8n.canvas.clickExecuteWorkflowButton();
		await expect(n8n.canvas.stopExecutionButton()).toBeHidden();

		await n8n.canvasComposer.reloadAndWaitForCanvas();

		await checkConnections();

		await n8n.canvas.clickExecuteWorkflowButton();
		await expect(n8n.canvas.stopExecutionButton()).toBeHidden();

		await expect(
			n8n.canvas.getConnectionLabelBetweenNodes('Edit Fields1', MERGE_NODE_NAME).first(),
		).toContainText('1 item');

		await expect(n8n.canvas.getNodeOutputHandle(MERGE_NODE_NAME).first()).toContainText('2 items');
	});

	test('should add nodes and check execution success', async ({ n8n }) => {
		const nodeCount = 3;

		await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
		await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();

		for (let i = 0; i < nodeCount; i++) {
			await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
		}
		await n8n.canvas.clickZoomToFitButton();

		await n8n.canvas.clickExecuteWorkflowButton();
		await expect(n8n.canvas.stopExecutionButton()).toBeHidden();

		await expect(n8n.canvas.getSuccessEdges()).toHaveCount(nodeCount);
		await expect(n8n.canvas.getAllNodeSuccessIndicators()).toHaveCount(nodeCount + 1);
		await expect(
			n8n.canvas.getCanvasHandlePlusWrapperByName('Code in JavaScript2'),
		).toHaveAttribute('data-plus-type', 'success');

		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
		await n8n.canvas.clickZoomToFitButton();

		await expect(
			n8n.canvas.getCanvasHandlePlusWrapperByName('Code in JavaScript3'),
		).not.toHaveAttribute('data-plus-type', 'success');

		await expect(n8n.canvas.getSuccessEdges()).toHaveCount(nodeCount + 1);
		await expect(n8n.canvas.getAllNodeSuccessIndicators()).toHaveCount(nodeCount + 1);
	});

	test('should delete node using context menu', async ({ n8n }) => {
		await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
		await n8n.canvas.clickZoomToFitButton();

		await n8n.canvas.deleteNodeFromContextMenu(CODE_NODE_DISPLAY_NAME);

		await expect(n8n.canvas.getCanvasNodes()).toHaveCount(1);
		await expect(n8n.canvas.nodeConnections()).toHaveCount(0);
	});

	test('should delete node using keyboard shortcut', async ({ n8n }) => {
		await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
		await n8n.canvas.nodeByName(CODE_NODE_DISPLAY_NAME).click();
		await n8n.page.keyboard.press('Backspace');

		await expect(n8n.canvas.getCanvasNodes()).toHaveCount(1);
		await expect(n8n.canvas.nodeConnections()).toHaveCount(0);
	});

	test('should delete node between two connected nodes', async ({ n8n }) => {
		await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
		await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });

		await expect(n8n.canvas.getCanvasNodes()).toHaveCount(3);
		await expect(n8n.canvas.nodeConnections()).toHaveCount(2);

		await n8n.canvas.nodeByName(CODE_NODE_DISPLAY_NAME).click();
		await n8n.canvas.clickZoomToFitButton();
		await n8n.page.keyboard.press('Backspace');

		await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2);
		await expect(n8n.canvas.nodeConnections()).toHaveCount(1);
	});

	test('should delete multiple nodes (context menu or shortcut)', async ({ n8n }) => {
		await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });
		await n8n.canvas.hitDeleteAllNodes();

		await expect(n8n.canvas.getCanvasNodes()).toHaveCount(0);

		await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });
		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });

		await n8n.canvas.rightClickCanvas();
		await n8n.canvas.getContextMenuItem('select_all').click();
		await n8n.canvas.rightClickCanvas();
		await n8n.canvas.getContextMenuItem('delete').click();

		await expect(n8n.canvas.getCanvasNodes()).toHaveCount(0);
	});

	test('should move node', async ({ n8n }) => {
		await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
		await n8n.canvas.nodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
		await n8n.canvas.addNode(CODE_NODE_NAME, { action: 'Code in JavaScript', closeNDV: true });

		await n8n.canvas.clickZoomToFitButton();

		const pos1 = await n8n.canvas.getNodePosition(CODE_NODE_DISPLAY_NAME);
		await n8n.canvas.dragNodeToRelativePosition(CODE_NODE_DISPLAY_NAME, 50, 150);
		const pos2 = await n8n.canvas.getNodePosition(CODE_NODE_DISPLAY_NAME);

		expect(pos2.x).toBeGreaterThan(pos1.x);
		expect(pos2.y).toBeGreaterThan(pos1.y);
	});
});
