import { useEffect, useState } from 'react'
import { fetchValidatedSession, loginWithCredentials, logoutUser } from './authApi'
import { authStorageKey } from './authStorage'
import {
  TOKEN_REFRESHED_EVENT,
  UNAUTHORIZED_EVENT,
  refreshAccessToken,
} from '../../utils/apiFetch'
import type { AuthSession } from '../../types/auth'

export function useAuthSession() {
  const [session, setSession] = useState<AuthSession | null>(null)
  const [authLoading, setAuthLoading] = useState(true)

  useEffect(() => {
    let isActive = true

    async function restoreSession() {
      let token = sessionStorage.getItem(authStorageKey)

      if (!token) {
        // Tab was reloaded — sessionStorage is empty, but the refresh cookie
        // may still be valid. Try once before giving up.
        token = await refreshAccessToken()
      }

      if (!token) {
        if (isActive) {
          setAuthLoading(false)
        }
        return
      }

      const restoredSession = await fetchValidatedSession(token)

      if (!restoredSession) {
        sessionStorage.removeItem(authStorageKey)
        if (isActive) {
          setSession(null)
          setAuthLoading(false)
        }
        return
      }

      if (isActive) {
        setSession(restoredSession)
        setAuthLoading(false)
      }
    }

    void restoreSession()

    return () => {
      isActive = false
    }
  }, [])

  async function handleLogin(username: string, password: string) {
    const data = await loginWithCredentials(username, password)
    const validatedSession = await fetchValidatedSession(data.token)

    if (!validatedSession) {
      throw new Error('Login succeeded, but the session could not be validated.')
    }

    sessionStorage.setItem(authStorageKey, data.token)
    setSession(validatedSession)
  }

  function handleLogout() {
    const token = sessionStorage.getItem(authStorageKey)
    sessionStorage.removeItem(authStorageKey)
    setSession(null)

    if (token) {
      void logoutUser(token)
    }
  }

  useEffect(() => {
    window.addEventListener(UNAUTHORIZED_EVENT, handleLogout)
    return () => window.removeEventListener(UNAUTHORIZED_EVENT, handleLogout)
  }, [])

  useEffect(() => {
    function onTokenRefreshed(event: Event) {
      const newToken = (event as CustomEvent<string>).detail
      if (!newToken) return
      setSession((current) => (current ? { ...current, token: newToken } : current))
    }
    window.addEventListener(TOKEN_REFRESHED_EVENT, onTokenRefreshed)
    return () => window.removeEventListener(TOKEN_REFRESHED_EVENT, onTokenRefreshed)
  }, [])

  return {
    session,
    authLoading,
    handleLogin,
    handleLogout,
  }
}
