import { Client, ok, simpleFetchHandler } from '@atcute/client';
import type { Nsid } from '@atcute/lexicons/syntax';
import type { OAuthUserAgent } from '@atcute/oauth-browser-client';

const rpcCache = new Map<string, Client>();

export const normalizeServiceUrl = (input: string): string => {
	if (input.startsWith('http://') || input.startsWith('https://')) {
		return input;
	}

	return `https://${input}`;
};

export const getRpc = (service: string): Client => {
	const normalized = normalizeServiceUrl(service);
	const existing = rpcCache.get(normalized);
	if (existing) {
		return existing;
	}

	const client = new Client({
		handler: simpleFetchHandler({
			service: normalized,
		}),
	});

	rpcCache.set(normalized, client);
	return client;
};

export const createAuthRpc = (agent: OAuthUserAgent): Client =>
	new Client({
		handler: agent,
	});

const serviceAudience = (service: string): `did:web:${string}` => {
	const url = new URL(normalizeServiceUrl(service));
	return `did:web:${url.host}`;
};

export const createServiceAuthRpc = async (
	agent: OAuthUserAgent,
	service: string,
	lxm: Nsid,
): Promise<Client> => {
	const authRpc = createAuthRpc(agent);
	const { token } = await ok(
		authRpc.get('com.atproto.server.getServiceAuth', {
			params: {
				aud: serviceAudience(service),
				exp: Math.floor(Date.now() / 1000) + 60,
				lxm,
			},
		}),
	);
	const serviceUrl = normalizeServiceUrl(service);

	return new Client({
		handler: async (pathname, init) => {
			const headers = new Headers(init.headers);
			headers.set('authorization', `Bearer ${token}`);
			return fetch(new URL(pathname, serviceUrl), {
				...init,
				headers,
			});
		},
	});
};
