import { useGlobalState } from './state'
import { useGlobalActions } from './actions'
import type { UseGlobalReturn } from './types'
import { useGlobalGetters } from './getters'
import { useNuxtApp, watch } from '#imports'

/**
 * Global state is injected by {@link import('~/plugins/state') state} plugin into our nuxt app (available as `$state`).
 * You can still call `useGlobal` to receive the `$state` object and access the global state.
 * If it's not available yet, a new global state object is created and injected into the nuxt app.
 *
 * Part of the state is stored in {@link WindowLocalStorage localStorage}, so it will be available even if the user closes the browser tab.
 * Check the {@link StoredState StoredState} type for more information.
 *
 * @example
 * ```js
 * import { useNuxtApp } from '#app'
 *
 * const { $state } = useNuxtApp()
 *
 * const token = $state.token.value
 * const user = $state.user.value
 * ```
 *
 * @example
 * ```js
 * import { useGlobal } from '#imports'
 *
 * const globalState = useGlobal()
 *
 * cont token = globalState.token.value
 * const user = globalState.user.value
 *
 * console.log(state.isLoading.value) // isLoading = true if any api request is still running
 * ```
 */
export const useGlobal = (): UseGlobalReturn => {
  const { $state, provide } = useNuxtApp()

  /** If state already exists, return it */
  if (typeof $state !== 'undefined') return $state

  const state = useGlobalState()

  const getters = useGlobalGetters(state)

  const actions = useGlobalActions(state)

  /** try to refresh token before expiry (5 min before expiry) */
  watch(
    () =>
      !!(
        state.jwtPayload.value &&
        state.jwtPayload.value.exp &&
        state.jwtPayload.value.exp - 5 * 60 < state.timestamp.value / 1000
      ),
    async (expiring) => {
      if (getters.signedIn.value && state.jwtPayload.value && expiring) {
        await actions.refreshToken()
      }
    },
    { immediate: true },
  )

  const globalState = { ...state, ...getters, ...actions } as UseGlobalReturn

  /** provide a fresh state instance into nuxt app */
  provide('state', globalState)

  return globalState
}
