import { goto } from "$app/navigation";
import { browser } from "$app/environment";

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

async function doFetch<T>(path: string, options?: RequestInit): Promise<T> {
  const res = await fetch(path, options);

  if (!res.ok) {
    let message = `Request failed with status ${res.status}`;
    let details: unknown;
    try {
      const body = await res.json();
      message = body?.message ?? message;
      details = body;
    } catch {
      // response body was not JSON, use default message
    }
    throw new ClientFetchError(res.status, message, details);
  }

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

export async function clientFetch<T = unknown>(
  path: string,
  options?: RequestInit,
): Promise<T> {
  try {
    return await doFetch<T>(path, options);
  } catch (err) {
    if (
      err instanceof ClientFetchError &&
      (err.details as any)?.error === "ExpiredAccessTokenError" &&
      browser
    ) {
      // Attempt a token refresh via the server, then retry once
      const refreshRes = await fetch("/api/auth/refresh");
      if (!refreshRes.ok) {
        goto("/login");
        throw new ClientFetchError(
          401,
          "Session expired — redirecting to login",
        );
      }
      // The refresh set new cookies; just retry the original request
      return doFetch<T>(path, options);
    }
    throw err;
  }
}

// Convenience alias
export const apiFetch = clientFetch;
