import type { ReactElement } from 'react'
import type { BlockSelectorProps } from '@/app/components/workflow/block-selector'
import type { Node, OnSelectBlock } from '@/app/components/workflow/types'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { memo, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
import BlockSelector from '@/app/components/workflow/block-selector'
import { BlockEnum } from '@/app/components/workflow/types'
import { FlowType } from '@/types/common'
import {
  useAvailableBlocks,
  useIsChatMode,
  useNodesMetaData,
  useNodesReadOnly,
  usePanelInteractions,
} from '../hooks'
import { useHooksStore } from '../hooks-store'
import { useWorkflowStore } from '../store'
import {
  generateNewNode,
  getNodeCustomTypeByNodeDataType,
  getNodesWithSameDefaultDataType,
} from '../utils'

type AddBlockProps = {
  renderTrigger?: (open: boolean) => ReactElement
  sideOffset?: BlockSelectorProps['sideOffset']
  alignOffset?: BlockSelectorProps['alignOffset']
  onClose?: () => void
  isolateKeyboardEvents?: boolean
}
const AddBlock = ({
  renderTrigger,
  sideOffset,
  alignOffset,
  onClose,
  isolateKeyboardEvents,
}: AddBlockProps) => {
  const { t } = useTranslation()
  const store = useStoreApi()
  const workflowStore = useWorkflowStore()
  const isChatMode = useIsChatMode()
  const { nodesReadOnly } = useNodesReadOnly()
  const { handlePaneContextmenuCancel } = usePanelInteractions()
  const [open, setOpen] = useState(false)
  const { availableNextBlocks } = useAvailableBlocks(BlockEnum.Start, false)
  const { nodesMap: nodesMetaDataMap } = useNodesMetaData()
  const flowType = useHooksStore((s) => s.configsMap?.flowType)
  const showStartTab = flowType !== FlowType.ragPipeline && !isChatMode

  const handleOpenChange = useCallback(
    (open: boolean) => {
      setOpen(open)
      if (!open) (onClose ?? handlePaneContextmenuCancel)()
    },
    [handlePaneContextmenuCancel, onClose],
  )

  const handleSelect = useCallback<OnSelectBlock>(
    (type, pluginDefaultValue) => {
      const { getNodes } = store.getState()
      const { defaultValue } = nodesMetaDataMap![type]
      const nodes = getNodes()
      const nodesWithSameType = getNodesWithSameDefaultDataType(nodes, type, defaultValue)
      const { newNode } = generateNewNode({
        type: getNodeCustomTypeByNodeDataType(type),
        data: {
          ...(defaultValue as Node['data']),
          title:
            nodesWithSameType.length > 0
              ? `${defaultValue.title} ${nodesWithSameType.length + 1}`
              : defaultValue.title,
          ...pluginDefaultValue,
          _isCandidate: true,
        },
        position: {
          x: 0,
          y: 0,
        },
      })
      workflowStore.setState({
        candidateNode: newNode,
      })
    },
    [store, workflowStore, nodesMetaDataMap],
  )

  const renderTriggerElement = useCallback(
    (open: boolean) => {
      return (
        <Button
          variant="ghost"
          size="small"
          disabled={nodesReadOnly}
          focusableWhenDisabled
          className={cn(
            'size-8 p-0 text-text-tertiary hover:text-text-secondary',
            nodesReadOnly && 'text-text-disabled hover:bg-transparent hover:text-text-disabled',
            open && 'bg-state-accent-active text-text-accent',
          )}
        >
          <span aria-hidden className="i-ri-add-circle-fill size-4" />
        </Button>
      )
    },
    [nodesReadOnly],
  )

  return (
    <BlockSelector
      open={open}
      onOpenChange={handleOpenChange}
      disabled={nodesReadOnly}
      onSelect={handleSelect}
      placement="right-start"
      sideOffset={sideOffset ?? 4}
      alignOffset={alignOffset ?? -8}
      trigger={renderTrigger || renderTriggerElement}
      triggerAriaLabel={t(($) => $['common.addBlock'], { ns: 'workflow' })}
      triggerTooltip={t(($) => $['common.addBlock'], { ns: 'workflow' })}
      popupClassName="min-w-[256px]!"
      availableBlocksTypes={availableNextBlocks}
      showStartTab={showStartTab}
      isolateKeyboardEvents={isolateKeyboardEvents}
    />
  )
}

export default memo(AddBlock)
