const BASE_URL = "/api";

interface ApiErrorPayload {
  message?: string;
  error?: string;
}

export class ApiClientError extends Error {
  status: number;
  statusText: string;
  payload: unknown;

  constructor(
    message: string,
    input: { status: number; statusText: string; payload: unknown },
  ) {
    super(message);
    this.name = "ApiClientError";
    this.status = input.status;
    this.statusText = input.statusText;
    this.payload = input.payload;
  }
}

async function readErrorPayload(response: Response): Promise<unknown> {
  const contentType = response.headers.get("content-type") ?? "";

  if (contentType.includes("application/json")) {
    return response.json().catch(() => null);
  }

  return response.text().catch(() => null);
}

function extractApiErrorMessage(payload: unknown, response: Response): string {
  if (payload && typeof payload === "object") {
    const candidate = payload as ApiErrorPayload;
    if (typeof candidate.message === "string" && candidate.message.trim()) {
      return candidate.message.trim();
    }
    if (typeof candidate.error === "string" && candidate.error.trim()) {
      return candidate.error.trim();
    }
  }

  if (typeof payload === "string" && payload.trim()) {
    return payload.trim();
  }

  return `API error: ${response.status} ${response.statusText}`;
}

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;

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

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

  if (!response.ok) {
    const payload = await readErrorPayload(response);
    const message = extractApiErrorMessage(payload, response);
    throw new ApiClientError(message, {
      status: response.status,
      statusText: response.statusText,
      payload,
    });
  }

  return response.json() as Promise<T>;
}
