const BASE_URL = '/api';

async function readResponseBody(response: Response) {
  const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
  const isJson = contentType.includes('application/json');

  if (isJson) {
    return {
      isJson: true,
      data: (await response.json()) as unknown
    };
  }

  return {
    isJson: false,
    data: await response.text()
  };
}

function buildNonJsonError(endpoint: string, status: number, statusText: string) {
  return new Error(
    `API ${endpoint} returned a non-JSON response (${status} ${statusText}). ` +
      'This usually means the request was redirected to an HTML page or hit an unexpected server error.'
  );
}

export async function apiClient<T>(endpoint: string, options?: RequestInit): Promise<T> {
  const isServer = typeof window === 'undefined';
  const requestHeaders = isServer ? await (await import('next/headers')).headers() : null;
  const host = requestHeaders?.get('x-forwarded-host') ?? requestHeaders?.get('host');
  const protocol = requestHeaders?.get('x-forwarded-proto') ?? 'http';
  const origin = isServer && host ? `${protocol}://${host}` : '';
  const optionHeaders = options?.headers;
  const restOptions = options ? { ...options } : null;
  const isFormData = typeof FormData !== 'undefined' && options?.body instanceof FormData;

  if (restOptions && 'headers' in restOptions) {
    delete restOptions.headers;
  }

  const res = await fetch(`${origin}${BASE_URL}${endpoint}`, {
    ...restOptions,
    headers: {
      ...(!isFormData ? { 'Content-Type': 'application/json' } : {}),
      ...(isServer && requestHeaders?.get('cookie')
        ? { cookie: requestHeaders.get('cookie') as string }
        : {}),
      ...optionHeaders
    }
  });

  const body = await readResponseBody(res);

  if (!res.ok) {
    if (body.isJson) {
      const errorBody = body.data as { message?: string };
      throw new Error(errorBody.message || `API error: ${res.status} ${res.statusText}`);
    }

    throw buildNonJsonError(endpoint, res.status, res.statusText);
  }

  if (!body.isJson) {
    throw buildNonJsonError(endpoint, res.status, res.statusText);
  }

  return body.data as T;
}
