import Avatar from '@/components/ui/Avatar'
import Badge from '@/components/ui/Badge'
import Button from '@/components/ui/Button'
import Dropdown from '@/components/ui/Dropdown'
import ScrollBar from '@/components/ui/ScrollBar'
import Spinner from '@/components/ui/Spinner'
import Tooltip from '@/components/ui/Tooltip'
import { AVATAR_URL } from '@/constants/app.constant'
import NotificationChannels from '@/constants/notification-channel.enum'
import { ROUTES_ENUM } from '@/constants/route.constant'
import {
  getList,
  updateRead,
  updateReadAll,
  updateSent,
} from '@/proxy/notification/notification.service'
import { useStoreState } from '@/store'
import withHeaderItem from '@/utils/hoc/withHeaderItem'
import { useLocalization } from '@/utils/hooks/useLocalization'
import useResponsive from '@/utils/hooks/useResponsive'
import useThemeClass from '@/utils/hooks/useThemeClass'
import isLastChild from '@/utils/isLastChild'
import classNames from 'classnames'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { useCallback, useEffect, useRef, useState } from 'react'
import { HiOutlineBell, HiOutlineMailOpen } from 'react-icons/hi'
import { Link } from 'react-router-dom'
import { Notification as Notify, toast } from '../ui'

dayjs.extend(relativeTime)

type NotificationList = {
  id: string
  creatorId: string
  tenantId?: string
  notificationType: string
  message: string
  date: string
  readed: boolean
}

const notificationHeight = 'h-72'
const notificationInterval = 120000

const notificationTypeAvatar = (creatorId: string, tenantId?: string) => {
  return <Avatar shape="circle" src={AVATAR_URL(creatorId, tenantId)} />
}

const NotificationToggle = ({
  className,
  unreadCount,
}: {
  className?: string
  unreadCount: number
}) => {
  return (
    <div className={classNames('text-2xl', className)}>
      {unreadCount > 0 ? (
        <Badge
          badgeStyle={{ top: '3px', right: '6px' }}
          content={unreadCount}
          innerClass="py-0 px-1"
        >
          <HiOutlineBell />
        </Badge>
      ) : (
        <HiOutlineBell />
      )}
    </div>
  )
}

