/**
 * Session hydration + proactive token refresh (nuxt3 port).
 *
 * Uses GET /auth/session (backend commit 053de76) as the source of truth
 * for the current user's auth state. Runs on app boot, on tab focus, and
 * after login/refresh so admin-side changes (suspend, role flip) surface
 * without requiring logout/login.
 *
 * Proactive refresh: single setTimeout scheduled at exp - 60s using
 * token.exp from /session. The reactive interceptor in services/api.ts
 * remains as a safety net for missed schedules (laptop sleep, throttled
 * background tabs).
 */

export default defineNuxtPlugin(() => {
  const REFRESH_LEAD_MS = 60 * 1000
  const MIN_DELAY_MS = 5 * 1000

  const auth = useAuthStore()
  let scheduled: ReturnType<typeof setTimeout> | null = null

  function clearScheduled() {
    if (scheduled) {
      clearTimeout(scheduled)
      scheduled = null
    }
  }

  async function doRefresh() {
    try {
      await auth.refreshTokens()
      // Pick up the new token.exp and reschedule.
      await auth.fetchSession().then(scheduleFromState)
    } catch {
      // Reactive interceptor handles terminal auth failures.
    }
  }

  function scheduleFromState(_data?: unknown) {
    clearScheduled()
    const exp = auth.tokenMeta?.exp
    if (!exp) return
    const delay = Math.max(exp * 1000 - Date.now() - REFRESH_LEAD_MS, MIN_DELAY_MS)
    scheduled = setTimeout(() => {
      scheduled = null
      doRefresh()
    }, delay)
  }

  async function hydrate() {
    if (!auth.token) return
    await auth.fetchSession()
    scheduleFromState()
  }

  // Boot — wait for cookie restoration before calling /session.
  if (import.meta.client) {
    auth.restoreFromCookies()
    hydrate()

    // Resume-from-sleep: setTimeout uses wall-clock time and either fires
    // very late or not at all after long sleeps (Chrome/Firefox fire late;
    // aggressive mobile webviews drop the timer). If the cached token.exp
    // is in the past by the time the tab regains focus, the wall-clock
    // schedule missed — force a refresh *before* any other post-resume
    // request can escape with the stale token. Both fetchSession and
    // refreshTokens are module-level deduped, so racing with a reactive
    // 401 from some other mount-time call collapses to one real refresh.
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState !== 'visible' || !auth.token) return
      const exp = auth.tokenMeta?.exp
      if (exp && exp * 1000 - Date.now() <= REFRESH_LEAD_MS) {
        doRefresh()
      } else {
        hydrate()
      }
    })
  }

  return {
    provide: {
      refreshSession: hydrate,
    },
  }
})
