"use client"

import {
  ActionIcon,
  Box,
  Button,
  Flex,
  Group,
  ScrollArea,
  Text,
  TextInput,
} from "@mantine/core"
import { showNotification } from "@mantine/notifications"
import {
  IconChevronLeft,
  IconChevronRight,
  IconChevronsLeft,
  IconChevronsRight,
  IconFlag,
  IconPlayerTrackNext,
  IconPlayerTrackNextFilled,
  IconPlayerTrackPrev,
  IconPlayerTrackPrevFilled,
} from "@tabler/icons-react"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { Project } from "../api/project/typing"
import { Task } from "../api/task/typing"
import { LabelState } from "../LabelNossr"
import { useLabelStore } from "../store"
import { usePermissionStore } from "../store/auth"
import { useBottomToolsStore } from "../useBottomToolsStore"
import { useDescToolsStore } from "../useDescToolsStore"
import { useKeyboardStore } from "../useKeyBoardStore"
import { useOtherToolsStore } from "../useOtherToolsStore"
import { usePaperStore } from "../usePaperStore"
import { useLabelTimeStore } from "../useTimerStore"
import { useTopToolsStore } from "../useTopToolsStore"
import { getSubAttrStatus } from "../util"
import { safeClone } from "../utils/clone"
import CustomModal from "./CustomModal"
import MetaOperationTool from "./MetaOperationTool"

interface BottomToolsComponentProps {
  labelState: LabelState
  taskDetail?: Task.DataProps
  projectDetail?: Project.DetailResponse
  renderPolygons: (
    operationId: string,
    imageData: Map<string, [number, any[], any, any[]][]> | undefined
  ) => void | null
}

type PlaybackDirection = "backward" | "forward"

