import { error, type NumericRange } from "@sveltejs/kit";
import { ClientResponseError } from "./client";

export const httpStatusFor = (cause: unknown): number => {
	if (cause instanceof ClientResponseError) {
		if (cause.status >= 400 && cause.status <= 599) return cause.status;
		switch (cause.error) {
			case "RecordNotFound":
				return 404;
			case "InvalidRequest":
				return 400;
			case "UpstreamFailed":
			case "UpstreamGone":
			case "InvalidRecord":
				return 502;
			case "Overloaded":
				return 503;
			default:
				return 500;
		}
	}
	return 500;
};

export const errorMessage = (cause: unknown, fallbackMessage = "Request failed"): string =>
	cause instanceof ClientResponseError ? (cause.description ?? cause.error) : fallbackMessage;

export const toHttpError = (cause: unknown, fallbackMessage = "Request failed"): never => {
	const status = httpStatusFor(cause) as NumericRange<400, 599>;
	throw error(status, errorMessage(cause, fallbackMessage));
};

export const parallel = async <T extends Record<string, Promise<unknown>>>(
	tasks: T
): Promise<{ [K in keyof T]: Awaited<T[K]> }> => {
	const keys = Object.keys(tasks) as (keyof T)[];
	const values = await Promise.all(keys.map((key) => tasks[key]));
	const out = {} as { [K in keyof T]: Awaited<T[K]> };
	keys.forEach((key, index) => {
		out[key] = values[index] as Awaited<T[keyof T]>;
	});
	return out;
};

// per-request promise de-dupe cache.
export interface RequestCache {
	run<T>(key: string, load: () => Promise<T>): Promise<T>;
}

export const createRequestCache = (): RequestCache => {
	const entries = new Map<string, Promise<unknown>>();
	return {
		run<T>(key: string, load: () => Promise<T>): Promise<T> {
			const existing = entries.get(key) as Promise<T> | undefined;
			if (existing) return existing;
			const pending = load();
			entries.set(key, pending);
			return pending;
		}
	};
};
