import {has, isEmpty} from 'licia'
import type {JSX} from 'react'
import {getAllIncomerNodeOutputVariables} from './Helper.tsx'
import CodeNode from './node/CodeNode.tsx'
import KnowledgeNode from './node/KnowledgeNode.tsx'
import LlmNode from './node/LlmNode.tsx'
import OutputNode from './node/OutputNode.tsx'
import SwitchNode from './node/SwitchNode.tsx'
import TemplateNode from './node/TemplateNode.tsx'
import type {NodeChecker} from './types.ts'

const inputSingleVariableChecker: (field: string) => NodeChecker = field => {
  return (id, inputSchema, nodes, edges, data) => {
    let nodeData = data[id] ?? {}
    if (has(nodeData, field)) {
      let expression = nodeData?.[field] ?? ''
      if (!isEmpty(expression)) {
        let outputVariables = new Set([
          ...getAllIncomerNodeOutputVariables(id, nodes, edges, data).map(i => `${i.id}.${i.variable}`),
          ...Object.keys(inputSchema),
        ])
        if (!outputVariables.has(expression)) {
          return {
            error: true,
            message: `节点 ${id} 存在错误：变量 ${expression} 不存在`,
          }
        }
      }
    }
    return {error: false}
  }
}

const inputMultiVariableChecker: NodeChecker = (id, inputSchema, nodes, edges, data) => {
  let nodeData = data[id] ?? {}
  if (has(nodeData, 'inputs')) {
    let inputs = nodeData?.inputs ?? {}
    if (!isEmpty(inputs)) {
      let outputVariables = new Set([
        ...getAllIncomerNodeOutputVariables(id, nodes, edges, data).map(i => `${i.id}.${i.variable}`),
        ...Object.keys(inputSchema),
      ])
      for (const key of Object.keys(inputs)) {
        let variable = inputs[key]?.variable ?? ''
        if (!outputVariables.has(variable)) {
          return {
            error: true,
            message: `节点 ${id} 存在错误：变量 ${variable} 不存在`,
          }
        }
      }
    }
  }
  return {error: false}
}

type NodeDefine = {
  key: string,
  group: string,
  name: string,
  icon: JSX.Element,
  description: string,
  component: any,
  checkers: NodeChecker[],
}

export const NodeRegistry: NodeDefine[] = [
  {
    key: 'llm-node',
    group: '普通节点',
    name: '大模型',
    icon: <i className="fa fa-message"/>,
    description: '使用大模型对话',
    component: LlmNode,
    checkers: [inputMultiVariableChecker],
  },
  {
    key: 'knowledge-node',
    group: '普通节点',
    name: '知识库',
    icon: <i className="fa fa-book-bookmark"/>,
    description: '',
    component: KnowledgeNode,
    checkers: [inputMultiVariableChecker],
  },
  {
    key: 'code-node',
    group: '普通节点',
    name: '代码执行',
    icon: <i className="fa fa-code"/>,
    description: '执行自定义的处理代码',
    component: CodeNode,
    checkers: [inputMultiVariableChecker],
  },
  {
    key: 'template-node',
    group: '普通节点',
    name: '模板替换',
    icon: <i className="fa fa-pen-nib"/>,
    description: '使用模板聚合转换变量表示',
    component: TemplateNode,
    checkers: [inputMultiVariableChecker],
  },
  {
    key: 'switch-node',
    group: '逻辑节点',
    name: '分支',
    icon: <i className="fa fa-code-fork"/>,
    description: '根据不同的情况前往不同的分支',
    component: SwitchNode,
    checkers: [],
  },
  {
    key: 'output-node',
    group: '输出节点',
    name: '输出',
    icon: <i className="fa fa-file"/>,
    description: '定义输出变量',
    component: OutputNode,
    checkers: [inputSingleVariableChecker('output')],
  },
]

export const NodeRegistryMap: Record<string, NodeDefine> = NodeRegistry.reduce((a, v) => ({...a, [v.key]: v}), {})
