<template>
  <SelectPopover
    v-if="node.nodeType === 'add'"
    v-bind="getAddOptions(node.parentType!)"
    @confirm="(val) => val?.length && emit('add', node.parentId!, node.parentType!, val)"
  >
    <div
      :style="addBtnStyle"
      class="w-120px h-24px text-#fff grid place-items-center rounded-4px hover:box-shadow-md cursor-pointer"
    >
      <LuPlus />
    </div>
  </SelectPopover>

  <div
    v-else-if="node.nodeType === 'root'"
    class="w-200px h-40px bg-#0068ff text-#fff grid place-items-center rounded-4px"
  >
    {{ node.name }}
  </div>

  <div
    v-else-if="node.nodeType === 'event'"
    class="w-200px h-40px bg-#4b85e2 text-#fff grid place-items-center rounded-4px relative group"
  >
    <div class="absolute top-4px right-4px gap-4px hidden group-hover:flex">
      <SelectPopover
        v-bind="getAddOptions('root')"
        :multiple="false"
        :selected="node.value"
        @confirm="handleChangeEvent"
      >
        <div class="bg-[rgba(0,0,0,.5)] p-2px cursor-pointer">
          <LuPencilLine size="12px" />
        </div>
      </SelectPopover>

      <div class="bg-[rgba(0,0,0,.5)] p-2px cursor-pointer" @click="emit('delete', node.id)">
        <LuTrash2 size="12px" class="text-accent-red" />
      </div>
    </div>
    {{ node.name }}
  </div>

  <div
    v-else-if="node.nodeType === 'target'"
    class="w-200px h-40px bg-#4B9889 text-#fff grid place-items-center rounded-4px relative group"
  >
    <div class="absolute top-4px right-4px gap-4px hidden group-hover:flex">
      <SelectPopover
        v-bind="getAddOptions('event')"
        :multiple="false"
        :selected="node.value"
        @confirm="handleChangeTarget"
      >
        <div class="bg-[rgba(0,0,0,.5)] p-2px cursor-pointer">
          <LuPencilLine size="12px" />
        </div>
      </SelectPopover>

      <div class="bg-[rgba(0,0,0,.5)] p-2px cursor-pointer" @click="emit('delete', node.id)">
        <LuTrash2 size="12px" class="text-accent-red" />
      </div>
    </div>
    {{ node.name }}
  </div>

  <ActionNode
    v-else-if="node.nodeType === 'action'"
    :node="node"
    :mindMap="mindMap"
    :mindMapNode="mindMapNode"
    @delete="() => emit('delete', node.id)"
    @update="(val) => emit('change', val)"
  />
</template>

<script setup lang="ts">
import type { NodeItemType, OptionType } from './type'
import type { WidgetEventTargetData } from '@/types/event'

import { computed } from 'vue'
import { klona } from 'klona'
import { v4 } from 'uuid'
import { LuPencilLine, LuPlus, LuTrash2 } from 'vue-icons-plus/lu'

import SelectPopover from './SelectPopover.vue'
import ActionNode from './ActionNode.vue'
import {
  getActionOptions,
  getBuiltinActionData,
  getEventOptions,
  getTargetOptions,
  isBuiltinTarget
} from './config'
import { useProjectStore } from '@/store/modules/project'
import MindMap from 'simple-mind-map'

const emit = defineEmits<{
  (e: 'add', parentId: string, parentType: string, value: OptionType[]): void
  (e: 'change', node: NodeItemType): void
  (e: 'delete', nodeId: string): void
}>()

const props = defineProps<{
  node: NodeItemType
  mindMap?: MindMap
  mindMapNode?: any
}>()

const projectStore = useProjectStore()

const getRootChildren = () =>
  ((projectStore.activeWidget?.events || []) as unknown as NodeItemType[]).filter(Boolean)

const addBtnStyle = computed(() => {
  const node = props.node
  return {
    backgroundColor:
      node.parentType === 'root' ? '#4b85e2' : node.parentType === 'event' ? '#4B9889' : '#4EB2BF'
  }
})

const findParentChildren = (parentId: string) => {
  const rootChildren = getRootChildren()

  if (props.node.parentType === 'root') {
    return rootChildren
  }

  for (const eventNode of rootChildren) {
    if (eventNode.id === parentId) {
      return eventNode.children || []
    }

    const targetNode = (eventNode.children || []).find((item) => item.id === parentId)
    if (targetNode) {
      return targetNode.children || []
    }
  }

  return []
}

