import { h, computed } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from '@n8n/i18n';
import { useMessage } from '@/app/composables/useMessage';
import { useToast } from '@/app/composables/useToast';
import { EnterpriseEditionFeature, MODAL_CONFIRM, VIEWS } from '@/app/constants';
import { DEBUG_PAYWALL_MODAL_KEY } from '../executions.constants';
import type { INodeUi } from '@/Interface';
import { useWorkflowsStore } from '@/app/stores/workflows.store';
import { useWorkflowExecutionStateStore } from '@/app/stores/workflowExecutionState.store';
import {
	createWorkflowDocumentId,
	injectWorkflowDocumentStore,
} from '@/app/stores/workflowDocument.store';
import { useSettingsStore } from '@/app/stores/settings.store';
import { useUIStore } from '@/app/stores/ui.store';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { useRootStore } from '@n8n/stores/useRootStore';
import { isFullExecutionResponse } from '@/app/utils/typeGuards';
import { sanitizeHtml } from '@/app/utils/htmlUtils';
import { usePageRedirectionHelper } from '@/app/composables/usePageRedirectionHelper';
import { isTrimmedNodeExecutionData } from 'n8n-workflow';

export const useExecutionDebugging = () => {
	const telemetry = useTelemetry();

	const router = useRouter();
	const i18n = useI18n();
	const message = useMessage();
	const toast = useToast();
	const workflowsStore = useWorkflowsStore();
	const workflowDocumentStore = injectWorkflowDocumentStore();
	const settingsStore = useSettingsStore();
	const uiStore = useUIStore();
	const { markStateDirty } = uiStore;

	const pageRedirectionHelper = usePageRedirectionHelper();

	const isDebugEnabled = computed(
		() => settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.DebugInEditor],
	);

	const applyExecutionData = async (executionId: string): Promise<void> => {
		const execution = await workflowsStore.getExecution(executionId);
		const workflowNodes = workflowDocumentStore.value.allNodes;

		if (!execution?.data?.resultData) {
			return;
		}

		const { runData, pinData = {} } = execution.data.resultData;

		const executionNodeNames = Object.keys(runData);
		const missingNodeNames = executionNodeNames.filter(
			(name) => !workflowNodes.some((node) => node.name === name),
		);

		// Using the pinned data of the workflow to check if the node is pinned
		// because workflowsStore.getCurrentWorkflow() returns a cached workflow without the updated pinned data
		const workflowPinnedNodeNames = Object.keys(workflowDocumentStore.value.pinnedDataByNodeName);
		const executionDataNodeNames = new Set([...executionNodeNames, ...Object.keys(pinData)]);
		const matchingPinnedNodeNames = [...executionDataNodeNames].filter((name) =>
			workflowPinnedNodeNames.includes(name),
		);

		if (matchingPinnedNodeNames.length > 0) {
			const confirmMessage = h('p', [
				i18n.baseText('nodeView.confirmMessage.debug.message'),
				h(
					'ul',
					{ class: 'mt-l ml-l' },
					matchingPinnedNodeNames.map((name) => h('li', sanitizeHtml(name))),
				),
			]);

			const overWritePinnedDataConfirm = await message.confirm(
				confirmMessage,
				i18n.baseText('nodeView.confirmMessage.debug.headline'),
				{
					type: 'warning',
					confirmButtonText: i18n.baseText('nodeView.confirmMessage.debug.confirmButtonText'),
					cancelButtonText: i18n.baseText('nodeView.confirmMessage.debug.cancelButtonText'),

					customClass: 'matching-pinned-nodes-confirmation',
				},
			);

			if (overWritePinnedDataConfirm === MODAL_CONFIRM) {
				matchingPinnedNodeNames.forEach((name) => {
					workflowDocumentStore.value.unpinNodeData(name);
				});
			} else {
				await router.push({
					name: VIEWS.EXECUTION_PREVIEW,
					params: { workflowId: workflowDocumentStore.value.workflowId, executionId },
				});
				return;
			}
		}

		// Set execution data
		workflowDocumentStore.value.resetAllNodesIssues();
		useWorkflowExecutionStateStore(workflowDocumentStore.value.documentId).setWorkflowExecutionData(
			execution,
		);

		// Pin data of all nodes which do not have a parent node
		const pinnableNodes = workflowNodes.filter(
			(node: INodeUi) => !workflowDocumentStore.value.getParentNodes(node.name).length,
		);

		let pinnings = 0;

		pinnableNodes.forEach((node: INodeUi) => {
			const taskData = runData[node.name]?.[0];
			if (taskData?.data?.main) {
				// Get the first main output that has data, preserving all execution data including binary
				const nodeData = taskData.data.main.find((output) => output && output.length > 0);
				if (nodeData) {
					// Pinning a placeholder would round-trip it through the next manual run and persist it to DB.
					if (isTrimmedNodeExecutionData(nodeData)) {
						return;
					}
					pinnings++;
					workflowDocumentStore.value.pinNodeData(node.name, nodeData);

					// Clear dirtiness timestamps so nodes don't appear dirty after restoration.
					// The old pinData({ isRestoration: true }) handled this internally.
					workflowDocumentStore.value.clearPinnedDataTimestamps(node.name);
				}
			}
		});

		if (pinnings > 0 || matchingPinnedNodeNames.length > 0) {
			markStateDirty();
		}

		toast.showToast({
			title: i18n.baseText('nodeView.showMessage.debug.title'),
			message: i18n.baseText('nodeView.showMessage.debug.content'),
			type: 'info',
		});

		if (missingNodeNames.length) {
			toast.showToast({
				title: i18n.baseText('nodeView.showMessage.debug.missingNodes.title'),
				message: i18n.baseText('nodeView.showMessage.debug.missingNodes.content', {
					interpolate: { nodeNames: missingNodeNames.join(', ') },
				}),
				type: 'warning',
			});
		}

		telemetry.track('User clicked debug execution button', {
			instance_id: useRootStore().instanceId,
			exec_status: isFullExecutionResponse(execution) ? execution.status : '',
			override_pinned_data: pinnableNodes.length === pinnings,
			all_exec_data_imported: missingNodeNames.length === 0,
		});
	};

	const handleDebugLinkClick = (event: Event): void => {
		if (!isDebugEnabled.value) {
			uiStore.openModalWithData({
				name: DEBUG_PAYWALL_MODAL_KEY,
				data: {
					title: i18n.baseText('executionsList.debug.paywall.title'),
					footerButtonAction: () => {
						uiStore.closeModal(DEBUG_PAYWALL_MODAL_KEY);
						void pageRedirectionHelper.goToUpgrade('debug', 'upgrade-debug');
					},
				},
			});
			event.preventDefault();
			event.stopPropagation();
			return;
		}
		useWorkflowExecutionStateStore(
			createWorkflowDocumentId(workflowsStore.workflowId),
		).setIsInDebugMode(false);
	};

	return {
		applyExecutionData,
		handleDebugLinkClick,
	};
};
