// Wrapper around $fetch for authenticated WRITES from the client. The OIDC
// access token in the server-side session can lapse while a tab stays open;
// the next write then hits a proxy that finds no token and 401s (a page
// refresh "fixed" it only because navigation re-ran the session refresh).
//
// On a 401 we hit nuxt-oidc-auth's refresh endpoint DIRECTLY (POST
// /api/_auth/refresh) rather than useOidcAuth().refresh() — the composable
// falls back to a full login() *redirect* when the refresh token is missing or
// expired, which would navigate away mid-save and throw out the user's input.
// Here, a failed refresh just rejects: we surface a clear error and leave the
// user on the page with their form intact, so they can re-submit after signing
// in again. Sign-in stays an explicit, user-driven action.
//
// Call this in setup(); the returned `request` can be invoked later.

interface ApiOpts {
  method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'
  body?: unknown
  query?: Record<string, unknown>
  headers?: Record<string, string>
}

function isUnauthorized(err: unknown): boolean {
  const e = err as { statusCode?: number; response?: { status?: number } }
  return e?.statusCode === 401 || e?.response?.status === 401
}

export function useApiFetch() {
  // Silent token refresh. Resolves true if the session now has a fresh access
  // token, false if it couldn't be refreshed (no/expired refresh token).
  async function refreshSession(): Promise<boolean> {
    try {
      await $fetch('/api/_auth/refresh', { method: 'POST', headers: { Accept: 'text/json' } })
      return true
    } catch {
      return false
    }
  }

  async function request<T>(url: string, opts: ApiOpts = {}): Promise<T> {
    const fetchOpts = opts as Parameters<typeof $fetch>[1]
    try {
      return (await $fetch(url, fetchOpts)) as T
    } catch (err) {
      if (!isUnauthorized(err)) throw err
      // Token lapsed mid-session — try a silent refresh, then retry once.
      if (await refreshSession()) {
        return (await $fetch(url, fetchOpts)) as T
      }
      // Refresh failed: the session is genuinely expired. Don't redirect (that
      // would discard the user's input) — fail loudly so the caller keeps the
      // form open and can show "sign in again to save".
      throw createError({
        statusCode: 401,
        statusMessage: 'Session expired',
        message: 'Your session expired. Please sign in again, then save your changes.',
        data: { message: 'Your session expired. Please sign in again, then save your changes.' },
      })
    }
  }

  return { request }
}
