import type { ComputedRef, MaybeRef } from 'vue';
import { ref, computed, unref } from 'vue';
import {
	CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE,
	NodeConnectionType,
	NodeHelpers,
} from 'n8n-workflow';
import type { INodeTypeDescription, Workflow, INode, INodeParameters } from 'n8n-workflow';
import {
	AI_CATEGORY_AGENTS,
	AI_CATEGORY_CHAINS,
	AI_CODE_NODE_TYPE,
	AI_SUBCATEGORY,
	CHAT_TRIGGER_NODE_TYPE,
	MANUAL_CHAT_TRIGGER_NODE_TYPE,
} from '@/constants';
import type { INodeUi } from '@/Interface';

export interface ChatTriggerDependencies {
	getNodeByName: (name: string) => INodeUi | null;
	getNodeType: (type: string, version: number) => INodeTypeDescription | null;
	canvasNodes: MaybeRef<INodeUi[]>;
	workflow: ComputedRef<Workflow>;
}

export function useChatTrigger({
	getNodeByName,
	getNodeType,
	canvasNodes,
	workflow,
}: ChatTriggerDependencies) {
	const chatTriggerName = ref<string | null>(null);
	const connectedNode = ref<INode | null>(null);

	const chatTriggerNode = computed(() =>
		chatTriggerName.value ? getNodeByName(chatTriggerName.value) : null,
	);

	const allowFileUploads = computed(() => {
		return (
			(chatTriggerNode.value?.parameters?.options as INodeParameters)?.allowFileUploads === true
		);
	});

	const allowedFilesMimeTypes = computed(() => {
		return (
			(
				chatTriggerNode.value?.parameters?.options as INodeParameters
			)?.allowedFilesMimeTypes?.toString() ?? ''
		);
	});

	/** Gets the chat trigger node from the workflow */
	function setChatTriggerNode() {
		const triggerNode = unref(canvasNodes).find((node) =>
			[CHAT_TRIGGER_NODE_TYPE, MANUAL_CHAT_TRIGGER_NODE_TYPE].includes(node.type),
		);

		if (!triggerNode) {
			return;
		}
		chatTriggerName.value = triggerNode.name;
	}

	/** Sets the connected node after finding the trigger */
	function setConnectedNode() {
		const triggerNode = chatTriggerNode.value;

		if (!triggerNode) {
			return;
		}

		const chatChildren = workflow.value.getChildNodes(triggerNode.name);

		const chatRootNode = chatChildren
			.reverse()
			.map((nodeName: string) => getNodeByName(nodeName))
			.filter((n): n is INodeUi => n !== null)
			// Reverse the nodes to match the last node logs first
			.reverse()
			.find((storeNode: INodeUi): boolean => {
				// Skip summarization nodes
				if (storeNode.type === CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE) return false;
				const nodeType = getNodeType(storeNode.type, storeNode.typeVersion);

				if (!nodeType) return false;

				// Check if node is an AI agent or chain based on its metadata
				const isAgent =
					nodeType.codex?.subcategories?.[AI_SUBCATEGORY]?.includes(AI_CATEGORY_AGENTS);
				const isChain =
					nodeType.codex?.subcategories?.[AI_SUBCATEGORY]?.includes(AI_CATEGORY_CHAINS);

				// Handle custom AI Langchain Code nodes that could act as chains or agents
				let isCustomChainOrAgent = false;
				if (nodeType.name === AI_CODE_NODE_TYPE) {
					// Get node connection types for inputs and outputs
					const inputs = NodeHelpers.getNodeInputs(workflow.value, storeNode, nodeType);
					const inputTypes = NodeHelpers.getConnectionTypes(inputs);

					const outputs = NodeHelpers.getNodeOutputs(workflow.value, storeNode, nodeType);
					const outputTypes = NodeHelpers.getConnectionTypes(outputs);

					// Validate if node has required AI connection types
					if (
						inputTypes.includes(NodeConnectionType.AiLanguageModel) &&
						inputTypes.includes(NodeConnectionType.Main) &&
						outputTypes.includes(NodeConnectionType.Main)
					) {
						isCustomChainOrAgent = true;
					}
				}

				// Skip if node is not an AI component
				if (!isAgent && !isChain && !isCustomChainOrAgent) return false;

				// Check if this node is connected to the trigger node
				const parentNodes = workflow.value.getParentNodes(storeNode.name);
				const isChatChild = parentNodes.some(
					(parentNodeName) => parentNodeName === triggerNode.name,
				);

				// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
				const result = Boolean(isChatChild && (isAgent || isChain || isCustomChainOrAgent));
				return result;
			});
		connectedNode.value = chatRootNode ?? null;
	}

	return {
		allowFileUploads,
		allowedFilesMimeTypes,
		chatTriggerNode,
		connectedNode: computed(() => connectedNode.value),
		setChatTriggerNode,
		setConnectedNode,
	};
}
