import {
  createContext,
  useState,
  useContext,
  useCallback,
  useEffect,
  useEffectEvent
} from 'react'
import { useLocation } from 'react-router-dom'
import { notification, Drawer } from 'antd'
import PropTypes from 'prop-types'
import { AuthContext } from './AuthContext'
import { ApiServerContext } from './ApiServerContext'
import NotificationCenter from '../common/NotificationCenter'
import Notification from '../common/Notification'
import { useMediaQuery } from 'react-responsive'

const NotificationContext = createContext()

const NotificationProvider = ({ children }) => {
  const [api, contextHolder] = notification.useNotification()
  const location = useLocation()
  const { authenticated } = useContext(AuthContext)
  const {
    showError,
    fetchNotificationsApi,
    markNotificationAsReadApi,
    markAllNotificationsAsReadApi,
    deleteNotificationApi,
    deleteAllNotificationsApi,
    registerNotificationListener,
    connected
  } = useContext(ApiServerContext)

  const [notificationCenterVisible, setNotificationCenterVisible] =
    useState(false)
  const [notifications, setNotifications] = useState([])
  const [notificationsLoading, setNotificationsLoading] = useState(false)

  const isMobile = useMediaQuery({ maxWidth: 768 })

  const fetchNotifications = useCallback(async () => {
    if (!authenticated) return []
    setNotificationsLoading(true)
    try {
      const data = await fetchNotificationsApi()
      setNotifications(data)
      return data
    } catch (err) {
      console.error(err)
      const is404 = err?.response?.status === 404
      if (is404) {
        setNotifications([])
      } else {
        showError(err, () => fetchNotifications())
      }
      return []
    } finally {
      setNotificationsLoading(false)
    }
  }, [authenticated, fetchNotificationsApi, showError])

  const onFetchNotifications = useEffectEvent(() => {
    if (!authenticated || !connected) return
    fetchNotifications()
  })

  const markNotificationAsRead = useCallback(
    async (notificationId) => {
      try {
        await markNotificationAsReadApi(notificationId)
        setNotifications((prev) =>
          prev.map((n) => (n._id === notificationId ? { ...n, read: true } : n))
        )
      } catch (err) {
        console.error(err)
        showError(err, () => markNotificationAsRead(notificationId))
      }
    },
    [markNotificationAsReadApi, showError]
  )

  const markAllNotificationsAsRead = useCallback(async () => {
    try {
      await markAllNotificationsAsReadApi()
      setNotifications((prev) => prev.map((n) => ({ ...n, read: true })))
    } catch (err) {
      console.error(err)
      showError(err, () => markAllNotificationsAsRead())
    }
  }, [markAllNotificationsAsReadApi, showError])

  const deleteNotification = useCallback(
    async (notificationId) => {
      try {
        await deleteNotificationApi(notificationId)
        setNotifications((prev) => prev.filter((n) => n._id !== notificationId))
      } catch (err) {
        console.error(err)
        showError(err, () => deleteNotification(notificationId))
      }
    },
    [deleteNotificationApi, showError]
  )

  const deleteAllNotifications = useCallback(async () => {
    try {
      await deleteAllNotificationsApi()
      setNotifications([])
    } catch (err) {
      console.error(err)
      showError(err, () => deleteAllNotifications())
    }
  }, [deleteAllNotificationsApi, showError])

  const toggleNotificationCenter = useCallback(() => {
    setNotificationCenterVisible((prev) => !prev)
  }, [])

  const unreadCount = notifications.filter((n) => !n.read).length

  // Initial load / when we become authenticated and connected
  useEffect(() => {
    onFetchNotifications()
  }, [authenticated, connected])

  // Refresh when the notification center opens
  useEffect(() => {
    if (notificationCenterVisible) {
      onFetchNotifications()
    }
  }, [notificationCenterVisible])

  useEffect(() => {
    setNotificationCenterVisible(false)
  }, [location.pathname])

  useEffect(() => {
    if (!authenticated || !registerNotificationListener) return
    const handleNotification = (notif) => {
      setNotifications((prev) => {
        const exists = prev.some((n) => n._id === notif._id)
        if (exists) return prev
        return [{ ...notif, read: false }, ...prev]
      })
      const show = api.open
      show({
        message: (
          <Notification
            notification={notif}
            onDelete={deleteNotification}
            showCard={false}
            showDelete={false}
            showExtraInfo={false}
            inlineIcon={true}
            showDetails={false}
            largeTitleSpacing={true}
          />
        ),
        icon: null,
        duration: 5,
        key: notif._id
      })
    }
    const unregister = registerNotificationListener(handleNotification)
    return unregister
  }, [authenticated, registerNotificationListener, api, deleteNotification])

  return (
    <NotificationContext.Provider
      value={{
        notificationCenterVisible,
        toggleNotificationCenter,
        notifications,
        fetchNotifications,
        markNotificationAsRead,
        markAllNotificationsAsRead,
        deleteNotification,
        deleteAllNotifications,
        unreadCount,
        notificationsLoading
      }}
    >
      {contextHolder}
      {children}
      <Drawer
        title='Notifications'
        placement='right'
        width={isMobile ? '100%' : 460}
        onClose={() => setNotificationCenterVisible(false)}
        open={notificationCenterVisible}
      >
        <NotificationCenter
          visible={notificationCenterVisible}
          onClose={() => setNotificationCenterVisible(false)}
        />
      </Drawer>
    </NotificationContext.Provider>
  )
}

NotificationProvider.propTypes = {
  children: PropTypes.node.isRequired
}

export { NotificationContext, NotificationProvider }
