import { Client, simpleFetchHandler } from "@atcute/client";
import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
import { mintServiceAuth, serviceDidForHost } from "$lib/auth/agent";

export { ClientResponseError, isXRPCErrorPayload, ok } from "@atcute/client";
export type { XRPCErrorPayload } from "@atcute/client";

// generated lexicons register ambient types; don't import the barrel at runtime.

export interface BobbinContext {
	readonly xrpc: Client;
	readonly serviceUrl: string;
	readonly fetch: typeof globalThis.fetch;
}

export interface CreateBobbinOptions {
	serviceUrl: string;
	fetch?: typeof globalThis.fetch;
	/** when set, every xrpc request carries a freshly minted service-auth jwt */
	agent?: OAuthUserAgent;
}

const nsidOf = (input: RequestInfo | URL): string | null => {
	const href = input instanceof Request ? input.url : String(input);
	let pathname: string;
	try {
		pathname = new URL(href).pathname;
	} catch {
		return null;
	}
	const [, prefix, nsid, ...rest] = pathname.split("/");
	return prefix === "xrpc" && nsid && rest.length === 0 ? nsid : null;
};

const withServiceAuth = (
	base: typeof globalThis.fetch,
	agent: OAuthUserAgent,
	origin: string
): typeof globalThis.fetch => {
	const aud = serviceDidForHost(new URL(origin).host);
	return async (input, init) => {
		const lxm = nsidOf(input);
		if (!lxm) return base(input, init);

		const token = await mintServiceAuth(agent, { aud, lxm });
		const headers = new Headers(init?.headers);
		headers.set("authorization", `Bearer ${token}`);
		return base(input, { ...init, headers });
	};
};

export const createBobbinClient = ({ serviceUrl, fetch, agent }: CreateBobbinOptions): BobbinContext => {
	const origin = serviceUrl.replace(/\/+$/, "");
	const baseFetch = fetch ?? globalThis.fetch;
	const boundFetch = agent ? withServiceAuth(baseFetch, agent, origin) : baseFetch;
	const xrpc = new Client({ handler: simpleFetchHandler({ service: origin, fetch: boundFetch }) });
	return { xrpc, serviceUrl: origin, fetch: boundFetch };
};

export type QueryValue = string | number | boolean | readonly (string | number)[] | undefined;

export interface XrpcRequestInit {
	signal?: AbortSignal;
	headers?: HeadersInit;
}
