import { getHeader } from 'h3'
import authMiddleware from './middleware/sidebase-auth'
import { getNitroRouteRules } from './utils/kit'
import { FetchConfigurationError } from './utils/fetch'
import { resolveApiBaseURL } from './utils/url'
import { _refreshHandler, addRouteMiddleware, defineNuxtPlugin, useAuth, useAuthState, useRuntimeConfig } from '#imports'

export default defineNuxtPlugin(async (nuxtApp) => {
  // 1. Initialize authentication state, potentially fetch current session
  const { data, lastRefreshedAt, loading } = useAuthState()
  const { getSession } = useAuth()

  // use runtimeConfig
  const wholeRuntimeConfig = useRuntimeConfig()
  const runtimeConfig = wholeRuntimeConfig.public.auth
  const globalAppMiddleware = runtimeConfig.globalAppMiddleware

  const routeRules = import.meta.server ? getNitroRouteRules(nuxtApp._route.path) : {}

  // Set the correct `baseURL` on the server,
  // because the client would not have access to environment variables
  if (import.meta.server) {
    runtimeConfig.baseURL = resolveApiBaseURL(wholeRuntimeConfig)
  }

  // Skip auth if we're prerendering
  let nitroPrerender = false
  if (nuxtApp.ssrContext) {
    nitroPrerender
      = getHeader(nuxtApp.ssrContext.event, 'x-nitro-prerender') !== undefined
  }

  // Prioritize `routeRules` setting over `runtimeConfig` settings, fallback to false
  let disableServerSideAuth = routeRules.disableServerSideAuth
  disableServerSideAuth ??= runtimeConfig?.disableServerSideAuth
  disableServerSideAuth ??= false

  if (disableServerSideAuth) {
    loading.value = true
  }

  // Only fetch session if it was not yet initialized server-side
  const isErrorUrl = nuxtApp.ssrContext?.error === true
  const requireAuthOnErrorPage = globalAppMiddleware === true || (typeof globalAppMiddleware === 'object' && globalAppMiddleware.allow404WithoutAuth)
  const shouldFetchSession = typeof data.value === 'undefined'
    && !nitroPrerender
    && !disableServerSideAuth
    && !(isErrorUrl && requireAuthOnErrorPage)

  if (shouldFetchSession) {
    try {
      await getSession()
    }
    catch (e) {
      // Do not throw the configuration error as it can lead to infinite recursion
      if (!(e instanceof FetchConfigurationError)) {
        throw e
      }
    }
  }

  // 2. Setup session maintanence, e.g., auto refreshing or refreshing on foux
  nuxtApp.hook('app:mounted', () => {
    _refreshHandler.init()
    if (disableServerSideAuth) {
      getSession()
    }
  })

  const _unmount = nuxtApp.vueApp.unmount
  nuxtApp.vueApp.unmount = function () {
    _refreshHandler.destroy()

    // Clear session
    lastRefreshedAt.value = undefined
    data.value = undefined

    // Call original unmount
    _unmount()
  }

  // 3. Enable the middleware, either globally or as a named `auth` option
  if (
    globalAppMiddleware === true
    || (typeof globalAppMiddleware === 'object' && globalAppMiddleware.isEnabled)
  ) {
    addRouteMiddleware('auth', authMiddleware, {
      global: true
    })
  }
})