'use client'

import {
    createContext,
    useCallback,
    useContext,
    useEffect,
    useState,
    type ReactNode,
} from 'react'
import { isNotificationUnread } from '@/lib/notifications/notificationUnread'
import {
    clearOptimisticUnread,
    mergeUnreadWithApi,
    subscribeUnreadBadge,
} from '@/lib/notifications/unreadBadge'
import { useNoticeWs } from '@/contexts/NoticeWsContext'
import {
    SESSION_CHANGED_EVENT,
    USER_NOTIFICATIONS_LIST_EVENT,
    USER_NOTIFICATIONS_UPDATED_EVENT,
    type UserNotificationsListDetail,
    type UserNotificationsUpdatedDetail,
} from '@/lib/notices/wsNotice'
import {
    readNotificationSettingsCache,
    shouldShowDesktopPushForType,
} from '@/lib/notifications/notificationSettingsCache'
import { showDesktopNotificationIfAllowed } from '@/lib/notifications/showDesktopNotification'

type UserNotificationsContextValue = {
    hasUnread: boolean
    refreshUnread: () => Promise<void>
}

const UserNotificationsContext = createContext<UserNotificationsContextValue>({
    hasUnread: false,
    refreshUnread: async () => {},
})

export function UserNotificationsProvider({ children }: { children: ReactNode }) {
    const [hasUnread, setHasUnread] = useState(false)
    const { listNotifications, wsReady } = useNoticeWs()

    const refreshUnread = useCallback(async () => {
        if (!wsReady) return
        try {
            const list = await listNotifications()
            const apiHasUnread = list.some((n) => !n.isRead)
            setHasUnread(mergeUnreadWithApi(apiHasUnread))
        } catch {
            // 保留樂觀未讀，不因網路錯誤清掉紅點
        }
    }, [listNotifications, wsReady])

    useEffect(() => {
        return subscribeUnreadBadge((optimistic) => {
            setHasUnread((prev) => prev || optimistic)
        })
    }, [])

    useEffect(() => {
        if (wsReady) {
            void refreshUnread()
        }
    }, [wsReady, refreshUnread])

    useEffect(() => {
        const tick = () => {
            if (document.visibilityState === 'visible' && wsReady) {
                void refreshUnread()
            }
        }
        const intervalId = window.setInterval(tick, 12_000)
        document.addEventListener('visibilitychange', tick)
        return () => {
            window.clearInterval(intervalId)
            document.removeEventListener('visibilitychange', tick)
        }
    }, [refreshUnread, wsReady])

    useEffect(() => {
        const onSessionChange = () => {
            clearOptimisticUnread()
            void refreshUnread()
        }
        window.addEventListener(SESSION_CHANGED_EVENT, onSessionChange)
        return () => window.removeEventListener(SESSION_CHANGED_EVENT, onSessionChange)
    }, [refreshUnread])

    useEffect(() => {
        const onList = (event: Event) => {
            const detail = (event as CustomEvent<UserNotificationsListDetail>).detail
            const list = detail?.notifications ?? []
            setHasUnread(mergeUnreadWithApi(list.some((n) => !n.isRead)))
        }
        window.addEventListener(USER_NOTIFICATIONS_LIST_EVENT, onList)
        return () => window.removeEventListener(USER_NOTIFICATIONS_LIST_EVENT, onList)
    }, [])

    useEffect(() => {
        const onUpdated = (event: Event) => {
            const detail = (event as CustomEvent<UserNotificationsUpdatedDetail>).detail
            const payload = detail?.payload
            if (payload && isNotificationUnread(payload)) {
                setHasUnread(true)
            }
            if (payload) {
                const cache = readNotificationSettingsCache()
                const allowDesktop =
                    payload.desktopEnabled ??
                    cache?.desktopNotificationEnabled ??
                    false
                const allowPush = payload.pushEnabled ?? shouldShowDesktopPushForType(payload.type, cache)
                if (allowDesktop && allowPush) {
                    showDesktopNotificationIfAllowed(payload)
                }
                window.setTimeout(() => void refreshUnread(), 400)
                return
            }
            void refreshUnread()
        }
        window.addEventListener(USER_NOTIFICATIONS_UPDATED_EVENT, onUpdated)
        return () => window.removeEventListener(USER_NOTIFICATIONS_UPDATED_EVENT, onUpdated)
    }, [refreshUnread])

    return (
        <UserNotificationsContext.Provider value={{ hasUnread, refreshUnread }}>
            {children}
        </UserNotificationsContext.Provider>
    )
}

export function useUserNotifications() {
    return useContext(UserNotificationsContext)
}
