const DEFAULT_BASE = '/api/cms'

let basePath = DEFAULT_BASE

export function setApiBase(path: string) {
  basePath = path
}

/**
 * Current API base path. Exposed so non-`fetch` consumers (in
 * particular `EventSource` for SSE channels) can resolve URLs against
 * the same prefix configured via `setApiBase()`. Keep this read-only
 * — mutation must always flow through `setApiBase()` so we never have
 * two callers with diverging views of the base.
 */
export function getApiBase(): string {
  return basePath
}

function getCsrfToken(): string {
  if (typeof document === 'undefined') return ''
  const match = document.cookie.match(/(?:^|;\s*)actuate_csrf=([^;]*)/)
  return match?.[1] ?? ''
}

/**
 * Fetch a CSRF token if we don't have one in cookies yet. Used both as a
 * one-shot bootstrap (e.g. on AdminRoot mount after a Server-Action login)
 * and lazily inside `cmsApi` before mutating requests.
 *
 * Multiple concurrent callers share a single in-flight fetch via the
 * `pendingFetch` cache so the admin doesn't fire N parallel `/auth/csrf`
 * requests when a page lights up after navigation.
 */
let pendingFetch: Promise<void> | null = null

export async function ensureCsrfToken(): Promise<void> {
  if (getCsrfToken()) return
  if (pendingFetch) return pendingFetch
  pendingFetch = (async () => {
    try {
      await fetch(`${basePath}/auth/csrf`, { credentials: 'include' })
    } catch {
      // best-effort — caller will see the 403 from the actual mutation if
      // the cookie still isn't set, instead of a silent network failure.
    } finally {
      pendingFetch = null
    }
  })()
  return pendingFetch
}

function getActiveLocale(): string | null {
  if (typeof localStorage === 'undefined') return null
  return localStorage.getItem('actuate-locale')
}

function appendLocaleParam(endpoint: string): string {
  const locale = getActiveLocale()
  if (!locale) return endpoint
  const separator = endpoint.includes('?') ? '&' : '?'
  return `${endpoint}${separator}locale=${encodeURIComponent(locale)}`
}

export async function cmsApi<T = unknown>(
  endpoint: string,
  options: RequestInit = {},
): Promise<{ data?: T; error?: string; status: number }> {
  const url = `${basePath}${appendLocaleParam(endpoint)}`
  const isFormData = typeof FormData !== 'undefined' && options.body instanceof FormData

  const headers: Record<string, string> = {
    ...((options.headers as Record<string, string>) || {}),
  }

  if (!isFormData) {
    headers['Content-Type'] = 'application/json'
  }

  const method = (options.method ?? 'GET').toUpperCase()
  if (method !== 'GET' && method !== 'HEAD') {
    // Bootstrap the CSRF cookie lazily — covers consumers that sign users
    // in via a custom Server Action (the Next.js App Router pattern) where
    // the admin never hits `/auth/login` directly and so never receives the
    // cookie from the login response. Without this, the very first mutation
    // after sign-in 403s.
    if (!getCsrfToken()) {
      await ensureCsrfToken()
    }
    const csrf = getCsrfToken()
    if (csrf) headers['x-csrf-token'] = csrf
  }

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

    if (!res.ok) {
      return { error: json.error || `Request failed (${res.status})`, status: res.status }
    }

    return { data: json.data ?? json, status: res.status }
  } catch (err) {
    return { error: err instanceof Error ? err.message : 'Network error', status: 0 }
  }
}
