import { authStorageKey } from '../features/auth/authStorage'

export const UNAUTHORIZED_EVENT = 'auth:unauthorized'
export const TOKEN_REFRESHED_EVENT = 'auth:tokenRefreshed'

const apiBaseUrl = import.meta.env.VITE_API_URL ?? 'http://localhost:5000'

let refreshPromise: Promise<string | null> | null = null

export async function refreshAccessToken(): Promise<string | null> {
  if (refreshPromise) {
    return refreshPromise
  }

  refreshPromise = (async () => {
    try {
      const response = await fetch(`${apiBaseUrl}/api/user/refresh`, {
        method: 'POST',
        credentials: 'include',
      })

      if (!response.ok) {
        return null
      }

      const data = (await response.json()) as { token?: string } | undefined
      const token = data?.token
      if (!token) {
        return null
      }

      sessionStorage.setItem(authStorageKey, token)
      window.dispatchEvent(new CustomEvent(TOKEN_REFRESHED_EVENT, { detail: token }))
      return token
    } catch {
      return null
    } finally {
      refreshPromise = null
    }
  })()

  return refreshPromise
}

export async function apiFetch(url: string, options?: RequestInit): Promise<Response> {
  const response = await fetch(url, { credentials: 'include', ...options })

  if (response.status !== 401) {
    return response
  }

  const newToken = await refreshAccessToken()
  if (!newToken) {
    window.dispatchEvent(new CustomEvent(UNAUTHORIZED_EVENT))
    throw new Error('Your session has expired. Please log in again.')
  }

  const retryHeaders = new Headers(options?.headers)
  if (retryHeaders.has('Authorization')) {
    retryHeaders.set('Authorization', `Bearer ${newToken}`)
  }

  const retry = await fetch(url, { credentials: 'include', ...options, headers: retryHeaders })
  if (retry.status === 401) {
    window.dispatchEvent(new CustomEvent(UNAUTHORIZED_EVENT))
    throw new Error('Your session has expired. Please log in again.')
  }

  return retry
}
