import { ClientResponseError, isXRPCErrorPayload, type XRPCErrorPayload } from '@atcute/client';
import type { BobbinContext, QueryValue, XrpcRequestInit } from './client';

export const buildUrl = (
	origin: string,
	nsid: string,
	params?: Record<string, QueryValue>
): URL => {
	const url = new URL(`/xrpc/${nsid}`, `${origin}/`);
	if (params) {
		for (const [key, value] of Object.entries(params)) {
			if (value === undefined) continue;
			if (Array.isArray(value)) {
				for (const item of value) url.searchParams.append(key, String(item));
			} else {
				url.searchParams.set(key, String(value));
			}
		}
	}
	return url;
};

export const toResponseError = async (response: Response): Promise<ClientResponseError> => {
	let data: XRPCErrorPayload = { error: 'XRPCError', message: response.statusText };
	try {
		const body: unknown = await response.json();
		if (isXRPCErrorPayload(body)) data = body;
	} catch {
		// keep the status-line fallback for non-json bodies.
	}
	return new ClientResponseError({ status: response.status, headers: response.headers, data });
};

export const jsonGet = async <T>(
	ctx: BobbinContext,
	nsid: string,
	params?: Record<string, QueryValue>,
	init?: XrpcRequestInit
): Promise<T> => {
	const response = await ctx.fetch(buildUrl(ctx.serviceUrl, nsid, params), {
		headers: { accept: 'application/json', ...init?.headers },
		signal: init?.signal
	});
	if (!response.ok) throw await toResponseError(response);
	return (await response.json()) as T;
};

export const rawGet = async (
	ctx: BobbinContext,
	nsid: string,
	params?: Record<string, QueryValue>,
	init?: XrpcRequestInit
): Promise<Response> => {
	const response = await ctx.fetch(buildUrl(ctx.serviceUrl, nsid, params), {
		headers: init?.headers,
		signal: init?.signal
	});
	if (!response.ok) throw await toResponseError(response);
	return response;
};