const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
  const { labelState, projectDetail, taskDetail, renderPolygons } = props

  const itemRefs = useRef<Record<string, HTMLDivElement | null>>({})

  const storeLabel = useLabelStore((state) => state.label)
  const setLabel = useLabelStore((state) => state.setLabel)
  const pushStateStack = useLabelStore((state) => state.pushStateStack)
  const {
    inputNumber,
    setInputNumber,
    activeImage,
    pendingImage,
    frameSwitchStatus,
    setActiveImage,
    setActiveImageId,
    requestFrameSwitch,
    onlyKeyFrame,
    setOnlyKeyFrame,
    keyFrameData,
    setKeyFrameData,
    selectedItems,
    setSelectedItems,
  } = useBottomToolsStore()
  const { metaOperation } = useDescToolsStore()
  const { ctrl: ctrlKey, shift: shiftKey } = useKeyboardStore()

  const [open, setOpen] = useState(false)
  const [conflictOpen, setConflictOpen] = useState(false)
  const [playDirection, setPlayDirection] = useState<PlaybackDirection | null>(
    null
  )
  const allKeys = useMemo(() => Array.from(labelState.keys()), [labelState])
  const isShowKeyFrame =
    projectDetail &&
    [5, 6].includes(projectDetail.label_type) &&
    allKeys.some(
      (k) => keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
    ) &&
    onlyKeyFrame
  const currentKeys = useMemo(
    () =>
      allKeys.filter((k) =>
        isShowKeyFrame
          ? keyFrameData.has(k) && keyFrameData.get(k)?.key_frame
          : true
      ),
    [allKeys, isShowKeyFrame, keyFrameData]
  )
  // 上一帧图片id
  const previousFrameKey = useMemo(() => {
    const currentIndex = currentKeys.findIndex((k) => k === activeImage)
    return currentIndex > 0 ? currentKeys[currentIndex - 1] : ""
  }, [activeImage, currentKeys])

  useEffect(() => {
    if (!currentKeys.length) {
      setActiveImage("")
      setInputNumber("")
      setActiveImageId(0)
      return
    }

    const currentIndex = activeImage
      ? currentKeys.findIndex((k) => k === activeImage)
      : -1
    if (currentIndex >= 0) {
      setInputNumber(String(currentIndex + 1))
      const absoluteIndex = allKeys.findIndex((k) => k === activeImage)
      setActiveImageId(absoluteIndex >= 0 ? absoluteIndex + 1 : 0)
      return
    }

    const firstKey = currentKeys[0]
    const absoluteIndex = allKeys.findIndex((k) => k === firstKey)
    setActiveImage(firstKey)
    setInputNumber("1")
    setActiveImageId(absoluteIndex >= 0 ? absoluteIndex + 1 : 1)
  }, [
    activeImage,
    allKeys,
    currentKeys,
    setActiveImage,
    setActiveImageId,
    setInputNumber,
  ])

  const scrollToIndex = useCallback(
    (index: number) => {
      const key = currentKeys[index]
      if (key && itemRefs.current[key]) {
        itemRefs.current[key]?.scrollIntoView({
          behavior: "smooth",
          block: "nearest",
          inline: "center",
        })
      }
    },
    [currentKeys]
  )

  const closeRightContext = useCallback(() => {
    useOtherToolsStore.getState().setShowContextMenu({
      showContextMenu: false,
      position: {
        top: 0,
        left: 0,
      },
      imageId: useOtherToolsStore.getState().imageId,
      operationId: useOtherToolsStore.getState().operationId,
      id: useOtherToolsStore.getState().id,
    })
  }, [])

  const goToVisibleIndex = useCallback(
    (index: number) => {
      if (index < 0 || index >= currentKeys.length) return false

      const nextImage = currentKeys[index]
      if (!nextImage) return false

      scrollToIndex(index)
      if (
        frameSwitchStatus === "preparing" &&
        pendingImage &&
        pendingImage === nextImage
      ) {
        closeRightContext()
        return true
      }
      if (nextImage !== activeImage) {
        requestFrameSwitch(nextImage)
      }
      closeRightContext()

      return true
    },
    [
      activeImage,
      closeRightContext,
      currentKeys,
      frameSwitchStatus,
      pendingImage,
      requestFrameSwitch,
      scrollToIndex,
    ]
  )

  const stepVisibleImage = useCallback(
    (direction: PlaybackDirection) => {
      const currentIndex = currentKeys.findIndex((k) => k === activeImage)
      if (currentIndex < 0) return false

      const nextIndex =
        direction === "backward" ? currentIndex - 1 : currentIndex + 1

      return goToVisibleIndex(nextIndex)
    },
    [activeImage, currentKeys, goToVisibleIndex]
  )

  const togglePlayback = useCallback(
    (direction: PlaybackDirection) => {
      if (currentKeys.length < 2) return
      setPlayDirection((current) => (current === direction ? null : direction))
    },
    [currentKeys.length]
  )

  useEffect(() => {
    if (!playDirection) return
    if (currentKeys.length < 2) {
      setPlayDirection(null)
      return
    }

    const timer = window.setInterval(() => {
      const moved = stepVisibleImage(playDirection)
      if (!moved) {
        setPlayDirection(null)
      }
    }, 1000)

    return () => {
      window.clearInterval(timer)
    }
  }, [currentKeys.length, playDirection, stepVisibleImage])

  const getOperationSchema = useCallback(
    (operationId: string) => {
      return (
        projectDetail?.label_schema_list?.find(
          (item) => item.category_id === +operationId
        ) || null
      )
    },
    [projectDetail?.label_schema_list]
  )

  // child 是否存在不符合要求的子属性
  const getOperationSubAttrStatus = useCallback(
    (imageId: string) => {
      let parentBool = labelState.get(imageId)?.some((operationId) => {
        let bool = storeLabel
          .get(imageId)
          ?.get(operationId)
          ?.some((child) => {
            return getSubAttrStatus(
              getOperationSchema(operationId),
              child[2]?.sub_attributes
            )
          })
        return bool
      })
      return parentBool
    },
    [getOperationSchema, labelState, storeLabel]
  )

  // 获取上一帧和当前帧标注对象数组
  const getLabelObjectsData = () => {
    let map1 =
      storeLabel.get(previousFrameKey) ??
      new Map<string, [number, any[], any, any[]][]>()
    let arr1 = []
    for (const value of map1.values()) arr1.push(...value)
    let map2 =
      storeLabel.get(activeImage) ??
      new Map<string, [number, any[], any, any[]][]>()
    let arr2 = []
    for (const value of map2.values()) arr2.push(...value)
    return [arr1, arr2]
  }

  // 上一帧是否有文本描述
  const prevFramehasTextDesc = () => {
    if (projectDetail?.label_type === 5) {
      let flag = false
      const currentDescData = safeClone(useDescToolsStore.getState().descData)
      const prevData1 = currentDescData.get(previousFrameKey) ?? new Map()
      flag = prevData1.size ? true : false
      if (flag) return true
      const currentMetaData = safeClone(useDescToolsStore.getState().metaData)
      const prevData2 = currentMetaData.get(previousFrameKey) ?? []
      flag = prevData2.length ? true : false
      return flag
    } else if (projectDetail?.label_type === 6) {
      const currentQaData = safeClone(useDescToolsStore.getState().qaData)
      const prevData = currentQaData.get(previousFrameKey) ?? {}
      return JSON.stringify(prevData) === "{}" ? false : true
    }
    return false
  }

  // 更新数据绘制
  const updateDataRender = (
    data: Map<string, [number, any[], any, any[]][]>
  ) => {
    if (!renderPolygons) return
    const list = usePaperStore.getState().group?.getItems({})
    list?.forEach((item) => {
      if (item.data.id) {
        item.remove()
      }
    })

    useTopToolsStore.getState().objectOperations?.forEach((item) => {
      renderPolygons(item.category_id.toString(), data)
    })
  }

  // 大语言标注继承上一帧时数据处理
  const handleLLMData = () => {
    // LLM
    if (projectDetail?.label_type === 5) {
      // 文本描述
      const currentDescData = safeClone(useDescToolsStore.getState().descData)
      const prevData1 = currentDescData.get(previousFrameKey) ?? new Map()
      currentDescData.set(activeImage, prevData1)
      useDescToolsStore.getState().setDescData(currentDescData)
      useDescToolsStore.getState().updateFlag(true)
      // 元操作序列
      const currentMetaData = safeClone(useDescToolsStore.getState().metaData)
      const prevData2 = currentMetaData.get(previousFrameKey) ?? []
      currentMetaData.set(activeImage, prevData2)
      useDescToolsStore.getState().setMetaData(currentMetaData)
    }
    // QA
    if (projectDetail?.label_type === 6) {
      const currentQaData = safeClone(useDescToolsStore.getState().qaData)
      const prevData = currentQaData.get(previousFrameKey) ?? {}
      currentQaData.set(activeImage, prevData)
      useDescToolsStore.getState().setQaData(currentQaData)
      useDescToolsStore.getState().updateFlag(true)
    }
  }

  // 继承上一帧的数据
  const handleCopyData = () => {
    // 图形绘制
    const [perviousData] = getLabelObjectsData()
    let map =
      storeLabel.get(activeImage) ??
      new Map<string, [number, any[], any, any[]][]>()
    perviousData.forEach((item) => {
      const id = item[2].category_id.toString()
      if (map.has(id)) {
        let data = map.get(id)
        data!.push(item)
        map.set(id, data as any)
      } else {
        map.set(id, [item])
      }
    })
    let updateData = new Map()
    for (const [key, value] of storeLabel) {
      if (key === activeImage) updateData.set(key, map)
      else updateData.set(key, value)
    }
    if (!storeLabel.has(activeImage)) updateData.set(activeImage, map)
    updateDataRender(updateData.get(activeImage)!)
    setLabel(updateData)
    pushStateStack(updateData)
    handleLLMData()
    setOpen(false)
  }

  const handleCopyConflictData = (flag: boolean) => {
    const [perviousData, nowData] = getLabelObjectsData()

    let map = new Map<string, [number, any[], any, any[]][]>()
    let arr = flag
      ? [
          ...perviousData,
          ...nowData.filter(
            (item) => !perviousData.map((p) => p[0]).includes(item[0])
          ),
        ]
      : [
          ...perviousData.filter(
            (item) => !nowData.map((d) => d[0]).includes(item[0])
          ),
          ...nowData,
        ]
    arr.forEach((item) => {
      const id = item[2].category_id.toString()
      if (map.has(id)) {
        let data = map.get(id)
        data!.push(item)
        map.set(id, data as any)
      } else {
        map.set(id, [item])
      }
    })
    let updateData = new Map()
    for (const [key, value] of storeLabel) {
      if (key === activeImage) updateData.set(key, map)
      else updateData.set(key, value)
    }
    updateDataRender(updateData.get(activeImage)!)
    setLabel(updateData)
    pushStateStack(updateData)
    handleLLMData()
    setConflictOpen(false)
  }

  return (
    <Box w="100%" h="100%">
      {projectDetail?.label_type === 5 && metaOperation ? (
        <Flex
          h={48}
          w="100%"
          align="center"
          style={{
            borderTop:
              "1px solid light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
          }}>
          <Box px="md" fw={600}>
            元操作序列
          </Box>
          <Box
            style={{
              maxWidth: "calc(100% - 98px)",
              overflowX: "auto",
              height: "100%",
            }}>
            <MetaOperationTool />
          </Box>
        </Flex>
      ) : null}
      <Box
        h={28}
        style={{
          border:
            "1px solid light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
        }}>
        <Flex justify="space-between" align="center" h="100%">
          <Box flex={1} />
          <Flex flex={1} align="center" justify="center" gap="xs">
            <ActionIcon
              variant="subtle"
              color="gray"
              size="sm"
              onClick={() => {
                goToVisibleIndex(0)
              }}>
              <IconChevronsLeft size={16} />
            </ActionIcon>
            <ActionIcon
              variant={playDirection === "backward" ? "filled" : "subtle"}
              color={playDirection === "backward" ? "blue" : "gray"}
              size="sm"
              disabled={currentKeys.length < 2}
              aria-label="向前播放"
              title="向前播放"
              onClick={() => {
                togglePlayback("backward")
              }}>
              {playDirection === "backward" ? (
                <IconPlayerTrackPrevFilled size={16} />
              ) : (
                <IconPlayerTrackPrev size={16} />
              )}
            </ActionIcon>
            <ActionIcon
              variant="subtle"
              color="gray"
              size="sm"
              onClick={() => {
                stepVisibleImage("backward")
              }}>
              <IconChevronLeft size={16} />
            </ActionIcon>
            <TextInput
              w={42}
              size="compact-xs"
              radius={"sm"}
              styles={{
                input: {
                  height: 20,
                  fontSize: "14px",
                  textAlign: "center",
                },
              }}
              value={inputNumber}
              onChange={(e) => {
                setInputNumber(e.target.value)
              }}
              onKeyDown={(e) => {
                if (e.key === "Enter") {
                  const targetIndex = Number(inputNumber)
                  if (
                    Number.isInteger(targetIndex) &&
                    targetIndex >= 1 &&
                    targetIndex <= currentKeys.length
                  ) {
                    goToVisibleIndex(targetIndex - 1)
                  } else {
                    const currentIndex = currentKeys.findIndex(
                      (key) => key === activeImage
                    )
                    setInputNumber(
                      currentIndex >= 0 ? String(currentIndex + 1) : ""
                    )
                  }
                }
              }}
            />
            <Text size="xs">/ {currentKeys.length}</Text>
            <ActionIcon
              variant="subtle"
              color="gray"
              size="sm"
              onClick={() => {
                stepVisibleImage("forward")
              }}>
              <IconChevronRight size={16} />
            </ActionIcon>
            <ActionIcon
              variant={playDirection === "forward" ? "filled" : "subtle"}
              color={playDirection === "forward" ? "blue" : "gray"}
              size="sm"
              disabled={currentKeys.length < 2}
              aria-label="向后播放"
              title="向后播放"
              onClick={() => {
                togglePlayback("forward")
              }}>
              {playDirection === "forward" ? (
                <IconPlayerTrackNextFilled size={16} />
              ) : (
                <IconPlayerTrackNext size={16} />
              )}
            </ActionIcon>
            <ActionIcon
              variant="subtle"
              color="gray"
              size="sm"
              onClick={() => {
                goToVisibleIndex(currentKeys.length - 1)
              }}>
              <IconChevronsRight size={16} />
            </ActionIcon>
          </Flex>
          <Flex flex={1} justify="flex-end" align="center" gap="xs" pr="xs">
            {projectDetail && [5, 6].includes(projectDetail.label_type) ? (
              <>
                {isShowKeyFrame ? (
                  <Button
                    variant="subtle"
                    size="compact-xs"
                    radius={"sm"}
                    onClick={() => {
                      const [firstKey] = labelState.keys()
                      setActiveImage(firstKey)
                      setInputNumber("1")
                      setActiveImageId(1)
                      setOnlyKeyFrame(false)
                    }}>
                    查看全部帧
                  </Button>
                ) : (
                  <Button
                    variant="subtle"
                    size="compact-xs"
                    radius={"sm"}
                    onClick={() => {
                      let flag = false
                      keyFrameData
                        .entries()
                        .forEach(
                          ([key, value]: [string, { key_frame?: boolean }]) => {
                            if (labelState.has(key) && value?.key_frame)
                              flag = true
                          }
                        )
                      if (!flag) {
                        showNotification({
                          title: "警告",
                          message: "当前任务暂未设定关键帧",
                          color: "yellow",
                        })
                        return
                      }
                      const filterKeys = Array.from(labelState.keys()).filter(
                        (key) =>
                          keyFrameData.has(key) &&
                          keyFrameData.get(key)?.key_frame
                      )
                      setActiveImage(filterKeys[0])
                      setInputNumber("1")
                      setActiveImageId(1)
                      setOnlyKeyFrame(true)
                    }}>
                    仅保留关键帧
                  </Button>
                )}
                {keyFrameData.has(activeImage) &&
                keyFrameData.get(activeImage)?.key_frame ? (
                  <Button
                    variant="subtle"
                    size="compact-xs"
                    radius={"sm"}
                    disabled={
                      !taskDetail ||
                      ![2, 4, 6].includes(taskDetail.label_status) ||
                      useTopToolsStore.getState().isView
                    }
                    onClick={() => {
                      if (!taskDetail) return
                      const data = keyFrameData.get(activeImage)!
                      data.key_frame = false
                      if (taskDetail.label_status === 2) {
                        data.label1_ts = 0
                        data.label1_uid = 0
                      } else if (taskDetail.label_status === 4) {
                        data.review1_ts = 0
                        data.review1_uid = 0
                      } else if (taskDetail.label_status === 6) {
                        data.review2_ts = 0
                        data.review2_uid = 0
                      }
                      setKeyFrameData(activeImage, data)
                    }}>
                    设定为非关键帧
                  </Button>
                ) : (
                  <Button
                    variant="subtle"
                    size="compact-xs"
                    radius={"sm"}
                    disabled={
                      !taskDetail ||
                      ![2, 4, 6].includes(taskDetail.label_status) ||
                      useTopToolsStore.getState().isView
                    }
                    onClick={() => {
                      if (!taskDetail) return
                      const data = keyFrameData.get(activeImage) || {
                        key_frame: false,
                        label1_ts: 0,
                        label1_uid: 0,
                        review1_ts: 0,
                        review1_uid: 0,
                        review2_ts: 0,
                        review2_uid: 0,
                      }
                      data.key_frame = true
                      const currentLabelTime =
                        useLabelTimeStore.getState().labelTime
                      if (taskDetail.label_status === 2) {
                        data.label1_ts = currentLabelTime.label
                        data.label1_uid = usePermissionStore.getState().user_id
                      } else if (taskDetail.label_status === 4) {
                        data.review1_ts = currentLabelTime.review1
                        data.review1_uid = usePermissionStore.getState().user_id
                      } else if (taskDetail.label_status === 6) {
                        data.review2_ts = currentLabelTime.review2
                        data.review2_uid = usePermissionStore.getState().user_id
                      }
                      setKeyFrameData(activeImage, data)
                    }}>
                    设定为关键帧
                  </Button>
                )}
              </>
            ) : null}
            <Button
              variant="subtle"
              size="compact-xs"
              radius={"sm"}
              disabled={
                (!getLabelObjectsData()[0].length && !prevFramehasTextDesc()) ||
                useTopToolsStore.getState().isView
              }
              onClick={() => {
                const [perviousData, nowData] = getLabelObjectsData()
                // check merge conflict
                if (!nowData.length) {
                  setOpen(true)
                  return
                }
                let flag = false
                const perviousIds = perviousData.map((item) => item[0])
                const nowIds = nowData.map((item) => item[0])
                perviousIds.forEach((id) => {
                  if (nowIds.includes(id)) flag = true
                })
                if (flag) setConflictOpen(true)
                else setOpen(true)
              }}>
              继承上一帧
            </Button>
          </Flex>
        </Flex>
      </Box>
      <Box
        h={
          projectDetail?.label_type === 5 && metaOperation
            ? "calc(100% - 76px)"
            : "calc(100% - 28px)"
        }
        style={{
          border:
            "1px solid light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
        }}>
        <ScrollArea.Autosize
          mah="100%"
          h={"100%"}
          w="100%"
          scrollbars="x"
          style={{ display: "flex", alignItems: "center" }}>
          <Flex gap={0} w="max-content" h="100%" align="center">
            {currentKeys.map((key, index) => {
              const isActive = activeImage === key
              const isPending =
                frameSwitchStatus === "preparing" &&
                pendingImage === key &&
                !isActive
              const isSelected =
                selectedItems.length && selectedItems.includes(key)
              const isHighlight = isActive || isSelected

              let textColor = "inherit"
              if (getOperationSubAttrStatus(key)) {
                textColor = "red"
              } else if (isHighlight) {
                textColor = "white"
              }

              return (
                <Box
                  key={key}
                  w={120}
                  h="100%"
                  p={6}
                  ref={(el) => {
                    itemRefs.current[key] = el
                  }}>
                  <Box
                    style={{
                      border: isHighlight
                        ? "1px solid var(--mantine-color-blue-6)"
                        : isPending
                          ? "1px dashed var(--mantine-color-blue-4)"
                          : "1px solid var(--mantine-color-gray-3)",
                      borderRadius: "6px",
                      backgroundColor: isHighlight
                        ? "var(--mantine-color-blue-6)"
                        : isPending
                          ? "rgba(34, 139, 230, 0.08)"
                          : undefined,
                      color: isHighlight ? "white" : undefined,
                      opacity: isPending ? 0.85 : 1,
                      cursor: "pointer",
                      position: "relative",
                      display: "flex",
                      alignItems: "center",
                      justifyContent: "center",
                    }}
                    onClick={(e) => {
                      if (e.shiftKey) {
                        e.preventDefault()
                      }
                      if (ctrlKey || shiftKey) {
                        setSelectedItems(key, ctrlKey, shiftKey)
                      } else {
                        goToVisibleIndex(index)
                        setSelectedItems("", false, false)
                      }
                      closeRightContext()
                    }}>
                    {keyFrameData.has(key) &&
                    keyFrameData.get(key)?.key_frame ? (
                      <Box
                        pos="absolute"
                        top={0}
                        right={8}
                        c={isHighlight ? "white" : "gray"}>
                        <IconFlag size={16} />
                      </Box>
                    ) : null}
                    <Text fw={600} size="lg" c={textColor}>
                      {index + 1}
                    </Text>
                  </Box>
                </Box>
              )
            })}
          </Flex>
        </ScrollArea.Autosize>
      </Box>
      <CustomModal
        title="提示"
        open={open}
        centered
        onCancel={() => {
          setOpen(false)
        }}
        onOk={handleCopyData}>
        确实要复制上一帧数据吗？
      </CustomModal>
      <CustomModal
        title="提示"
        open={conflictOpen}
        centered
        onCancel={() => {
          setConflictOpen(false)
        }}
        footer={
          <Group justify="flex-end" gap="xs" mt="md">
            <Button
              variant="default"
              onClick={() => {
                setConflictOpen(false)
              }}>
              取消
            </Button>
            <Button
              color="red"
              onClick={() => {
                handleCopyConflictData(false)
              }}>
              否
            </Button>
            <Button
              onClick={() => {
                handleCopyConflictData(true)
              }}>
              是
            </Button>
          </Group>
        }>
        与上一帧数据存在冲突，是否要复制上一帧数据并覆盖吗？
      </CustomModal>
    </Box>
  )
}

export default BottomTools
