import { error, redirect } from "@sveltejs/kit";
import { createBobbinClient } from "$lib/api/client";
import type { MiniDoc } from "$lib/api/identity";
import { getProfile, type ProfileRecord } from "$lib/api/records";
import {
	enrich,
	countOf,
	viewerUriOf,
	TYPE_COUNT,
	TYPE_VIEWER,
	type LinkDescriptor,
	type LinkSource
} from "$lib/api/enrich";
import { toHttpError, httpStatusFor } from "$lib/api/load";
import { ClientResponseError } from "$lib/api/client";
import { rkeyFromUri } from "$lib/api/uri";
import type { ProfileCounts } from "$lib/components/profile/types";
import type { LayoutLoad } from "./$types";

const REPOS: LinkSource = "sh.tangled.repo:subject";
const STRINGS: LinkSource = "sh.tangled.string:subject";
const STARS: LinkSource = "sh.tangled.feed.star:.repo";
const FOLLOWERS: LinkSource = "sh.tangled.graph.follow:subject";
const FOLLOWING: LinkSource = "sh.tangled.graph.follow:.repo";
const VOUCHES: LinkSource = "sh.tangled.graph.vouch:subject";

const COUNT_DESCRIPTORS: LinkDescriptor[] = [
	REPOS,
	STRINGS,
	STARS,
	FOLLOWERS,
	FOLLOWING,
	VOUCHES
].map((source) => ({ source, type: TYPE_COUNT }));

export const load: LayoutLoad = async (event) => {
	const parent = await event.parent();
	const identifier = decodeURIComponent(event.params.handle);

	// rejects bare words so unrelated paths 404 instead of resolving as actors
	if (!identifier.startsWith("did:") && !identifier.includes(".")) {
		error(404, "Not found");
	}

	const ctx = createBobbinClient({ serviceUrl: parent.publicConfig.bobbinUrl, fetch: event.fetch });
	const viewerDid = parent.auth?.did;
	const resolved = await enrich<MiniDoc>(ctx, {
		xrpc: "com.bad-example.identity.resolveMiniDoc",
		params: { identifier },
		enrich: [
			...COUNT_DESCRIPTORS.map((descriptor) => ({ ...descriptor, targets: ["did"] })),
			...(viewerDid ? [{ source: FOLLOWERS, type: TYPE_VIEWER, targets: ["did"] }] : [])
		],
		...(viewerDid ? { viewer: viewerDid } : {})
	}).catch((cause) => toHttpError(cause, "Could not resolve user"));
	const doc = resolved.output;

	// redirects dids and stale handles to the canonical handle url
	const canonical = doc.handle && !doc.handle.endsWith(".invalid") ? doc.handle : null;
	if (canonical && identifier.toLowerCase() !== canonical.toLowerCase()) {
		redirect(307, `/${canonical}${event.url.search}`);
	}

	const did = doc.did;

	const profile: ProfileRecord | null = await getProfile(ctx, did)
		.then((view) => view.value)
		.catch((cause) => {
			if (cause instanceof ClientResponseError && httpStatusFor(cause) === 404) return null;
			return toHttpError(cause, "Could not load profile");
		});

	const counts: ProfileCounts = {
		repos: countOf(resolved.data, did, REPOS),
		strings: countOf(resolved.data, did, STRINGS),
		stars: countOf(resolved.data, did, STARS),
		followers: countOf(resolved.data, did, FOLLOWERS),
		following: countOf(resolved.data, did, FOLLOWING),
		vouches: countOf(resolved.data, did, VOUCHES)
	};
	const viewerFollowUri =
		viewerDid && viewerDid !== did ? viewerUriOf(resolved.data, did, FOLLOWERS) : null;
	const viewerFollowRkey = viewerFollowUri ? rkeyFromUri(viewerFollowUri) : viewerFollowUri;

	const notJoined = !profile && Object.values(counts).every((n) => n === 0);

	return {
		identity: { did, handle: doc.handle },
		profile,
		counts,
		viewerFollowRkey,
		notJoined
	};
};
