import type { Cookies, ServerLoadEvent } from "@sveltejs/kit";

export type FetchOptions = {
	fetchFn?: typeof fetch;
	token?: string;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	data?: any;
	version?: number;
	extraHeaders?: Record<string, string>;
};

/**
 * Fetch a path from the API and parse the response.
 * To make sure the request is authenticated in load functions,
 * pass `fetch` from the request object into opts.
 *
 * @param method The HTTP method, i.e. GET, POST, PATCH
 * @param path The path to request, minus the leading `/api/v2`
 * @param opts Extra options for this request
 * @returns T
 * @throws APIError
 */
export default async function request<T>(
	method: string,
	path: string,
	opts: FetchOptions = {},
): Promise<T> {
	const { token, data, version, extraHeaders } = opts;
	const fetchFn = opts.fetchFn ?? fetch;

	const resp = await fetchFn(`/api/v${version ?? 2}${path}`, {
		method,
		body: data ? JSON.stringify(data) : undefined,
		headers: {
			...extraHeaders,
			...(token ? { Authorization: token } : {}),
			"Content-Type": "application/json",
		},
	});

	if (resp.status < 200 || resp.status >= 400) throw await resp.json();
	return (await resp.json()) as T;
}

/**
 * Fetch a path from the API and discard the response.
 * To make sure the request is authenticated in load functions,
 * pass `fetch` from the request object into opts.
 *
 * @param method The HTTP method, i.e. GET, POST, PATCH
 * @param path The path to request, minus the leading `/api/v2`
 * @param opts Extra options for this request
 * @throws APIError
 */
export async function fastRequest(
	method: string,
	path: string,
	opts: FetchOptions = {},
): Promise<void> {
	const { token, data, version, extraHeaders } = opts;
	const fetchFn = opts.fetchFn ?? fetch;

	const resp = await fetchFn(`/api/v2${version ?? 2}${path}`, {
		method,
		body: data ? JSON.stringify(data) : undefined,
		headers: {
			...extraHeaders,
			...(token ? { Authorization: token } : {}),
			"Content-Type": "application/json",
		},
	});

	if (resp.status < 200 || resp.status >= 400) throw await resp.json();
}

/**
 * Helper function to get a token from a request cookie.
 * Accepts both a cookie object ({ cookies }) or a request object (req).
 * @param s A Cookies or ServerLoadEvent object
 * @returns A token, or `undefined` if no token is set.
 */
export const getToken = (s: Cookies | ServerLoadEvent) =>
	"cookies" in s ? s.cookies.get("pronounscc-token") : s.get("pronounscc-token");