const findParentNode = (parentId: string) => {
  for (const eventNode of getRootChildren()) {
    if (eventNode.id === parentId) return eventNode

    const targetNode = (eventNode.children || []).find((item) => item.id === parentId)
    if (targetNode) return targetNode
  }

  return
}

const findNodeById = (nodeId?: string) => {
  if (!nodeId) return

  for (const eventNode of getRootChildren()) {
    if (eventNode.id === nodeId) return eventNode

    for (const targetNode of eventNode.children || []) {
      if (targetNode.id === nodeId) return targetNode

      const actionNode = (targetNode.children || []).find((item) => item.id === nodeId)
      if (actionNode) return actionNode
    }
  }

  return
}

const getSiblingValues = (parentType: 'root' | 'event' | 'target') => {
  if (parentType === 'target') return []

  const siblings = props.node.parentId ? findParentChildren(props.node.parentId) : []
  return siblings
    .filter((item) => item.id !== props.node.id && item.nodeType !== 'add')
    .map((item) => item.value)
    .filter(Boolean) as string[]
}

const withDisabled = (options: OptionType[], selectedValues: string[]) =>
  options.map((item) => ({
    ...item,
    disabled: selectedValues.includes(item.value)
  }))

const getSelectedActionValues = () => {
  const parentNode =
    props.node.nodeType === 'target'
      ? props.node
      : props.node.nodeType === 'add' && props.node.parentType === 'target'
        ? findParentNode(props.node.parentId!)
        : undefined

  return (parentNode?.children || [])
    .filter((item) => item.nodeType === 'action')
    .map((item) => item.config?.data?.actionData?.actionKey || item.value)
    .filter(Boolean) as string[]
}

const getTargetDataForActionOptions = () => {
  if (props.node.nodeType === 'target') {
    return props.node.config?.data as WidgetEventTargetData | undefined
  }

  if (props.node.nodeType === 'add' && props.node.parentType === 'target') {
    return findNodeById(props.node.parentId)?.config?.data as WidgetEventTargetData | undefined
  }

  return
}

const getAddOptions = (type: NonNullable<NodeItemType['parentType']>) => {
  const widget = projectStore.activeWidget

  switch (type) {
    case 'root':
      return {
        title: '事件',
        multiple: true,
        options: withDisabled(getEventOptions(widget?.type), getSiblingValues('root'))
      }

    case 'event':
      return {
        title: '目标',
        multiple: true,
        options: withDisabled(getTargetOptions(projectStore.project), getSiblingValues('event'))
      }

    case 'target': {
      const targetData = getTargetDataForActionOptions()
      return {
        title: '动作',
        multiple: true,
        options: withDisabled(
          getActionOptions(targetData, projectStore.project, projectStore.activePage?.id),
          getSelectedActionValues()
        )
      }
    }

    default:
      return {
        title: '',
        multiple: false,
        options: [] as OptionType[]
      }
  }
}

const handleChangeEvent = (val?: OptionType[]) => {
  const item = val?.[0]
  if (!item || props.node.nodeType !== 'event') return

  emit('change', {
    ...props.node,
    name: item.label,
    value: item.value,
    config: {
      ...props.node.config,
      data: item.data
    }
  })
}

const handleChangeTarget = (val?: OptionType[]) => {
  const item = val?.[0]
  if (!item || props.node.nodeType !== 'target') return

  const targetData = item.data as WidgetEventTargetData
  const builtinActionData = getBuiltinActionData(targetData)
  const actionOptions = getActionOptions(
    targetData,
    projectStore.project,
    projectStore.activePage?.id
  )
  const actionOption = builtinActionData
    ? actionOptions.find((option) => option.value === builtinActionData.actionKey)
    : undefined

  const currentActionChild = props.node.children?.find((child) => child.nodeType === 'action')
  const nextChildren: NodeItemType[] =
    targetData.kind === 'custom_code'
      ? [
          {
            id: currentActionChild?.id || v4(),
            name: '自定义代码',
            nodeType: 'action',
            value: currentActionChild?.value ?? '',
            config: {
              data: {
                kind: 'custom_code',
                targetData
              }
            }
          }
        ]
      : actionOption
        ? [
            {
              id: currentActionChild?.id || v4(),
              name: actionOption.label,
              nodeType: 'action',
              value: klona(actionOption.defaultValue),
              config: {
                data: {
                  targetData,
                  actionData: actionOption.data,
                  kind: targetData.kind
                }
              }
            }
          ]
        : []

  emit('change', {
    ...props.node,
    name: item.label,
    value: item.value,
    config: {
      ...props.node.config,
      data: item.data
    },
    children: isBuiltinTarget(targetData) ? nextChildren : []
  })
}
</script>