const _Notification = ({ className }: { className?: string }) => {
  const { translate } = useLocalization()

  const [notificationList, setNotificationList] = useState<NotificationList[]>([])
  const [unreadNotificationCount, setUnreadNotificationCount] = useState(0)
  const [noResult, setNoResult] = useState(false)
  const [loading, setLoading] = useState(false)

  const [toastNotificationList, setToastNotificationList] = useState<string[]>([])
  const toastNotificationInterval = useRef<NodeJS.Timer>()
  const [desktopNotificationList, setDesktopNotificationList] = useState<string[]>([])
  const desktopNotificationInterval = useRef<NodeJS.Timer>()

  const { bgTheme } = useThemeClass()
  const { larger } = useResponsive()

  const direction = useStoreState((state) => state.theme.direction)
  const tabHasFocus = useStoreState((a) => a.base.common.tabHasFocus)

  const getReactNotificationCount = useCallback(async () => {
    const resp = await getList({
      channels: [NotificationChannels.UiActivity],
      isListRequest: false,
      isRead: false,
      maxResultCount: 1,
    })
    setUnreadNotificationCount(resp.data?.totalCount ?? 0)
  }, [setUnreadNotificationCount])

  useEffect(() => {
    getReactNotificationCount()
    var intervalId = setInterval(() => {
      getReactNotificationCount()
    }, notificationInterval)

    return () => {
      clearInterval(intervalId)
    }
  }, [getReactNotificationCount])

  const onNotificationOpen = useCallback(async () => {
    const currentUnread = notificationList.filter((a) => !a.readed).length
    if (currentUnread !== unreadNotificationCount) {
      setLoading(true)
      const resp = await getList({
        channels: [NotificationChannels.UiActivity],
        isListRequest: false,
        maxResultCount: 1000,
      })
      const items = resp.data.items ?? []
      for (const notification of items) {
        await updateSent(notification.id, true)
      }
      const newNotificationList = items.map(
        (a) =>
          ({
            id: a.id,
            notificationType: a.notificationType,
            date: a.creationTime.toLocaleString(),
            message: a.message,
            creatorId: a.creatorId,
            tenantId: a.tenantId,
            readed: a.isRead,
          }) as NotificationList,
      )

      setLoading(false)
      setNotificationList(newNotificationList)
      setNoResult(newNotificationList.length == 0)
    }
  }, [notificationList, setLoading, unreadNotificationCount])

  const onMarkAllAsRead = useCallback(async () => {
    await updateReadAll(NotificationChannels.UiToast, true)

    const list = notificationList.map((item: NotificationList) => {
      if (!item.readed) {
        item.readed = true
      }
      return item
    })
    setNotificationList(list)
    setUnreadNotificationCount(0)
  }, [notificationList])

  const onMarkAsRead = useCallback(
    async (id: string) => {
      await updateRead(id, true)

      const list = notificationList.map((item) => {
        if (item.id === id) {
          item.readed = true
        }
        return item
      })
      setNotificationList(list)

      const unreadCount = notificationList.filter((item) => !item.readed).length
      setUnreadNotificationCount(unreadCount)
    },
    [notificationList],
  )

  // Toast
  const getToastNotifications = async () => {
    const resp = await getList({
      channels: [NotificationChannels.UiToast],
      isListRequest: false,
      maxResultCount: 1000,
    })
    const items = resp.data.items ?? []
    const newNotificationList = items.filter(
      (a) => !toastNotificationList.includes(a.id) && !a.isSent,
    )
    setToastNotificationList(newNotificationList.map((a) => a.id))
    for (const notification of newNotificationList) {
      toast.push(
        <Notify
          type="success"
          duration={0}
          closable={true}
          onClose={async () => {
            await updateRead(notification.id, true)
          }}
        >
          {notification.message}
        </Notify>,
        {
          placement: 'bottom-end',
        },
      )

      await updateSent(notification.id, true)
    }
    setToastNotificationList([])
  }

  useEffect(() => {
    toastNotificationInterval.current = setInterval(async () => {
      if (tabHasFocus) {
        await getToastNotifications()
      }
    }, notificationInterval)

    return () => {
      clearInterval(toastNotificationInterval.current)
    }
  }, [toastNotificationList, tabHasFocus])

  //Desktop
  const getDesktopNotifications = async () => {
    const resp = await getList({
      channels: [NotificationChannels.Desktop],
      isListRequest: false,
      maxResultCount: 1000,
    })
    const items = resp.data.items ?? []
    const newNotificationList = items.filter(
      (a) => !desktopNotificationList.includes(a.id) && !a.isSent,
    )
    setDesktopNotificationList(newNotificationList.map((a) => a.id))
    for (const notification of newNotificationList) {
      var options = {
        body: notification.message,
        dir: 'ltr',
      } as NotificationOptions
      new window.Notification(notification.notificationType, options)

      await updateSent(notification.id, true)
    }
    setDesktopNotificationList([])
  }

  useEffect(() => {
    desktopNotificationInterval.current = setInterval(async () => {
      await getDesktopNotifications()
    }, notificationInterval)

    return () => {
      clearInterval(desktopNotificationInterval.current)
    }
  }, [desktopNotificationList])

  return (
    <Dropdown
      renderTitle={
        <NotificationToggle unreadCount={unreadNotificationCount} className={className} />
      }
      menuClass="p-0 min-w-[280px] md:min-w-[340px]"
      placement={larger.md ? 'bottom-end' : 'bottom-center'}
      onOpen={onNotificationOpen}
    >
      <Dropdown.Item variant="header">
        <div className="border-b border-gray-200 dark:border-gray-600 px-4 py-2 flex items-center justify-between">
          <h6>{translate('::Abp.Identity.ActivityLogs.Notifications')}</h6>
          <Tooltip title={translate('::Abp.Identity.ActivityLogs.MarkAllRead')}>
            <Button
              variant="plain"
              shape="circle"
              size="sm"
              icon={<HiOutlineMailOpen className="text-xl" />}
              onClick={onMarkAllAsRead}
            />
          </Tooltip>
        </div>
      </Dropdown.Item>
      <div className={classNames('overflow-y-auto', notificationHeight)}>
        <ScrollBar direction={direction}>
          {notificationList.length > 0 &&
            notificationList.map((item, index) => (
              <div
                key={item.id}
                className={`relative flex px-4 py-4 cursor-pointer hover:bg-gray-50 active:bg-gray-100 dark:hover:bg-black dark:hover:bg-opacity-20  ${
                  !isLastChild(notificationList, index)
                    ? 'border-b border-gray-200 dark:border-gray-600'
                    : ''
                }`}
                onClick={() => onMarkAsRead(item.id)}
              >
                <div>{notificationTypeAvatar(item.creatorId, item.tenantId)}</div>
                <div className="ltr:ml-3 rtl:mr-3">
                  <div>
                    {item.notificationType && (
                      <span className="font-semibold heading-text">{item.notificationType} </span>
                    )}
                    <div>{item.message}</div>
                  </div>
                  <span className="text-xs">{dayjs(item.date).fromNow()}</span>
                </div>
                <Badge
                  className="absolute top-4 ltr:right-4 rtl:left-4 mt-1.5"
                  innerClass={`${item.readed ? 'bg-gray-300' : bgTheme} `}
                />
              </div>
            ))}
          {loading && (
            <div className={classNames('flex items-center justify-center', notificationHeight)}>
              <Spinner size={40} />
            </div>
          )}
          {noResult && (
            <div className={classNames('flex items-center justify-center', notificationHeight)}>
              <div className="text-center">
                <img
                  className="mx-auto mb-2 max-w-[150px]"
                  src="/img/others/no-notification.png"
                  alt="no-notification"
                />
                <h6 className="font-semibold">No notifications!</h6>
                <p className="mt-1">Please Try again later</p>
              </div>
            </div>
          )}
        </ScrollBar>
      </div>
      <Dropdown.Item variant="header">
        <div className="flex justify-center border-t border-gray-200 dark:border-gray-600 px-4 py-2">
          <Link
            to={ROUTES_ENUM.admin.activityLogs}
            className="font-semibold cursor-pointer p-2 px-3 text-gray-600 hover:text-gray-900 dark:text-gray-200 dark:hover:text-white"
          >
            {translate('::Abp.Identity.ActivityLogs.ViewAllActivity')}
          </Link>
        </div>
      </Dropdown.Item>
    </Dropdown>
  )
}

const Notification = withHeaderItem(_Notification)

export default Notification
