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 toHttpError = (cause: unknown, fallbackMessage = 'Request failed'): never => {
	const status = httpStatusFor(cause) as NumericRange<400, 599>;
	const message =
		cause instanceof ClientResponseError ? (cause.description ?? cause.error) : fallbackMessage;
	throw error(status, message);
};

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;
		}
	};
};
