import { Temporal } from "@js-temporal/polyfill";
import "urlpattern-polyfill";
globalThis.addEventListener = () => {};
import { U as getTypeId, ot as name, st as version, t as Activity } from "./vocab-DuJO9k13.mjs";
import { getLogger } from "@logtape/logtape";
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
//#region src/federation/inbox.ts
var InboxListenerSet = class InboxListenerSet {
	#listeners;
	constructor() {
		this.#listeners = /* @__PURE__ */ new Map();
	}
	clone() {
		const clone = new InboxListenerSet();
		clone.#listeners = new Map(this.#listeners);
		return clone;
	}
	add(type, listener) {
		if (this.#listeners.has(type)) throw new TypeError("Listener already set for this type.");
		this.#listeners.set(type, listener);
	}
	dispatchWithClass(activity) {
		let cls = activity.constructor;
		const inboxListeners = this.#listeners;
		if (inboxListeners == null) return null;
		while (true) {
			if (inboxListeners.has(cls)) break;
			if (cls === Activity) return null;
			cls = globalThis.Object.getPrototypeOf(cls);
		}
		const listener = inboxListeners.get(cls);
		return {
			class: cls,
			listener
		};
	}
	dispatch(activity) {
		return this.dispatchWithClass(activity)?.listener ?? null;
	}
};
let warnedAboutDefaultIdempotency = false;
async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
	const logger = getLogger([
		"fedify",
		"federation",
		"inbox"
	]);
	let cacheKey = null;
	if (activity.id != null) {
		const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
		const strategy = idempotencyStrategy ?? "per-origin";
		if (idempotencyStrategy === void 0 && !warnedAboutDefaultIdempotency) {
			logger.warn("Using default idempotency strategy 'per-origin'. This default will change to 'per-inbox' in Fedify 2.0. Please explicitly set the idempotency strategy using .withIdempotency().");
			warnedAboutDefaultIdempotency = true;
		}
		let keyString;
		if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
		else switch (strategy) {
			case "global":
				keyString = activity.id.href;
				break;
			case "per-origin":
				keyString = `${ctx.origin}\n${activity.id.href}`;
				break;
			case "per-inbox":
				keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
				break;
			default: keyString = `${ctx.origin}\n${activity.id.href}`;
		}
		if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
	}
	if (cacheKey != null) {
		if (await kv.get(cacheKey) === true) {
			logger.debug("Activity {activityId} has already been processed.", {
				activityId: activity.id?.href,
				activity: json,
				recipient
			});
			span.setStatus({
				code: SpanStatusCode.UNSET,
				message: `Activity ${activity.id?.href} has already been processed.`
			});
			return "alreadyProcessed";
		}
	}
	if (activity.actorId == null) {
		logger.error("Missing actor.", { activity: json });
		span.setStatus({
			code: SpanStatusCode.ERROR,
			message: "Missing actor."
		});
		return "missingActor";
	}
	span.setAttribute("activitypub.actor.id", activity.actorId.href);
	if (queue != null) {
		const carrier = {};
		propagation.inject(context.active(), carrier);
		try {
			await queue.enqueue({
				type: "inbox",
				id: crypto.randomUUID(),
				baseUrl: ctx.origin,
				activity: json,
				identifier: recipient,
				attempt: 0,
				started: (/* @__PURE__ */ new Date()).toISOString(),
				traceContext: carrier
			});
		} catch (error) {
			logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
				error,
				activityId: activity.id?.href,
				activity: json,
				recipient
			});
			span.setStatus({
				code: SpanStatusCode.ERROR,
				message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
			});
			throw error;
		}
		logger.info("Activity {activityId} is enqueued.", {
			activityId: activity.id?.href,
			activity: json,
			recipient
		});
		return "enqueued";
	}
	tracerProvider = tracerProvider ?? trace.getTracerProvider();
	return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
		const dispatched = inboxListeners?.dispatchWithClass(activity);
		if (dispatched == null) {
			logger.error("Unsupported activity type:\n{activity}", {
				activity: json,
				recipient
			});
			span.setStatus({
				code: SpanStatusCode.UNSET,
				message: `Unsupported activity type: ${getTypeId(activity).href}`
			});
			span.end();
			return "unsupportedActivity";
		}
		const { class: cls, listener } = dispatched;
		span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
		try {
			await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
		} catch (error) {
			try {
				await inboxErrorHandler?.(ctx, error);
			} catch (error) {
				logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
					error,
					activityId: activity.id?.href,
					activity: json,
					recipient
				});
			}
			logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
				error,
				activityId: activity.id?.href,
				activity: json,
				recipient
			});
			span.setStatus({
				code: SpanStatusCode.ERROR,
				message: String(error)
			});
			span.end();
			return "error";
		}
		if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
		logger.info("Activity {activityId} has been processed.", {
			activityId: activity.id?.href,
			activity: json,
			recipient
		});
		span.end();
		return "success";
	});
}
//#endregion
export { routeActivity as n, InboxListenerSet as t };
