import { startNode } from './start'
import { endNode } from './end'
import { httpNode } from './http'
import { conditionNode } from './condition'
import { databaseNode } from './database'
import { codeNode } from './code'
import { iterationNode } from './iteration'
import { loopNode } from './loop'
import { listNode } from './list'
import { questionClassifierNode } from './question-classifier'
import { loopEndNode } from './loop-end'
import { triggerScheduleNode } from './schedule'
import { webhookNode } from './webhook'
import { moduleInvokeNode } from './module-invoke'
import { basicDatasetNode } from './basic-dataset'
import { viewDataNode } from './view-data'
import { smsSenderNode } from './sms-sender'
import { mailSenderNode } from './mail-sender'
import { workflowApprovalNode } from './workflow-approval'
import { llmNode } from './llm'
import { knowledgeRetrievalNode } from './knowledge-retrieval'

import { getNodeDisplayName } from '@/nodes/i18n'
import type { INodeType } from '../Interface'

const loopStartNode = {
	...startNode,
	name: 'loop-start',
	nodeType: 'loop-start',
	displayName: getNodeDisplayName('loop-start'),
	hideToolBar: true,
	hideInLibary: true,
	schema: {
		...startNode.schema,
		nodeType: 'loop-start',
		data: {
			...startNode.schema.data,
			type: 'loop-start',
			title: getNodeDisplayName('loop-start')
		}
	}
}

const iterationStartNode = {
	...startNode,
	name: 'iteration-start',
	nodeType: 'iteration-start',
	displayName: getNodeDisplayName('iteration-start'),
	hideToolBar: true,
	hideInLibary: true,
	schema: {
		...startNode.schema,
		nodeType: 'iteration-start',
		data: {
			...startNode.schema.data,
			type: 'iteration-start',
			title: getNodeDisplayName('iteration-start')
		}
	}
}

/**
 * 异常分支输出
 * @param node
 * @returns
 */
const withFailBranchOutput = (node: INodeType): INodeType => {
	const baseOutputs = node.outputs

	return {
		...node,
		outputs: (data: any) => {
			const resolved = typeof baseOutputs === 'function' ? baseOutputs(data) : baseOutputs
			const outputs = Array.isArray(resolved) ? [...resolved] : []

			if (data?.error_strategy !== 'fail-branch') {
				return outputs
			}

			const failOutputId = data?.fail_branch_node_id

			if (!failOutputId) {
				return outputs
			}

			const hasFailOutput = outputs.some((item) => {
				if (typeof item === 'string') {
					return item === failOutputId
				}

				return !!item && typeof item === 'object' && 'id' in item && item.id === failOutputId
			})

			if (!hasFailOutput) {
				outputs.push({
					id: failOutputId,
					type: 'port',
					label: '异常分支',
					labelStyle: {
						color: '#dc6803'
					}
				})
			}

			return outputs
		}
	}
}

const baseNodes = [
	startNode,
	endNode,
	llmNode,
	httpNode,
	conditionNode,
	databaseNode,
	codeNode,
	iterationNode,
	loopNode,
	listNode,
	questionClassifierNode,
	loopStartNode,
	iterationStartNode,
	loopEndNode,
	triggerScheduleNode,
	webhookNode,
	moduleInvokeNode,
	basicDatasetNode,
	viewDataNode,
	smsSenderNode,
	mailSenderNode,
	workflowApprovalNode,
	knowledgeRetrievalNode
]

const nodes = baseNodes.map((node) => withFailBranchOutput(node))

const nodeMap = nodes.reduce(
	(acc, cur) => {
		acc[cur.name] = cur
		if (cur.name === 'database') {
			acc['database-query'] = cur
		}
		return acc
	},
	{} as Record<string, INodeType>
)

/**
 * 动态注册节点
 */
export const registerDynamicNodes = (dynamicNodes: INodeType[]) => {
	for (const node of dynamicNodes) {
		if (!node?.name) continue

		const normalizedNode = withFailBranchOutput(node)
		const existedIndex = nodes.findIndex((item) => item.name === normalizedNode.name)
		if (existedIndex >= 0) {
			nodes.splice(existedIndex, 1, normalizedNode)
		} else {
			nodes.push(normalizedNode)
		}

		nodeMap[normalizedNode.name] = normalizedNode
		nodeMap[normalizedNode.schema?.nodeType || normalizedNode.name] = normalizedNode

		// Compatibility alias in case backend sends nodeType under `data.nodeType`.
		const schemaNodeType = normalizedNode.schema?.nodeType || normalizedNode.schema?.data?.nodeType
		if (schemaNodeType && schemaNodeType !== normalizedNode.name) {
			nodeMap[schemaNodeType] = normalizedNode
		}
	}
}

export { nodes, nodeMap }
