'use client'

import { AUTH_SYNC_EVENT } from '@/constants/auth'
import {
  AUTH_BROADCAST_CHANNEL,
  AUTH_CURRENT_PAGE_KEY,
  AUTH_INFO_KEY,
  AUTH_REFRESH_FAILED_EVENT,
} from '@/constants/system'
import { useRouter } from '@/i18n/navigation'
import { useLogout, useRefresh } from '@/services/auth/auth.query-options'
import {
  AuthContextType,
  AuthFullInfo,
  AuthInfoBase,
  AuthInfoFullSchema,
} from '@/services/auth/auth.schema'
import { fetchLoginPageUrl } from '@/share/hooks/login-redirect'
import { toastSuccessOptionsForSize } from '@/share/ui/sonner'
import { useLocale, useTranslations } from 'next-intl'
import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react'
import { toast } from 'sonner'

const AuthContext = createContext<AuthContextType | null>(null)

export const AuthProvider = ({
  children,
}: Readonly<{ children: React.ReactNode }>) => {
  const router = useRouter()
  const locale = useLocale()
  const t = useTranslations('layout')
  const [authInfo, setAuthInfo] = useState<AuthFullInfo | null>(null)
  const [isLoading, setIsLoading] = useState(true)

  const logoutMutation = useLogout(() => {
    localStorage.removeItem(AUTH_INFO_KEY)
    sendAuthEvent('logout')
    setAuthInfo(null)
    toast.success(t('mess.logout.success'), toastSuccessOptionsForSize('lg'))
  })

  const login = useCallback(
    async (
      authDataLogin: AuthInfoBase,
      options?: { callback?: () => void; isNotSendBroadcast?: boolean }
    ) => {
      const authLogin = AuthInfoFullSchema.parse(authDataLogin)
      localStorage.setItem(AUTH_INFO_KEY, JSON.stringify(authLogin))
      setAuthInfo(authLogin)
      if (!options?.isNotSendBroadcast) sendAuthEvent('changed')
      options?.callback?.()
    },
    []
  )

  const logout = useCallback(
    (options?: { callback?: () => void; isNotSendBroadcast?: boolean }) => {
      if (options?.isNotSendBroadcast) {
        if (authInfo) {
          localStorage.removeItem(AUTH_INFO_KEY)
          setAuthInfo(null)
        }
        options?.callback?.()
        return
      }

      if (authInfo) {
        logoutMutation.mutate({
          callback: `${window.location.origin}`,
        })
      }
    },
    [authInfo, logoutMutation]
  )

  const syncAuthFromStorage = useCallback(() => {
    const savedAuthInfoLocal = localStorage.getItem(AUTH_INFO_KEY)
    if (savedAuthInfoLocal) {
      const savedAuthInfo = AuthInfoFullSchema.safeParse(
        JSON.parse(savedAuthInfoLocal)
      )
      if (savedAuthInfo.success) {
        setAuthInfo(savedAuthInfo.data)
        return
      }
    }
    setAuthInfo(null)
  }, [])

  const initAuth = useCallback(() => {
    syncAuthFromStorage()
    setIsLoading(false)
  }, [syncAuthFromStorage])

  const redirectToLoginPage = useCallback(() => {
    void fetchLoginPageUrl(locale).then(
      (url) => {
        window.location.assign(url)
      },
      () => {
        router.replace('/')
      }
    )
  }, [locale, router])

  // Proactive refresh: gọi `/auth/refresh` ngay trước khi access cookie hết hạn
  // (tại thời điểm `refresh_at = expires_in - 120s`) để user không bao giờ
  // gặp 401 giữa thao tác. Dùng `isNotSendBroadcast` tránh kích hoạt
  // `router.refresh()` ở các tab khác — mỗi tab tự lên lịch timer của nó.
  const refreshMutation = useRefresh(
    (data) => login(data, { isNotSendBroadcast: true }),
    () => logout({ isNotSendBroadcast: true })
  )

  useEffect(() => {
    if (!authInfo?.refresh_at) return
    const delay = Math.max(0, authInfo.refresh_at - Date.now())
    const timeout = setTimeout(() => {
      refreshMutation.mutate()
    }, delay)
    return () => clearTimeout(timeout)
    // `refreshMutation.mutate` là stable reference từ react-query nên không
    // gây re-schedule liên tục; chỉ phụ thuộc `refresh_at` ở deps để timer
    // reset khi authInfo thay đổi (login mới / refresh thành công).
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [authInfo?.refresh_at])

  useEffect(() => {
    if (typeof window === 'undefined') {
      return
    }
    const onRefreshTokenUnauthorized = () => {
      logout()
    }
    window.addEventListener(
      AUTH_REFRESH_FAILED_EVENT,
      onRefreshTokenUnauthorized
    )
    return () => {
      window.removeEventListener(
        AUTH_REFRESH_FAILED_EVENT,
        onRefreshTokenUnauthorized
      )
    }
  }, [logout])

  useEffect(() => {
    initAuth()

    let channel: BroadcastChannel | null = null
    try {
      if (typeof BroadcastChannel !== 'undefined') {
        channel = new BroadcastChannel(AUTH_BROADCAST_CHANNEL)
        channel.onmessage = ({ data }) => {
          try {
            if (sessionStorage.getItem(AUTH_CURRENT_PAGE_KEY) === 'true') {
              return
            }
            switch (data) {
              case 'logout': {
                syncAuthFromStorage()
                redirectToLoginPage()
                break
              }
              case 'changed': {
                syncAuthFromStorage()
                const hasSession = Boolean(localStorage.getItem(AUTH_INFO_KEY))
                if (hasSession) {
                  router.refresh()
                } else {
                  redirectToLoginPage()
                }
                break
              }
            }
          } catch (error) {
            toast.error(String(error))
          }
        }
      }
    } catch (error) {
      toast.error(String(error))
    }

    return () => {
      if (channel) {
        try {
          channel.onmessage = null
          channel.close()
        } catch (error) {
          toast.error(String(error))
        }
      }
    }
  }, [initAuth, redirectToLoginPage, router, syncAuthFromStorage])

  const value = useMemo(
    () => ({
      authInfo,
      isLoading,
      login,
      logout,
    }),
    [authInfo, isLoading, login, logout]
  )

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}

function sendAuthEvent(event: AUTH_SYNC_EVENT) {
  try {
    if (typeof BroadcastChannel !== 'undefined') {
      const channel = new BroadcastChannel(AUTH_BROADCAST_CHANNEL)
      sessionStorage.setItem(AUTH_CURRENT_PAGE_KEY, 'true')
      setTimeout(() => {
        sessionStorage.removeItem(AUTH_CURRENT_PAGE_KEY)
      }, 1000)
      try {
        channel.postMessage(event)
      } catch (error) {
        toast.error(String(error))
      } finally {
        try {
          channel.close()
        } catch (error) {
          toast.error(String(error))
        }
      }
    }
  } catch (error) {
    toast.error(String(error))
  }
}

export const useAuth = () => {
  const ctx = useContext(AuthContext)
  if (!ctx) {
    throw new Error('useAuth must be used within AuthProvider')
  }
  return ctx
}
