const BASE_URL = '/api/v1'

// ADR: Access-Token bewusst in localStorage (nicht in-memory), Tradeoff akzeptiert.
// Grund: 30min-Lifetime begrenzt XSS-Fenster; Refresh-Token liegt bereits als
// HttpOnly-Cookie (siehe M-2 unten). Voller HttpOnly-Umbau des Access-Tokens
// wäre größerer Architektur-Eingriff (Backend müsste jede Response als Cookie
// setzen) – noch nicht umgesetzt, siehe Security-Review 2026-09.

// Läuft ein Refresh bereits? Damit parallele Requests nicht mehrfach refreshen
let _refreshing: Promise<string | null> | null = null

async function refreshAccessToken(): Promise<string | null> {
  // M-2: Kein refresh_token aus localStorage – Browser schickt HttpOnly-Cookie automatisch mit
  try {
    const res = await fetch(`${BASE_URL}/auth/refresh`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',  // HttpOnly-Cookie mitschicken
    })
    if (!res.ok) {
      // Refresh fehlgeschlagen → ausloggen
      localStorage.removeItem('access_token')
      window.location.href = '/login'
      return null
    }
    const data = await res.json()
    localStorage.setItem('access_token', data.access_token)
    // refresh_token kommt nicht mehr im Body (HttpOnly-Cookie)
    return data.access_token
  } catch {
    localStorage.removeItem('access_token')
    window.location.href = '/login'
    return null
  }
}

async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
  const token = localStorage.getItem('access_token')
  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    ...(options.headers as Record<string, string>),
  }
  if (token) headers['Authorization'] = `Bearer ${token}`

  // credentials: 'include' damit der HttpOnly-Cookie (refresh_token) bei Auth-Requests mitgeschickt wird
  const res = await fetch(`${BASE_URL}${path}`, { ...options, headers, credentials: 'include' })

  // 401 → Token-Refresh versuchen, dann Request wiederholen
  if (res.status === 401 && !path.startsWith('/auth/')) {
    if (!_refreshing) {
      _refreshing = refreshAccessToken().finally(() => { _refreshing = null })
    }
    const newToken = await _refreshing
    if (!newToken) return undefined as T  // Redirect zu /login läuft bereits

    // Request mit neuem Token wiederholen
    const retryHeaders = { ...headers, Authorization: `Bearer ${newToken}` }
    const retry = await fetch(`${BASE_URL}${path}`, { ...options, headers: retryHeaders, credentials: 'include' })
    if (!retry.ok) {
      const err = await retry.json().catch(() => ({ detail: retry.statusText }))
      const msg = typeof err.detail === 'string'
        ? err.detail
        : Array.isArray(err.detail)
          ? err.detail.map((e: { msg: string }) => e.msg).join(', ')
          : retry.statusText
      throw new Error(msg)
    }
    if (retry.status === 204) return undefined as T
    return retry.json()
  }

  if (!res.ok) {
    const err = await res.json().catch(() => ({ detail: res.statusText }))
    const msg = typeof err.detail === 'string'
      ? err.detail
      : Array.isArray(err.detail)
        ? err.detail.map((e: { msg: string }) => e.msg).join(', ')
        : res.statusText
    throw new Error(msg)
  }

  if (res.status === 204) return undefined as T
  return res.json()
}

async function requestForm<T>(path: string, form: FormData): Promise<T> {
  const token = localStorage.getItem('access_token')
  const headers: Record<string, string> = {}
  if (token) headers['Authorization'] = `Bearer ${token}`
  // No Content-Type header – browser sets multipart boundary automatically
  const res = await fetch(`${BASE_URL}${path}`, { method: 'POST', body: form, headers, credentials: 'include' })
  if (!res.ok) {
    const err = await res.json().catch(() => ({ detail: res.statusText }))
    const msg = typeof err.detail === 'string'
      ? err.detail
      : Array.isArray(err.detail)
        ? err.detail.map((e: { msg: string }) => e.msg).join(', ')
        : res.statusText
    throw new Error(msg)
  }
  if (res.status === 204) return undefined as T
  return res.json()
}

export const api = {
  get:      <T>(path: string)                  => request<T>(path),
  post:     <T>(path: string, body: unknown)   => request<T>(path, { method: 'POST',  body: JSON.stringify(body) }),
  patch:    <T>(path: string, body: unknown)   => request<T>(path, { method: 'PATCH', body: JSON.stringify(body) }),
  del:      <T>(path: string)                  => request<T>(path, { method: 'DELETE' }),
  postForm: <T>(path: string, form: FormData)  => requestForm<T>(path, form),
}
