import type {
  UpdateWorkflowNodesMapPayload,
} from './index'
import type { WorkflowNodesMap } from './node'
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import { PreviewCard, PreviewCardContent, PreviewCardTrigger } from '@langgenius/dify-ui/preview-card'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { mergeRegister } from '@lexical/utils'
import {
  COMMAND_PRIORITY_EDITOR,
} from 'lexical'
import {
  memo,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useReactFlow, useStoreApi } from 'reactflow'
import { isRagVariableVar, isSpecialVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import VarFullPathPanel from '@/app/components/workflow/nodes/_base/components/variable/var-full-path-panel'
import {
  VariableLabelInEditor,
} from '@/app/components/workflow/nodes/_base/components/variable/variable-label'
import { Type } from '@/app/components/workflow/nodes/llm/types'
import { isExceptionVariable } from '@/app/components/workflow/utils'
import { useSelectOrDelete } from '../../hooks'
import {
  DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND,
  UPDATE_WORKFLOW_NODES_MAP,
} from './index'
import { WorkflowVariableBlockNode } from './node'
import { useLlmModelPluginInstalled } from './use-llm-model-plugin-installed'

type WorkflowVariableBlockComponentProps = {
  nodeKey: string
  variables: string[]
  workflowNodesMap: WorkflowNodesMap
  availableVariables?: NodeOutPutVar[]
  environmentVariables?: Var[]
  conversationVariables?: Var[]
  ragVariables?: Var[]
  getVarType?: (payload: {
    nodeId: string
    valueSelector: ValueSelector
  }) => Type
}

const WorkflowVariableBlockComponent = ({
  nodeKey,
  variables,
  workflowNodesMap = {},
  availableVariables,
  getVarType,
}: WorkflowVariableBlockComponentProps) => {
  const { t } = useTranslation()
  const [editor] = useLexicalComposerContext()
  const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND)
  const variablesLength = variables.length
  const isRagVar = isRagVariableVar(variables)
  const isShowAPart = variablesLength > 2 && !isRagVar
  const varName = (
    () => {
      const isSystem = isSystemVar(variables)
      const varName = variables[variablesLength - 1]
      return `${isSystem ? 'sys.' : ''}${varName}`
    }
  )()
  const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState<WorkflowNodesMap>(workflowNodesMap)
  const [localAvailableVariables, setLocalAvailableVariables] = useState<NodeOutPutVar[]>(availableVariables || [])
  const node = localWorkflowNodesMap![variables[isRagVar ? 1 : 0]!]

  const isException = isExceptionVariable(varName, node?.type)
  const sourceNodeId = variables[isRagVar ? 1 : 0]
  const isLlmModelInstalled = useLlmModelPluginInstalled(sourceNodeId!, localWorkflowNodesMap)
  const variableValid = useMemo(() => {
    if (isSpecialVar(variables[0] ?? ''))
      return true

    if (!variables[1])
      return false

    const sourceNode = localAvailableVariables.find(v => v.nodeId === variables[0])
    if (!sourceNode)
      return false

    return sourceNode.vars.some(v => v.variable === variables[1])
  }, [localAvailableVariables, variables])

  const reactflow = useReactFlow()
  const store = useStoreApi()

  useEffect(() => {
    if (!editor.hasNodes([WorkflowVariableBlockNode]))
      throw new Error('WorkflowVariableBlockPlugin: WorkflowVariableBlock not registered on editor')

    return mergeRegister(
      editor.registerCommand(
        UPDATE_WORKFLOW_NODES_MAP,
        (payload: UpdateWorkflowNodesMapPayload) => {
          setLocalWorkflowNodesMap(payload.workflowNodesMap)
          setLocalAvailableVariables(payload.availableVariables)
          return true
        },
        COMMAND_PRIORITY_EDITOR,
      ),
    )
  }, [editor])

  const handleVariableJump = useCallback(() => {
    const workflowContainer = document.getElementById('workflow-container')
    const {
      clientWidth,
      clientHeight,
    } = workflowContainer!

    const {
      setViewport,
    } = reactflow
    const { transform } = store.getState()
    const zoom = transform[2]
    const position = node!.position
    setViewport({
      x: (clientWidth - 400 - node!.width! * zoom) / 2 - position!.x * zoom,
      y: (clientHeight - node!.height! * zoom) / 2 - position!.y * zoom,
      zoom: transform[2],
    })
  }, [node, reactflow, store])

  const Item = (
    <VariableLabelInEditor
      nodeType={node?.type}
      nodeTitle={node?.title}
      variables={variables}
      onClick={(e) => {
        e.stopPropagation()
        handleVariableJump()
      }}
      isExceptionVariable={isException}
      errorMsg={
        !variableValid
          ? t('errorMsg.invalidVariable', { ns: 'workflow' })
          : !isLlmModelInstalled
              ? t('errorMsg.modelPluginNotInstalled', { ns: 'workflow' })
              : undefined
      }
      isSelected={isSelected}
      ref={ref}
      notShowFullPath={isShowAPart}
    />
  )

  if (!node || !isShowAPart)
    return Item

  return (
    <PreviewCard>
      <PreviewCardTrigger delay={300} closeDelay={200} render={<div>{Item}</div>} />
      <PreviewCardContent popupClassName="border-0 bg-transparent p-0 shadow-none">
        <VarFullPathPanel
          nodeName={node.title}
          path={variables.slice(1)}
          varType={getVarType
            ? getVarType({
                nodeId: variables[0]!,
                valueSelector: variables,
              })
            : Type.string}
          nodeType={node?.type}
        />
      </PreviewCardContent>
    </PreviewCard>
  )
}

export default memo(WorkflowVariableBlockComponent)
