import { MAIN_HEADER_TABS, VIEWS } from '@/constants';
import type { IZoomConfig } from '@/Interface';
import { useWorkflowsStore } from '@/stores/workflows.store';
import type { ConnectionDetachedParams } from '@jsplumb/core';
import type { IConnection } from 'n8n-workflow';
import type { Route } from 'vue-router';

/*
	Constants and utility functions mainly used by canvas store
	and components used to display workflow in node view.
	These are general-purpose functions that are exported
	with this module and should be used by importing from
	'@/utils/canvasUtils'.
*/

const SCALE_CHANGE_FACTOR = 1.25;
const MIN_SCALE = 0.2;
const MAX_SCALE = 5;
const SCROLL_ZOOM_SPEED = 0.01;
const MAX_WHEEL_DELTA = 32;

const clamp = (min: number, max: number) => (num: number) => {
	return Math.max(min, Math.min(max, num));
};

const clampScale = clamp(MIN_SCALE, MAX_SCALE);

export const applyScale =
	(scale: number) =>
	({ scale: initialScale, offset: [xOffset, yOffset], origin }: IZoomConfig): IZoomConfig => {
		const newScale = clampScale(initialScale * scale);
		const scaleChange = newScale / initialScale;

		const xOrigin = origin?.[0] ?? window.innerWidth / 2;
		const yOrigin = origin?.[1] ?? window.innerHeight / 2;

		// Calculate the new offsets based on the zoom origin
		xOffset = xOrigin - scaleChange * (xOrigin - xOffset);
		yOffset = yOrigin - scaleChange * (yOrigin - yOffset);

		return {
			scale: newScale,
			offset: [xOffset, yOffset],
		};
	};

export const scaleBigger = applyScale(SCALE_CHANGE_FACTOR);

export const scaleSmaller = applyScale(1 / SCALE_CHANGE_FACTOR);

export const scaleReset = (config: IZoomConfig): IZoomConfig => {
	return applyScale(1 / config.scale)(config);
};

export const getNodeViewTab = (route: Route): string | null => {
	if (route.meta?.nodeView) {
		return MAIN_HEADER_TABS.WORKFLOW;
	} else if (
		[VIEWS.WORKFLOW_EXECUTIONS, VIEWS.EXECUTION_PREVIEW, VIEWS.EXECUTION_HOME]
			.map(String)
			.includes(String(route.name))
	) {
		return MAIN_HEADER_TABS.EXECUTIONS;
	}
	return null;
};

export const getConnectionInfo = (
	connection: ConnectionDetachedParams,
): [IConnection, IConnection] | null => {
	const sourceInfo = connection.sourceEndpoint.parameters;
	const targetInfo = connection.targetEndpoint.parameters;
	const sourceNode = useWorkflowsStore().getNodeById(sourceInfo.nodeId);
	const targetNode = useWorkflowsStore().getNodeById(targetInfo.nodeId);

	if (sourceNode && targetNode) {
		return [
			{
				node: sourceNode.name,
				type: sourceInfo.type,
				index: sourceInfo.index,
			},
			{
				node: targetNode.name,
				type: targetInfo.type,
				index: targetInfo.index,
			},
		];
	}
	return null;
};

const clampWheelDelta = clamp(-MAX_WHEEL_DELTA, MAX_WHEEL_DELTA);

export const normalizeWheelEventDelta = (event: WheelEvent): { deltaX: number; deltaY: number } => {
	const factorByMode: Record<number, number> = {
		[WheelEvent.DOM_DELTA_PIXEL]: 1,
		[WheelEvent.DOM_DELTA_LINE]: 8,
		[WheelEvent.DOM_DELTA_PAGE]: 24,
	};

	const factor = factorByMode[event.deltaMode] ?? 1;

	return {
		deltaX: clampWheelDelta(event.deltaX * factor),
		deltaY: clampWheelDelta(event.deltaY * factor),
	};
};

export const getScaleFromWheelEventDelta = (delta: number): number => {
	return Math.pow(2, -delta * SCROLL_ZOOM_SPEED);
};
