import {
  ActionIcon,
  Box,
  Button,
  Card,
  Center,
  Flex,
  Loader,
  ScrollArea,
  SegmentedControl,
  Typography
} from '@tidbcloud/uikit'
import { useIntersection, useInterval } from '@tidbcloud/uikit/hooks'
import { IconLayoutLeft, IconMinus, IconRefreshCcw02 } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { useEffect, useRef, useState } from 'react'
import { useQueryClient } from 'react-query'

import { FormErrorMessage } from 'dbaas/components/Form'
import { getListNotificationsQueryKey, listNotifications, useMarkNotificationAsSeen } from 'dbaas/services/notification'
import { FormsNotification, FormsNotifications } from 'dbaas/services/notification/interface'
import useStores from 'dbaas/stores/useStores'
import { eventTracking } from 'dbaas/utils/tracking'

import { useHelpCenterContext } from '../HelpCenter/context'

import { REFETCH_INTERVAL } from './constants'
import { Empty } from './Empty'
import { MessageItem } from './MessageItem'
import { NotificationCount } from './NotificationCount'
import NotificationDetail from './NotificationDetail'
import { NotificationType } from './types'

export const NotificationCard: React.FC<React.PropsWithChildren<unknown>> = observer(() => {
  const {
    store: { tenantData }
  } = useStores()
  const [dialogOpened, setDialogOpened] = useState(false)
  const [listError, setListError] = useState<string | null>(null)
  const [markError, setMarkError] = useState<string | null>(null)
  const initialQueryParams = {
    queryType: NotificationType.UNSEEN,
    pageSize: 10
  }
  const [queryParams, setQueryParams] = useState(initialQueryParams)
  const requestIdRef = useRef(0)

  const { closeDialog, dialogOpened: helpDialogOpened } = useHelpCenterContext()
  const [markAllIsLoading, setMarkAllIsLoading] = useState(false)
  const [selectedMsg, setSelectedMsg] = useState<FormsNotification | null>(null)
  const [messageList, setMessageList] = useState<FormsNotifications>()
  const messageListRef = useRef<FormsNotifications | undefined>()

  const containerRef = useRef<HTMLDivElement>(null)
  const { ref, entry } = useIntersection({
    root: containerRef.current,
    threshold: 1
  })

  const [isDetailOpened, setIsDetailOpened] = useState(false)

  const queryClient = useQueryClient()

  useEffect(() => {
    if (helpDialogOpened && dialogOpened) {
      toggleDialog()
    }
  }, [helpDialogOpened])

  const [isFetching, setIsFetching] = useState(false)

  const getMessageList = async () => {
    setIsFetching(true)
    try {
      const curDataLen = messageListRef.current?.data?.length || 0
      const requestPage = Math.floor(curDataLen / queryParams.pageSize) + 1
      const scrapNum = curDataLen % queryParams.pageSize
      const requestPageSize = queryParams.pageSize - scrapNum

      const requestIdRefId = ++requestIdRef.current

      const res = await listNotifications(tenantData.id, queryParams.queryType, requestPage, requestPageSize)
      setListError(null)

      if (requestIdRefId !== requestIdRef.current) {
        return
      }

      const messageData = (messageListRef.current?.data || []).concat(res?.data || [])

      const data = {
        ...res,
        data: messageData
      }

      setMessageList(data)
      messageListRef.current = data
    } catch (e) {
      setListError(e as string)
    } finally {
      setIsFetching(false)
    }
  }

  const getNewMessages = async () => {
    try {
      const res = await listNotifications(tenantData.id, NotificationType.ALL, 1, queryParams.pageSize)

      if (!res?.data?.length) {
        return
      }

      const list = res.data
      const isAll = queryParams.queryType === NotificationType.ALL
      const unseenDatas = res.data.filter((item) => !item.is_read)
      // current list is empty
      if (!messageList?.data?.length) {
        const data = isAll
          ? res
          : {
              data: unseenDatas,
              has_more: false,
              count: unseenDatas.length
            }
        setMessageList(data)
        messageListRef.current = data
        return
      }

      const firstMessage = messageList?.data?.[0]
      const lastEqualMessageItemIndex = list.findIndex((item) => item.notification_id === firstMessage?.notification_id)

      if (lastEqualMessageItemIndex > -1) {
        const newList = list.slice(0, lastEqualMessageItemIndex)
        const data = isAll
          ? {
              ...messageList,
              data: [...newList, ...(messageList?.data || [])]
            }
          : {
              ...messageList,
              data: [...newList.filter((item) => !item.is_read), ...(messageList?.data || [])]
            }
        data.count = data.data.length
        setMessageList(data)
        messageListRef.current = data
      }
    } catch (_e) {}
  }
  const interval = useInterval(getNewMessages, REFETCH_INTERVAL)

  useEffect(() => {
    if (!dialogOpened) {
      interval.stop()
      resetData()
      return
    }

    interval.stop()
    getMessageList()
    interval.start()
  }, [dialogOpened, tenantData?.id, queryParams.queryType])

  useEffect(() => {
    return interval.stop
  }, [])

  useEffect(() => {
    if (!entry?.isIntersecting) {
      return
    }

    getMessageList()
  }, [entry?.isIntersecting])

  const resetData = () => {
    setMessageList(undefined)
    messageListRef.current = undefined

    setSelectedMsg(null)

    setListError(null)
    setMarkError(null)
  }

  const { mutateAsync: markAsSeen } = useMarkNotificationAsSeen()
  const handleMarkAsSeen = async (id: string, notificationId: string, isRead: boolean) => {
    try {
      if (isRead) {
        return
      }

      await markAsSeen({
        tenantId: tenantData.id,
        type: 'singal',
        notificationId: id
      })

      const data = messageList?.data || []
      const index = data.findIndex((item) => item.notification_id === notificationId)
      data[index].is_read = true

      setMessageList({
        ...messageList,
        data: [...data]
      })
      messageListRef.current = {
        ...messageList,
        data: [...data]
      }
    } catch (err) {
      setListError(err as string)
    }
  }

  const { mutateAsync: markAllAsSeen } = useMarkNotificationAsSeen()
  const handleMarkAllAsSeen = async () => {
    setMarkError(null)
    setSelectedMsg(null)

    setMarkAllIsLoading(true)
    try {
      await markAllAsSeen({
        tenantId: tenantData.id,
        type: NotificationType.ALL,
        notificationId: NotificationType.ALL
      })

      setMessageList(undefined)
      messageListRef.current = undefined
      setQueryParams({
        ...initialQueryParams,
        queryType: queryParams.queryType
      })

      setMessageList(undefined)
      messageListRef.current = undefined

      getMessageList()

      queryClient.invalidateQueries(getListNotificationsQueryKey(tenantData.id, NotificationType.UNSEEN, 1, 10))
    } catch (e) {
      setMarkError(e as string)
    } finally {
      setMarkAllIsLoading(false)
    }
  }

  const toggleDialog = () => {
    if (dialogOpened) {
      setDialogOpened(false)
      eventTracking('Close notification dialog')
    } else {
      closeDialog()
      setDialogOpened(true)
      eventTracking('Open notification dialog')
    }
  }

  const refreshList = () => {
    eventTracking('Refresh notification list')

    setSelectedMsg(null)
    setMarkError(null)

    setMessageList(undefined)
    messageListRef.current = undefined

    getMessageList()
    queryClient.invalidateQueries(getListNotificationsQueryKey(tenantData.id, NotificationType.UNSEEN, 1, 10))
  }

  const isEmpty = (messageList?.data?.length ?? 0) === 0

  const content = (
    <Box
      data-testid="notification-card"
      sx={(theme) => ({
        zIndex: 100,
        opacity: dialogOpened ? 1 : 0,
        overflow: dialogOpened ? '' : 'hidden',
        width: dialogOpened ? (selectedMsg && isDetailOpened ? 960 : 400) : 0,
        height: dialogOpened ? 'auto' : 0,
        transition: dialogOpened ? 'opacity 0.4s ease-in-out' : undefined,
        position: 'absolute',
        bottom: 24,
        left: 244,
        border: `1px solid ${theme.colors.carbon[5]}`,
        borderRadius: theme.defaultRadius,
        boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.05), 0px 8px 32px rgba(0, 0, 0, 0.05)'
      })}
    >
      <Card p={0} bd={0}>
        <Flex
          justify="space-between"
          align="center"
          h={44}
          p="sm"
          sx={(t) => ({ backgroundColor: t.colors.carbon[2] })}
        >
          <Typography variant="action-md" c="carbon.9">
            Notifications
          </Typography>
          <ActionIcon aria-label="Close Notification" onClick={toggleDialog}>
            <IconMinus size={14} strokeWidth={3} />
          </ActionIcon>
        </Flex>

        <Flex>
          <Box sx={{ flex: '0 0 400px' }}>
            <Flex mx={12} my={12} justify="space-between">
              <Flex align="center" gap={12}>
                <SegmentedControl
                  size="xs"
                  data={[
                    {
                      label: 'Unread',
                      value: NotificationType.UNSEEN
                    },
                    {
                      label: 'All',
                      value: NotificationType.ALL
                    }
                  ]}
                  defaultValue={NotificationType.UNSEEN}
                  value={queryParams.queryType}
                  onChange={(v: NotificationType) => {
                    setSelectedMsg(null)
                    setMessageList(undefined)
                    setListError(null)
                    setMarkError(null)
                    messageListRef.current = undefined

                    setQueryParams({
                      ...queryParams,
                      queryType: v
                    })
                  }}
                />

                <Button
                  variant="default"
                  size="sm"
                  aria-label="Mark all as read"
                  disabled={isEmpty}
                  data-mp-event="Notification center mark all as read clicked"
                  loading={markAllIsLoading}
                  onClick={handleMarkAllAsSeen}
                >
                  Mark all as read
                </Button>

                <ActionIcon size={32} variant="subtle" onClick={refreshList}>
                  <IconRefreshCcw02 />
                </ActionIcon>
              </Flex>
              {selectedMsg && (
                <ActionIcon size={32} variant="subtle" onClick={() => setIsDetailOpened(!isDetailOpened)}>
                  <IconLayoutLeft />
                </ActionIcon>
              )}
            </Flex>

            <ScrollArea type="never" px="12" h="calc(100vh - 230px)" ref={containerRef}>
              <Box h="100%">
                {listError && <FormErrorMessage message={listError} />}
                {markError && <FormErrorMessage message={markError} />}
                {isFetching && isEmpty && (
                  <Center pt={32}>
                    <Loader size="sm" />
                  </Center>
                )}

                {!!(!isFetching && isEmpty) && (
                  <Empty height={'calc(100vh - 230px)'} isAll={queryParams.queryType === NotificationType.ALL} />
                )}

                {(messageList?.data ?? []).map((v, idx) => (
                  <MessageItem
                    key={v.notification_id}
                    title={v.worker_flow_name}
                    subject={v.subject}
                    type={v.notification_group || ''}
                    sendAt={v.send_at}
                    isActive={v.notification_id === selectedMsg?.notification_id}
                    isRead={!!v.is_read}
                    isFirst={idx === 0}
                    handleItemClick={async () => {
                      setIsDetailOpened(true)
                      setSelectedMsg(v)

                      await handleMarkAsSeen(v.mark_as_id || '', v.notification_id || '', v.is_read || false)

                      queryClient.invalidateQueries(
                        getListNotificationsQueryKey(tenantData.id, NotificationType.UNSEEN, 1, 10)
                      )
                    }}
                  />
                ))}

                {!isFetching && messageList?.has_more && (
                  <Center>
                    <Loader ref={ref} mt={8} size="sm" />
                  </Center>
                )}
              </Box>
            </ScrollArea>
          </Box>
          {isDetailOpened && <NotificationDetail data={selectedMsg} />}
        </Flex>
      </Card>
    </Box>
  )

  return (
    <Box>
      <NotificationCount toggleDialog={toggleDialog} />
      {content}
    </Box>
  )
})
