import { encodeClientIdParam } from './client-id-util';

type SkipCacheUntil = {
	[key: string]: number;
};

type CachableImage = {
	getUrl: (...props: any[]) => string;
	bustCache: (...props: any[]) => void;
};

export const cachedApplicationLogo: CachableImage = {
	getUrl: (light = true) => {
		const url = new URL('/api/application-images/logo', window.location.origin);
		if (!light) url.searchParams.set('light', 'false');
		return getCachedImageUrl(url);
	},
	bustCache: (light = true) => {
		const url = new URL('/api/application-images/logo', window.location.origin);
		if (!light) url.searchParams.set('light', 'false');
		bustImageCache(url);
	}
};

export const cachedEmailLogo: CachableImage = {
	getUrl: () => getCachedImageUrl(new URL('/api/application-images/email', window.location.origin)),
	bustCache: () => bustImageCache(new URL('/api/application-images/email', window.location.origin))
};

export const cachedDefaultProfilePicture: CachableImage = {
	getUrl: () =>
		getCachedImageUrl(
			new URL('/api/application-images/default-profile-picture', window.location.origin)
		),
	bustCache: () =>
		bustImageCache(
			new URL('/api/application-images/default-profile-picture', window.location.origin)
		)
};

export const cachedBackgroundImage: CachableImage = {
	getUrl: () =>
		getCachedImageUrl(new URL('/api/application-images/background', window.location.origin)),
	bustCache: () =>
		bustImageCache(new URL('/api/application-images/background', window.location.origin))
};

export const cachedProfilePicture: CachableImage = {
	getUrl: (userId: string) => {
		const url = new URL(`/api/users/${userId}/profile-picture.png`, window.location.origin);
		return getCachedImageUrl(url);
	},
	bustCache: (userId: string) => {
		const url = new URL(`/api/users/${userId}/profile-picture.png`, window.location.origin);
		bustImageCache(url);
	}
};

export const cachedOidcClientLogo: CachableImage = {
	getUrl: (clientId: string, light = true) => {
		const url = new URL(
			`/api/oidc/clients/${encodeClientIdParam(clientId)}/logo`,
			window.location.origin
		);
		if (!light) url.searchParams.set('light', 'false');
		return getCachedImageUrl(url);
	},
	bustCache: (clientId: string, light = true) => {
		const url = new URL(
			`/api/oidc/clients/${encodeClientIdParam(clientId)}/logo`,
			window.location.origin
		);
		if (!light) url.searchParams.set('light', 'false');
		bustImageCache(url);
	}
};

function getCachedImageUrl(url: URL) {
	const baseKey = normalizeUrlForKey(url);
	const skipCacheUntil = getSkipCacheUntil(baseKey);
	const skipCache = skipCacheUntil > Date.now();

	const finalUrl = new URL(url.toString());
	if (skipCache) {
		finalUrl.searchParams.set('skip-cache', skipCacheUntil.toString());
	}

	return finalUrl.pathname + (finalUrl.search ? `?${finalUrl.searchParams.toString()}` : '');
}

function bustImageCache(url: URL) {
	const key = normalizeUrlForKey(url);
	const expiresAt = Date.now() + 1000 * 60 * 15;

	const store: SkipCacheUntil = JSON.parse(localStorage.getItem('skip-cache-until') ?? '{}');
	store[key] = expiresAt;
	localStorage.setItem('skip-cache-until', JSON.stringify(store));
}

function getSkipCacheUntil(key: string): number {
	const store: SkipCacheUntil = JSON.parse(localStorage.getItem('skip-cache-until') ?? '{}');
	return store[key] ?? 0;
}

// Removes transient params and normalizes query order before hashing
function normalizeUrlForKey(url: URL) {
	const u = new URL(url.toString());
	u.searchParams.delete('skip-cache');

	const sortedParams = new URLSearchParams(
		[...u.searchParams.entries()].sort(([a], [b]) => a.localeCompare(b))
	);
	const normalized = u.pathname + (sortedParams.toString() ? `?${sortedParams.toString()}` : '');
	return hashKey(normalized);
}

function hashKey(key: string): string {
	let hash = 0;
	for (let i = 0; i < key.length; i++) {
		const char = key.charCodeAt(i);
		hash = (hash << 5) - hash + char;
		hash |= 0;
	}
	return Math.abs(hash).toString(36);
}
