import type { ActionDropdownItem, INodeUi } from '@/Interface';
import {
	NOT_DUPLICATABLE_NODE_TYPES,
	STICKY_NODE_TYPE,
	PRODUCTION_ONLY_TRIGGER_NODE_TYPES,
} from '@/app/constants';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import { useSourceControlStore } from '@/features/integrations/sourceControl.ee/sourceControl.store';
import { useUIStore } from '@/app/stores/ui.store';
import { useCollaborationStore } from '@/features/collaboration/collaboration/collaboration.store';
import { useFocusedNodesStore } from '@/features/ai/assistant/focusedNodes.store';
import { useI18n } from '@n8n/i18n';
import { getResourcePermissions } from '@n8n/permissions';
import type { INode, INodeTypeDescription } from 'n8n-workflow';
import { NodeHelpers, WEBHOOK_NODE_TYPE } from 'n8n-workflow';
import { computed, type ComputedRef } from 'vue';
import { isPresent } from '@/app/utils/typesUtils';
import { useEditorContext } from '@/app/composables/useEditorContext';
import { usePinnedData } from '@/app/composables/usePinnedData';
import { injectWorkflowDocumentStore } from '@/app/stores/workflowDocument.store';

export type ContextMenuAction =
	| 'open'
	| 'copy'
	| 'toggle_activation'
	| 'duplicate'
	| 'execute'
	| 'copy_test_url'
	| 'copy_production_url'
	| 'rename'
	| 'replace'
	| 'toggle_pin'
	| 'delete'
	| 'select_all'
	| 'deselect_all'
	| 'add_node'
	| 'add_sticky'
	| 'change_color'
	| 'open_sub_workflow'
	| 'tidy_up'
	| 'extract_sub_workflow'
	| 'focus_ai_on_selected';

/**
 * Actions that, once selected, hand off to another floating layer (e.g. a
 * popover) which then takes focus. For these the context menu must not restore
 * focus on close — otherwise the restore lands outside the freshly-opened layer
 * and immediately dismisses it.
 */
const FOCUS_HANDOFF_ACTIONS = new Set<ContextMenuAction>(['change_color']);

export function isFocusHandoffAction(action: ContextMenuAction): boolean {
	return FOCUS_HANDOFF_ACTIONS.has(action);
}

type Item = ActionDropdownItem<ContextMenuAction>;

