export interface FakeFetchRule {
  urlPattern: RegExp;
  response: Response | (() => Response);
}

export interface FakeFetchCall {
  url: string;
  init: RequestInit | undefined;
}

export interface FakeFetch {
  fakeFetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
  calls: FakeFetchCall[];
  reset: () => void;
}

export function createFakeFetch(rules: FakeFetchRule[]): FakeFetch {
  const calls: FakeFetchCall[] = [];

  const fakeFetch = (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
    const url =
      typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;

    calls.push({ url, init });

    for (const rule of rules) {
      if (rule.urlPattern.test(url)) {
        // Clone static responses so repeated calls each get a fresh, unread body.
        const response =
          typeof rule.response === "function"
            ? rule.response()
            : (rule.response.clone() as Response);
        return Promise.resolve(response);
      }
    }

    throw new Error(`No fake-fetch rule matched URL: ${url}`);
  };

  const reset = (): void => {
    calls.length = 0;
  };

  return { fakeFetch, calls, reset };
}

export function jsonResponse(body: unknown, status = 200): Response {
  return new Response(JSON.stringify(body), {
    status,
    headers: { "Content-Type": "application/json" },
  });
}

export function errorResponse(status: number, statusText = "", body = ""): Response {
  return new Response(body, { status, statusText });
}
