import { useCallback, useMemo } from 'react'
import produce from 'immer'
import { useStoreApi } from 'reactflow'
import type { ValueSelector, Var } from '../../types'
import { VarType } from '../../types'
import { getOperators } from '../if-else/utils'
import { OrderBy } from './types'
import type { Condition, Limit, ListFilterNodeType } from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import {
  useIsChatMode,
  useNodesReadOnly,
  useWorkflow,
  useWorkflowVariables,
} from '@/app/components/workflow/hooks'

const useConfig = (id: string, payload: ListFilterNodeType) => {
  const { nodesReadOnly: readOnly } = useNodesReadOnly()
  const isChatMode = useIsChatMode()

  const store = useStoreApi()
  const { getBeforeNodesInSameBranch } = useWorkflow()

  const {
    getNodes,
  } = store.getState()
  const currentNode = getNodes().find(n => n.id === id)
  const isInIteration = payload.isInIteration
  const iterationNode = isInIteration ? getNodes().find(n => n.id === currentNode!.parentId) : null
  const isInLoop = payload.isInLoop
  const loopNode = isInLoop ? getNodes().find(n => n.id === currentNode!.parentId) : null
  const availableNodes = useMemo(() => {
    return getBeforeNodesInSameBranch(id)
  }, [getBeforeNodesInSameBranch, id])

  const { inputs, setInputs } = useNodeCrud<ListFilterNodeType>(id, payload)

  const { getCurrentVariableType } = useWorkflowVariables()
  const getType = useCallback((variable?: ValueSelector) => {
    const varType = getCurrentVariableType({
      parentNode: isInIteration ? iterationNode : loopNode,
      valueSelector: variable || inputs.variable || [],
      availableNodes,
      isChatMode,
      isConstant: false,
    })
    let itemVarType = VarType.string
    switch (varType) {
      case VarType.arrayNumber:
        itemVarType = VarType.number
        break
      case VarType.arrayString:
        itemVarType = VarType.string
        break
      case VarType.arrayFile:
        itemVarType = VarType.file
        break
      case VarType.arrayObject:
        itemVarType = VarType.object
        break
      default:
        itemVarType = varType
    }
    return { varType, itemVarType }
  }, [availableNodes, getCurrentVariableType, inputs.variable, isChatMode, isInIteration, iterationNode, loopNode])

  const { varType, itemVarType } = getType()

  const itemVarTypeShowName = useMemo(() => {
    if (!inputs.variable)
      return '?'
    return [(itemVarType || VarType.string).substring(0, 1).toUpperCase(), (itemVarType || VarType.string).substring(1)].join('')
  }, [inputs.variable, itemVarType])

  const hasSubVariable = [VarType.arrayFile].includes(varType)

  const handleVarChanges = useCallback((variable: ValueSelector | string) => {
    const newInputs = produce(inputs, (draft) => {
      draft.variable = variable as ValueSelector
      const { varType, itemVarType } = getType(draft.variable)
      const isFileArray = varType === VarType.arrayFile

      draft.var_type = varType
      draft.item_var_type = itemVarType
      draft.filter_by.conditions = [{
        key: (isFileArray && !draft.filter_by.conditions[0]?.key) ? 'name' : '',
        comparison_operator: getOperators(itemVarType, isFileArray ? { key: 'name' } : undefined)[0],
        value: '',
      }]
      if (isFileArray && draft.order_by.enabled && !draft.order_by.key)
        draft.order_by.key = 'name'
    })
    setInputs(newInputs)
  }, [getType, inputs, setInputs])

  const filterVar = useCallback((varPayload: Var) => {
    // Don't know the item struct of VarType.arrayObject, so not support it
    return [VarType.arrayNumber, VarType.arrayString, VarType.arrayFile].includes(varPayload.type)
  }, [])

  const handleFilterEnabledChange = useCallback((enabled: boolean) => {
    const newInputs = produce(inputs, (draft) => {
      draft.filter_by.enabled = enabled
      if (enabled && !draft.filter_by.conditions)
        draft.filter_by.conditions = []
    })
    setInputs(newInputs)
  }, [hasSubVariable, inputs, setInputs])

  const handleFilterChange = useCallback((condition: Condition) => {
    const newInputs = produce(inputs, (draft) => {
      draft.filter_by.conditions[0] = condition
    })
    setInputs(newInputs)
  }, [inputs, setInputs])

  const handleLimitChange = useCallback((limit: Limit) => {
    const newInputs = produce(inputs, (draft) => {
      draft.limit = limit
    })
    setInputs(newInputs)
  }, [inputs, setInputs])

  const handleExtractsEnabledChange = useCallback((enabled: boolean) => {
    const newInputs = produce(inputs, (draft) => {
      draft.extract_by.enabled = enabled
      if (enabled)
        draft.extract_by.serial = '1'
    })
    setInputs(newInputs)
  }, [inputs, setInputs])

  const handleExtractsChange = useCallback((value: string) => {
    const newInputs = produce(inputs, (draft) => {
      draft.extract_by.serial = value
    })
    setInputs(newInputs)
  }, [inputs, setInputs])

  const handleOrderByEnabledChange = useCallback((enabled: boolean) => {
    const newInputs = produce(inputs, (draft) => {
      draft.order_by.enabled = enabled
      if (enabled) {
        draft.order_by.value = OrderBy.ASC
        if (hasSubVariable && !draft.order_by.key)
          draft.order_by.key = 'name'
      }
    })
    setInputs(newInputs)
  }, [hasSubVariable, inputs, setInputs])

  const handleOrderByKeyChange = useCallback((key: string) => {
    const newInputs = produce(inputs, (draft) => {
      draft.order_by.key = key
    })
    setInputs(newInputs)
  }, [inputs, setInputs])

  const handleOrderByTypeChange = useCallback((type: OrderBy) => {
    return () => {
      const newInputs = produce(inputs, (draft) => {
        draft.order_by.value = type
      })
      setInputs(newInputs)
    }
  }, [inputs, setInputs])

  return {
    readOnly,
    inputs,
    filterVar,
    varType,
    itemVarType,
    itemVarTypeShowName,
    hasSubVariable,
    handleVarChanges,
    handleFilterEnabledChange,
    handleFilterChange,
    handleLimitChange,
    handleOrderByEnabledChange,
    handleOrderByKeyChange,
    handleOrderByTypeChange,
    handleExtractsEnabledChange,
    handleExtractsChange,
  }
}

export default useConfig