export function useContextMenuItems(targetNodeIds: ComputedRef<string[]>): ComputedRef<Item[]> {
	const uiStore = useUIStore();
	const nodeTypesStore = useNodeTypesStore();
	const workflowDocumentStore = injectWorkflowDocumentStore();
	const sourceControlStore = useSourceControlStore();
	const collaborationStore = useCollaborationStore();
	const focusedNodesStore = useFocusedNodesStore();
	const i18n = useI18n();

	// Per-editor host overrides (already ANDed with the instance-wide store
	// flags) — e.g. the Instance AI artifact preview supersedes the AI
	// capabilities of its embedded editor, which must hide the AI actions.
	const { aiAssistant, aiBuilder, instanceAi } = useEditorContext();

	const workflowPermissions = computed(
		() => getResourcePermissions(workflowDocumentStore?.value?.scopes).workflow,
	);

	const isReadOnly = computed(
		() =>
			sourceControlStore.preferences.branchReadOnly ||
			uiStore.isReadOnlyView ||
			!workflowPermissions.value.update ||
			(workflowDocumentStore?.value?.isArchived ?? false) ||
			collaborationStore.shouldBeReadOnly,
	);

	const canOpenSubworkflow = computed(() => {
		if (targetNodes.value.length !== 1) return false;

		const node = targetNodes.value[0];

		if (!NodeHelpers.isNodeWithWorkflowSelector(node)) return false;

		return !!NodeHelpers.getSubworkflowId(node);
	});

	const targetNodes = computed(() =>
		targetNodeIds.value
			.map((nodeId) => workflowDocumentStore?.value?.getNodeById(nodeId))
			.filter(isPresent),
	);

	const canAddNodeOfType = (nodeType: INodeTypeDescription) => {
		const sameTypeNodes = (workflowDocumentStore?.value?.allNodes ?? []).filter(
			(n) => n.type === nodeType.name,
		);
		return nodeType.maxNodes === undefined || sameTypeNodes.length < nodeType.maxNodes;
	};

	const canDuplicateNode = (node: INode): boolean => {
		const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
		if (!nodeType) return false;
		if (NOT_DUPLICATABLE_NODE_TYPES.includes(nodeType.name)) return false;

		return canAddNodeOfType(nodeType);
	};

	const hasPinData = (node: INode): boolean => {
		return !!workflowDocumentStore?.value?.pinnedDataByNodeName?.[node.name];
	};

	const isExecutable = (node: INodeUi) => {
		if (!workflowDocumentStore?.value) return false;

		const nodeType = nodeTypesStore.getNodeType(
			node.type,
			node.typeVersion,
		) as INodeTypeDescription;

		return NodeHelpers.isExecutable(
			{ expression: workflowDocumentStore.value.getExpressionHandler() },
			node,
			nodeType,
		);
	};

	const isWebhookNode = (node: INodeUi) => {
		if (node.type === WEBHOOK_NODE_TYPE) return true;
		if (!node.webhookId) return false;
		if (!node.type.toLocaleLowerCase().includes('trigger')) return false;
		if (!isExecutable(node)) return false;

		return true;
	};

	return computed(() => {
		const nodes = targetNodes.value;
		const onlyStickies = nodes.every((node) => node.type === STICKY_NODE_TYPE);

		const i18nOptions = {
			adjustToNumber: nodes.length,
			interpolate: {
				subject: onlyStickies
					? i18n.baseText('contextMenu.sticky', { adjustToNumber: nodes.length })
					: i18n.baseText('contextMenu.node', { adjustToNumber: nodes.length }),
			},
		};

		const selectionActions: Item[] = [
			{
				id: 'select_all',
				divided: true,
				label: i18n.baseText('contextMenu.selectAll'),
				shortcut: { metaKey: true, keys: ['A'] },
				disabled: nodes.length === (workflowDocumentStore?.value?.allNodes ?? []).length,
			},
			{
				id: 'deselect_all',
				label: i18n.baseText('contextMenu.deselectAll'),
				disabled: nodes.length === 0,
			},
		];

		const extractionActions: Item[] = [
			{
				id: 'extract_sub_workflow',
				divided: true,
				label: i18n.baseText('contextMenu.extract', { adjustToNumber: nodes.length }),
				shortcut: { altKey: true, keys: ['X'] },
				disabled: isReadOnly.value,
			},
		];

		const aiActions: Item[] = [
			!onlyStickies &&
				(aiAssistant.value || aiBuilder.value) &&
				!instanceAi.value &&
				focusedNodesStore.isFeatureEnabled && {
					id: 'focus_ai_on_selected',
					divided: true,
					label: i18n.baseText('contextMenu.focusAiOnSelected', {
						adjustToNumber: nodes.length,
						interpolate: { count: nodes.length },
					}),
					shortcut: { altKey: true, keys: ['I'] },
					disabled: isReadOnly.value,
				},
		].filter(Boolean) as Item[];

		const layoutActions: Item[] = [
			{
				id: 'tidy_up',
				divided: true,
				label: i18n.baseText(
					nodes.length < 2 ? 'contextMenu.tidyUpWorkflow' : 'contextMenu.tidyUpSelection',
				),
				shortcut: { shiftKey: true, altKey: true, keys: ['T'] },
				disabled: isReadOnly.value,
			},
		];

		if (nodes.length === 0) {
			return [
				{
					id: 'add_node',
					shortcut: { keys: ['N'] },
					label: i18n.baseText('contextMenu.addNode'),
					disabled: isReadOnly.value,
				},
				{
					id: 'add_sticky',
					shortcut: { shiftKey: true, keys: ['s'] },
					label: i18n.baseText('contextMenu.addSticky'),
					disabled: isReadOnly.value,
				},
				...layoutActions,
				...selectionActions,
			];
		} else {
			const menuActions: Item[] = [
				!onlyStickies && {
					id: 'toggle_activation',
					label: nodes.every((node) => node.disabled)
						? i18n.baseText('contextMenu.activate', i18nOptions)
						: i18n.baseText('contextMenu.deactivate', i18nOptions),
					shortcut: { keys: ['D'] },
					disabled: isReadOnly.value,
				},
				!onlyStickies && {
					id: 'toggle_pin',
					label: nodes.every((node) => hasPinData(node))
						? i18n.baseText('contextMenu.unpin', i18nOptions)
						: i18n.baseText('contextMenu.pin', i18nOptions),
					shortcut: { keys: ['p'] },
					disabled: isReadOnly.value || !nodes.every((n) => usePinnedData(n).canPinNode(true)),
				},
				{
					id: 'copy',
					label: i18n.baseText('contextMenu.copy', i18nOptions),
					shortcut: { metaKey: true, keys: ['C'] },
				},
				{
					id: 'duplicate',
					label: i18n.baseText('contextMenu.duplicate', i18nOptions),
					shortcut: { metaKey: true, keys: ['D'] },
					disabled: isReadOnly.value || !nodes.every(canDuplicateNode),
				},
				...layoutActions,
				...extractionActions,
				...aiActions,
				...selectionActions,
				{
					id: 'delete',
					divided: true,
					label: i18n.baseText('contextMenu.delete', i18nOptions),
					shortcut: { keys: ['Del'] },
					disabled: isReadOnly.value,
				},
			].filter(Boolean) as Item[];

			if (nodes.length === 1) {
				const copyWebhookActions: Item[] = [];

				if (isWebhookNode(nodes[0])) {
					const isProductionOnly = PRODUCTION_ONLY_TRIGGER_NODE_TYPES.includes(nodes[0].type);
					const isWorkflowActive = workflowDocumentStore?.value?.active ?? false;
					if (!isProductionOnly) {
						copyWebhookActions.push({
							divided: true,
							id: 'copy_test_url',
							label: i18n.baseText('contextMenu.copyTestUrl'),
							shortcut: { shiftKey: true, altKey: true, keys: ['U'] },
							disabled: false,
						});
					}

					if (isWorkflowActive) {
						copyWebhookActions.push({
							divided: isProductionOnly,
							id: 'copy_production_url',
							label: i18n.baseText('contextMenu.copyProductionUrl'),
							shortcut: { altKey: true, keys: ['U'] },
							disabled: false,
						});
					}
				}

				const singleNodeActions: Item[] = onlyStickies
					? [
							{
								id: 'open',
								label: i18n.baseText('contextMenu.editSticky'),
								shortcut: { keys: ['↵'] },
								disabled: isReadOnly.value,
							},
							{
								id: 'change_color',
								label: i18n.baseText('contextMenu.changeColor'),
								disabled: isReadOnly.value,
							},
						]
					: [
							{
								id: 'open',
								label: i18n.baseText('contextMenu.open'),
								shortcut: { keys: ['↵'] },
							},
							{
								id: 'execute',
								label: i18n.baseText('contextMenu.test'),
								disabled: isReadOnly.value || !isExecutable(nodes[0]),
							},
							...copyWebhookActions,
							{
								divided: !!copyWebhookActions.length,
								id: 'rename',
								label: i18n.baseText('contextMenu.rename'),
								shortcut: { keys: ['Space'] },
								disabled: isReadOnly.value,
							},
							{
								id: 'replace',
								label: i18n.baseText('contextMenu.replace'),
								shortcut: { keys: ['R'] },
								disabled: isReadOnly.value,
							},
						];

				if (NodeHelpers.isNodeWithWorkflowSelector(nodes[0])) {
					singleNodeActions.push({
						id: 'open_sub_workflow',
						label: i18n.baseText('contextMenu.openSubworkflow'),
						shortcut: { shiftKey: true, metaKey: true, keys: ['O'] },
						disabled: !canOpenSubworkflow.value,
					});
				}
				// Add actions only available for a single node
				menuActions.unshift(...singleNodeActions);
			}

			return menuActions;
		}
	});
}
