const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL || '/api').replace(/\/$/, '')
const API_PREFIX = '/v1'

let accessToken: string | null = null

export class ApiError extends Error {
  constructor(
    message: string,
    public status: number,
    public details?: unknown,
  ) {
    super(message)
    this.name = 'ApiError'
  }
}

export function setApiAccessToken(token: string | null) {
  accessToken = token
}

export function getApiAccessToken() {
  return accessToken
}

function makeUrl(path: string, query?: Record<string, unknown>) {
  const normalizedPath = path.startsWith('/') ? path : `/${path}`
  const url = new URL(`${API_BASE_URL}${API_PREFIX}${normalizedPath}`, window.location.origin)

  Object.entries(query ?? {}).forEach(([key, value]) => {
    if (value === undefined || value === null || value === '') return
    url.searchParams.set(key, String(value))
  })

  return url.toString()
}

export function buildApiUrl(path: string, query?: Record<string, unknown>) {
  return makeUrl(path, query)
}

async function parseResponse(response: Response) {
  if (response.status === 204) return undefined

  const contentType = response.headers.get('content-type') ?? ''
  if (contentType.includes('application/json')) return response.json()

  const text = await response.text()
  return text || undefined
}

export async function apiRequest<T>(
  path: string,
  options: RequestInit & { query?: Record<string, unknown> } = {},
): Promise<T> {
  const headers = new Headers(options.headers)
  if (!headers.has('Accept')) headers.set('Accept', 'application/json')
  if (options.body && !headers.has('Content-Type')) headers.set('Content-Type', 'application/json')
  if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`)

  const response = await fetch(makeUrl(path, options.query), {
    ...options,
    headers,
    credentials: 'include',
  })
  const body = await parseResponse(response)

  if (!response.ok) {
    const message =
      typeof body === 'object' && body && 'message' in body
        ? String((body as { message: unknown }).message)
        : typeof body === 'object' && body && 'detail' in body
          ? String((body as { detail: unknown }).detail)
          : `API request failed with status ${response.status}`
    throw new ApiError(message, response.status, body)
  }

  return body as T
}

export function extractItems<T>(payload: T[] | { items?: T[] } | undefined): T[] {
  if (Array.isArray(payload)) return payload
  return payload?.items ?? []
}


export async function apiRequestBlob(
  path: string,
  options: RequestInit & { query?: Record<string, unknown> } = {},
): Promise<Blob> {
  const headers = new Headers(options.headers)
  if (!headers.has('Accept')) headers.set('Accept', 'text/calendar')
  if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`)

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

  if (!response.ok) {
    const body = await parseResponse(response)
    const message =
      typeof body === 'object' && body && 'message' in body
        ? String((body as { message: unknown }).message)
        : `API request failed with status ${response.status}`
    throw new ApiError(message, response.status, body)
  }

  return response.blob()
}
