/**
 * HTTP transport + 401-retry consolidator.
 *
 * `request()` moved from client.js to break the circular dependency:
 * domain modules import request from here; client.js re-exports from those
 * same domain modules, so request cannot also live in client.js.
 *
 * `fetchWithRetry()` consolidates 3 blob-download patterns that previously
 * duplicated identical auth-injection + 401-retry boilerplate:
 *   - adminExportAuditLogCsv (admin.js)
 *   - adminDownloadDailyExport (admin.js)
 *   - fetchDocumentContent (documents.js)
 *
 * Security: Bearer token injected from authStore (Pinia memory only — CLAUDE.md).
 * Token is NEVER read from localStorage or sessionStorage.
 */

/**
 * Core HTTP transport. All JSON-returning endpoints go through this function.
 *
 * On 401: attempts one token refresh via authStore.refresh() then retries.
 * Skip-refresh guard: login/register return 401 for bad credentials (not expired
 * tokens), and refresh itself must not retry (would cause infinite loop).
 *
 * @param {string} path — API path (e.g. '/api/documents')
 * @param {RequestInit & {_retry?: boolean}} [options] — fetch options
 * @returns {Promise<any>} — parsed JSON response, or null for 204/empty body
 */
export async function request(path, options = {}) {
  // Lazy import to avoid circular dependency (stores/auth.js → api/client.js → stores/auth.js)
  const { useAuthStore } = await import('../stores/auth.js')
  const authStore = useAuthStore()

  const headers = { ...(options.headers || {}) }
  if (authStore.accessToken) {
    headers['Authorization'] = `Bearer ${authStore.accessToken}`
  }

  const res = await fetch(path, { ...options, headers, credentials: 'include' })

  // 401 → attempt refresh → retry once
  // Skip refresh for auth endpoints: login/register return 401 for bad credentials (not expired tokens),
  // and refresh itself must not retry to avoid an infinite loop.
  const noRefreshPaths = ['/api/auth/login', '/api/auth/register', '/api/auth/refresh']
  if (res.status === 401 && !options._retry && !noRefreshPaths.includes(path)) {
    try {
      await authStore.refresh()
      return request(path, { ...options, _retry: true })
    } catch {
      authStore.accessToken = null
      authStore.user = null
      throw new Error('Session expired')
    }
  }

  if (!res.ok) {
    let msg = `HTTP ${res.status}`
    let payload = null
    try {
      const body = await res.json()
      if (typeof body.detail === 'object' && body.detail !== null) {
        payload = body.detail
        msg = body.detail.message || `HTTP ${res.status}`
      } else {
        msg = body.detail || msg
      }
    } catch {}
    const err = new Error(msg)
    err.status = res.status
    if (payload) err.payload = payload
    throw err
  }
  if (res.status === 204 || res.headers.get('content-length') === '0') return null
  return res.json()
}

/**
 * Authenticated fetch with 401-retry for non-JSON responses (blobs, raw Response).
 *
 * Consolidates adminExportAuditLogCsv, adminDownloadDailyExport, fetchDocumentContent
 * which share identical auth-injection + 401-retry boilerplate (CODE-08).
 *
 * Unlike request(), this does NOT parse the response — the raw Response is returned
 * so callers can call .text(), .blob(), etc. as appropriate.
 *
 * Security: Bearer token from authStore.accessToken (memory only — CLAUDE.md).
 * On 401-and-not-already-retried: calls authStore.refresh() and recurses with
 * _retry=true. On refresh failure: clears in-memory token state and throws
 * 'Session expired'.
 *
 * @param {string} url — full URL to fetch
 * @param {RequestInit} [options] — fetch options (method, headers, etc.)
 * @param {boolean} [_retry] — internal retry guard; callers must NOT pass this
 * @returns {Promise<Response>} — raw Response; caller decides how to consume it
 */
export async function fetchWithRetry(url, options = {}, _retry = false) {
  const { useAuthStore } = await import('../stores/auth.js')
  const authStore = useAuthStore()

  const headers = { ...(options.headers || {}) }
  if (authStore.accessToken) {
    headers['Authorization'] = `Bearer ${authStore.accessToken}`
  }

  const res = await fetch(url, { ...options, headers, credentials: 'include' })

  if (res.status === 401 && !_retry) {
    try {
      await authStore.refresh()
      return fetchWithRetry(url, options, true)
    } catch {
      authStore.accessToken = null
      authStore.user = null
      throw new Error('Session expired')
    }
  }

  return res
}
