const API_URL = process.env.NEXT_PUBLIC_API_URL?.replace(/\+$/,"");

if (!API_URL){
    throw new Error("NEXT_PUBLIC_API_URL is not configured");
}

export class ApiError extends Error{
    constructor(
        public status: number,
        message: string
    ){
        super(message);
        this.name = "ApiError";
    }
}


let refreshPromise: Promise<boolean> | null = null;

async function refreshAccessToken(): Promise<boolean> {
  if (!refreshPromise) {
    refreshPromise = fetch(
      `${API_URL}/api/v1/auth/refresh`,
      {
        method: "POST",
        credentials: "include",
        cache: "no-store",
      }
    )
      .then((response) => response.ok)
      .catch(() => false)
      .finally(() => {
        refreshPromise = null;
      });
  }

  return refreshPromise;
}

export async function apiClient<T>(
  endpoint: string,
  options: RequestInit = {}
): Promise<T> {
  const url = `${API_URL}/api/v1${endpoint}`;

  const request = () =>
    fetch(url, {
      ...options,
      credentials: "include",
      cache: "no-store",
    });

  let response = await request();

  if (response.status === 401) {
    const refreshed = await refreshAccessToken();

    if (refreshed) {
      response = await request();
    }
  }

  if (!response.ok) {
    const errorBody: unknown = await response.json().catch(() => null);
    const rawMessage =
      typeof errorBody === "object" &&
      errorBody !== null &&
      "message" in errorBody
        ? errorBody.message
        : null;
    const bodyMessage =
      typeof rawMessage === "string"
        ? rawMessage
        : Array.isArray(rawMessage)
          ? rawMessage.join(", ")
          : null;

    throw new ApiError(
      response.status,
      bodyMessage ?? `API request failed: ${response.status}`
    );
  }

  return response.json() as Promise<T>;
}