import clsx from 'clsx';
import {
	BookMarked,
	Link,
	LoaderCircle,
	MapPin,
	Rss,
	Star,
	UserRoundPlus,
	UserRoundMinus,
	Users,
	GalleryVertical,
	Shield,
	ShieldCheck,
	ShieldAlert,
	ArrowRight,
	X,
	Check,
	Pencil,
	LineSquiggle,
} from 'lucide-solid';
import { A, useParams, useSearchParams, useNavigate } from '@solidjs/router';
import { createQuery, useQueryClient } from '../lib/atproto-query';
import { For, Show, Switch, Match, createMemo, createSignal, createEffect, type Component } from 'solid-js';
import type { Did, ResourceUri } from '@atcute/lexicons/syntax';
import { ErrorState, LoadingState, PlaceholderAvatar, SkeletonBlock, textareaStyles, inputStyles } from '../components/common';
import { RepoCard, RepoCardSkeleton, StringCardSkeleton } from '../components/repo';
import { useStringPreloader } from '../lib/preloading';
import {
	listFollowRecords,
	createFollow,
	deleteFollow,
	getFollowers,
	getFollowing,
	getVouchRecord,
	putVouchRecord,
	deleteVouchRecord,
	type FollowerFollowingItem,
	type FollowRecord,
	type VouchRecord,
} from '../lib/api/graph';
import { resolveActor, getActorProfile, resolveAvatarUrl, putActorProfile } from '../lib/api/identity';
import { listRepoRecords, getRepoByDid, getRepoByUri, type RepoContext } from '../lib/api/repos';
import { listStringRecords } from '../lib/api/strings';
import { listStarRecordsByActor } from '../lib/api/stars';
import { listAllAppviewRecords } from '../lib/api/appview';
import { useAuth } from '../lib/auth';
import { formatRelativeTime, getErrorMessage } from '../lib/repo-utils';
import { ShTangledActorProfile } from '@atcute/tangled';

const ProfileTabs: Component<{
	activeTab: string;
	reposCount: number;
	starsCount: number;
	stringsCount: number;
	reposLoading?: boolean;
	starsLoading?: boolean;
	stringsLoading?: boolean;
}> = (props) => {
	const tabs = [
		{ id: 'overview', label: 'overview', icon: () => <GalleryVertical class="w-4 h-4 mr-2" /> },
		{ id: 'repos', label: 'repos', icon: () => <BookMarked class="w-4 h-4 mr-2" />, count: () => props.reposCount, loading: () => props.reposLoading },
		{ id: 'starred', label: 'starred', icon: () => <Star class="w-4 h-4 mr-2" />, count: () => props.starsCount, loading: () => props.starsLoading },
		{ id: 'strings', label: 'strings', icon: () => <LineSquiggle class="w-4 h-4 mr-2" />, count: () => props.stringsCount, loading: () => props.stringsLoading },
	];

	return (
		<nav class="w-full pl-4 overflow-x-auto overflow-y-hidden">
			<div class="flex z-60">
				<For each={tabs}>
					{(tab) => {
						const isActive = () => props.activeTab === tab.id;
						return (
							<A
								href={`?tab=${tab.id}`}
								class="relative -mr-px group no-underline hover:no-underline"
							>
								<div
									class={clsx(
										'px-4 py-1 mr-1 text-black dark:text-white text-base min-w-[80px] text-center relative rounded-t whitespace-nowrap',
										isActive()
											? '-mb-px bg-white dark:bg-gray-800 font-medium'
											: 'group-hover:bg-gray-100/25 group-hover:dark:bg-gray-700/25'
									)}
								>
									<span class="flex items-center justify-center">
										{tab.icon()}
										{tab.label}
										{tab.count !== undefined && (
											<span class="bg-gray-200 dark:bg-gray-700 rounded py-0.5 px-1 text-sm ml-1 min-w-[20px] inline-flex items-center justify-center">
												<Show when={!tab.loading?.()} fallback={<SkeletonBlock class="h-3 w-4" />}>
													{tab.count()}
												</Show>
											</span>
										)}
									</span>
								</div>
							</A>
						);
					}}
				</For>
			</div>
		</nav>
	);
};

const ProfileCardComponent: Component<{
	actor: any;
	profile?: any;
	followersCount: number;
	followingCount: number;
	loggedInUserDid?: string;
	isFollowing: boolean;
	onFollowToggle: () => void;
	actionLoading: boolean;
	onTabChange: (tab: string) => void;
	vouchRecord?: VouchRecord | null;
	onVouchSubmit: (kind: 'vouch' | 'denounce' | 'none', reason?: string) => Promise<void>;
	vouchActionLoading: boolean;
	onProfileUpdate: (profile: ShTangledActorProfile.Main) => Promise<void>;
	profileUpdateLoading: boolean;
	reposCount: number;
	starsCount: number;
	mergedPRCount: number;
	closedPRCount: number;
	openPRCount: number;
	openIssueCount: number;
	closedIssueCount: number;
	profileLoading?: boolean;
	followersLoading?: boolean;
	followingLoading?: boolean;
	vouchLoading?: boolean;
}> = (props) => {
	const userIdent = () => props.actor.handle || props.actor.did;
	
	const pronouns = () => props.profile?.pronouns;
	const description = () => props.profile?.description;
	const location = () => props.profile?.location;
	const websiteLinks = () => props.profile?.links ?? [];
	const includeBluesky = () => props.profile?.bluesky;

	const isSelf = () => props.loggedInUserDid === props.actor.did;

	const [kind, setKind] = createSignal<'vouch' | 'denounce' | 'none'>('none');
	const [reason, setReason] = createSignal('');
	let popoverRef: HTMLDivElement | undefined;

	createEffect(() => {
		const record = props.vouchRecord;
		if (record) {
			setKind(record.value.kind);
			setReason(record.value.reason || '');
		} else {
			setKind('none');
			setReason('');
		}
	});

	const popoverId = () => `vouch-modal-${props.actor.did.replace(/[^a-zA-Z0-9]/g, '-')}`;

	const isVouched = () => props.vouchRecord?.value.kind === 'vouch';
	const isDenounced = () => props.vouchRecord?.value.kind === 'denounce';

	const handleFollowClick = () => {
		if (!props.loggedInUserDid) {
			alert('Please sign in to follow users.');
			return;
		}
		props.onFollowToggle();
	};

	const handleVouchClick = (e: MouseEvent) => {
		if (!props.loggedInUserDid) {
			e.preventDefault();
			alert('Please sign in to vouch for users.');
		}
	};

	const [isEditing, setIsEditing] = createSignal(false);
	const [editBioText, setEditBioText] = createSignal('');
	const [editPronounsText, setEditPronounsText] = createSignal('');
	const [editLocationText, setEditLocationText] = createSignal('');
	const [editBluesky, setEditBluesky] = createSignal(false);
	const [editLinks, setEditLinks] = createSignal<string[]>(['', '', '', '', '']);
	const [editStat1, setEditStat1] = createSignal('');
	const [editStat2, setEditStat2] = createSignal('');

	const startEditing = () => {
		const p = props.profile;
		setEditBioText(p?.description || '');
		setEditPronounsText(p?.pronouns || '');
		setEditLocationText(p?.location || '');
		setEditBluesky(!!p?.bluesky);
		
		const existingLinks = p?.links || [];
		const newLinks = ['', '', '', '', ''];
		for (let i = 0; i < 5; i++) {
			newLinks[i] = existingLinks[i] || '';
		}
		setEditLinks(newLinks);

		const existingStats = p?.stats || [];
		setEditStat1(existingStats[0] || '');
		setEditStat2(existingStats[1] || '');

		setIsEditing(true);
	};

	const handleSubmitProfile = async (e: SubmitEvent) => {
		e.preventDefault();
		const finalLinks = editLinks().map(l => l.trim()).filter(Boolean);
		const finalStats = [editStat1(), editStat2()].filter(Boolean);
		
		const updatedProfile: ShTangledActorProfile.Main = {
			$type: 'sh.tangled.actor.profile',
			...props.profile,
			description: editBioText().trim() || undefined,
			pronouns: editPronounsText().trim() || undefined,
			location: editLocationText().trim() || undefined,
			bluesky: editBluesky(),
			links: finalLinks.length > 0 ? finalLinks : undefined,
			stats: finalStats.length > 0 ? finalStats : undefined,
		};

		await props.onProfileUpdate(updatedProfile);
		setIsEditing(false);
	};

	const getStatValue = (kind: string) => {
		if (kind === 'repository-count') return props.reposCount;
		if (kind === 'star-count') return props.starsCount;
		if (kind === 'merged-pull-request-count') return props.mergedPRCount;
		if (kind === 'closed-pull-request-count') return props.closedPRCount;
		if (kind === 'open-pull-request-count') return props.openPRCount;
		if (kind === 'open-issue-count') return props.openIssueCount;
		if (kind === 'closed-issue-count') return props.closedIssueCount;
		return 0;
	};

	const formatStatLabel = (kind: string) => {
		const labels: Record<string, string> = {
			'merged-pull-request-count': 'merged prs',
			'closed-pull-request-count': 'closed prs',
			'open-pull-request-count': 'open prs',
			'open-issue-count': 'open issues',
			'closed-issue-count': 'closed issues',
			'repository-count': 'repos',
			'star-count': 'stars received',
		};
		return labels[kind] || kind;
	};

	const avatarQuery = createQuery(() => ({
		queryKey: ['avatar', props.actor.did],
		queryFn: async () => resolveAvatarUrl(props.actor.did),
		staleTime: 300_000,
	}));

	return (
		<div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center">
			{/* Avatar */}
			<div id="avatar" class="col-span-1 flex justify-center items-center">
				<div class="w-3/4 aspect-square relative">
					<Show
						when={avatarQuery.data}
						fallback={<PlaceholderAvatar size="absolute inset-0 w-full h-full p-2" iconSize="size-1/2" />}
					>
						<img
							class="absolute inset-0 w-full h-full object-cover rounded-full p-2"
							src={avatarQuery.data!}
							alt={userIdent()}
						/>
					</Show>
				</div>
			</div>

			{/* Details */}
			<div class="col-span-2">
				<div class="flex items-center flex-row flex-nowrap gap-2">
					<p title={userIdent()} class="text-lg font-bold dark:text-white overflow-hidden text-ellipsis whitespace-nowrap">
						{userIdent()}
					</p>
					<Show when={!props.profileLoading} fallback={<SkeletonBlock class="h-4 w-12" />}>
						<Show when={pronouns()}>
							<p class="text-gray-500 dark:text-gray-400">{pronouns()}</p>
						</Show>
					</Show>
				</div>

				<div class="md:hidden">
					<div class="flex items-center gap-2 my-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full text-sm">
						<span class="flex-shrink-0"><Users class="size-4" /></span>
						<span id="followers">
							<A href="?tab=followers">
								<Show when={!props.followersLoading} fallback={<SkeletonBlock class="h-4 w-8 inline-block mr-1 align-middle" />}>
									{props.followersCount}
								</Show>
								{' '}followers
							</A>
						</span>
						<span class="select-none after:content-['·']"></span>
						<span id="following">
							<A href="?tab=following">
								<Show when={!props.followingLoading} fallback={<SkeletonBlock class="h-4 w-8 inline-block mr-1 align-middle" />}>
									{props.followingCount}
								</Show>
								{' '}following
							</A>
						</span>
					</div>
				</div>
			</div>

			{/* Bio and links */}
			<div class="col-span-3 md:col-span-full">
				<div id="profile-bio" class="text-sm space-y-2">
					<Show
						when={isEditing()}
						fallback={
							<>
								<Show
									when={!props.profileLoading}
									fallback={
										<div class="space-y-2 pb-4 md:pb-2">
											<SkeletonBlock class="h-4 w-full" />
											<SkeletonBlock class="h-4 w-5/6" />
										</div>
									}
								>
									<Show when={description()}>
										<p class="text-base pb-4 md:pb-2">{description()}</p>
									</Show>
								</Show>

								<div class="hidden md:block">
									<div class="flex items-center gap-2 my-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full text-sm">
										<span class="flex-shrink-0"><Users class="size-4" /></span>
										<span id="followers">
											<A href="?tab=followers">
												<Show when={!props.followersLoading} fallback={<SkeletonBlock class="h-4 w-8 inline-block mr-1 align-middle" />}>
													{props.followersCount}
												</Show>
												{' '}followers
											</A>
										</span>
										<span class="select-none after:content-['·']"></span>
										<span id="following">
											<A href="?tab=following">
												<Show when={!props.followingLoading} fallback={<SkeletonBlock class="h-4 w-8 inline-block mr-1 align-middle" />}>
													{props.followingCount}
												</Show>
												{' '}following
											</A>
										</span>
									</div>
								</div>

								<Show
									when={!props.profileLoading}
									fallback={
										<div class="flex flex-col gap-2 mb-2">
											<div class="flex items-center gap-2">
												<SkeletonBlock class="size-4 shrink-0 rounded-full" />
												<SkeletonBlock class="h-4 w-24" />
											</div>
											<div class="flex items-center gap-2">
												<SkeletonBlock class="size-4 shrink-0 rounded-full" />
												<SkeletonBlock class="h-4 w-36" />
											</div>
										</div>
									}
								>
									<div class="flex flex-col gap-2 mb-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full">
										<Show when={location()}>
											<div class="flex items-center gap-2">
												<span class="flex-shrink-0"><MapPin class="size-4" /></span>
												<span>{location()}</span>
											</div>
										</Show>

										<Show when={includeBluesky()}>
											<div class="flex items-center gap-2">
												<span class="flex-shrink-0">
													<svg class="w-4 h-4 text-black dark:text-white" xmlns="http://www.w3.org/2000/svg" role="img" viewBox="-3 -3 30 30">
														<title>Bluesky</title>
														<path fill="none" stroke="currentColor" d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z" stroke-width="2.25"/>
													</svg>
												</span>
												<a id="bluesky-link" href={`https://bsky.app/profile/${props.actor.did}`} target="_blank" rel="noopener noreferrer">
													{props.actor.handle}
												</a>
											</div>
										</Show>

										<For each={websiteLinks()}>
											{(link) => (
												<Show when={link}>
													<div class="flex items-center gap-2">
														<span class="flex-shrink-0"><Link class="size-4" /></span>
														<a rel="nofollow me" href={link} target="_blank" class="truncate">
															{link}
														</a>
													</div>
												</Show>
											)}
										</For>
									</div>
								</Show>

								<Show
									when={!props.profileLoading}
									fallback={
										<div class="flex items-center justify-evenly gap-2 py-2">
											<div class="flex flex-col items-center gap-2">
												<SkeletonBlock class="h-6 w-8" />
												<SkeletonBlock class="h-4 w-12" />
											</div>
											<div class="flex flex-col items-center gap-2">
												<SkeletonBlock class="h-6 w-8" />
												<SkeletonBlock class="h-4 w-12" />
											</div>
										</div>
									}
								>
									<Show when={props.profile?.stats && props.profile.stats.length > 0}>
										<div class="flex items-center justify-evenly gap-2 py-2">
											<For each={props.profile.stats}>
												{(statKind) => (
													<Show when={statKind.length > 0}>
														<div class="flex flex-col items-center gap-2">
															<span class="text-xl font-bold">{getStatValue(statKind)}</span>
															<span>{formatStatLabel(statKind)}</span>
														</div>
													</Show>
												)}
											</For>
										</div>
									</Show>
								</Show>

								<div class="flex my-2 items-center gap-2">
									<Show
										when={!props.profileLoading}
										fallback={<SkeletonBlock class="h-8 w-full rounded" />}
									>
										<Show
											when={!isSelf()}
											fallback={
												<button
													type="button"
													onClick={startEditing}
													class="btn w-full flex gap-2 items-center justify-center group text-sm"
												>
													<Pencil class="size-4 inline" />
													edit
												</button>
											}
										>
											<button
												onClick={handleFollowClick}
												disabled={props.actionLoading}
												class="btn w-full flex gap-2 items-center justify-center group text-sm"
											>
												<Show
													when={props.actionLoading}
													fallback={
														<Show
															when={props.isFollowing}
															fallback={
																<>
																	<UserRoundPlus class="size-4 inline" />
																	follow
																</>
															}
														>
															<>
																<UserRoundMinus class="size-4 inline" />
																unfollow
															</>
														</Show>
													}
												>
													<LoaderCircle class="size-4 animate-spin inline" />
													<Show when={props.isFollowing} fallback="follow">
														unfollow
													</Show>
												</Show>
											</button>
										</Show>
									</Show>

									<a
										class="btn text-sm no-underline hover:no-underline flex items-center gap-2 group"
										href={`/${userIdent()}/feed.atom`}
									>
										<Rss class="size-4" />
									</a>
								</div>

								<Show when={!isSelf()}>
									<div class="relative w-full mt-2">
										<Show when={!props.profileLoading && !props.vouchLoading} fallback={<SkeletonBlock class="h-8 w-full rounded" />}>
											<button
												id={`vouch-btn-${popoverId()}`}
												type="button"
												popovertarget={popoverId()}
												popovertargetaction="toggle"
												onClick={handleVouchClick}
												class={clsx(
													'w-full flex gap-2 items-center justify-center text-sm',
													isVouched() ? 'btn-create' : isDenounced() ? 'btn-cancel' : 'btn'
												)}
											>
												<Show
													when={isVouched()}
													fallback={
														<Show
															when={isDenounced()}
															fallback={
																<>
																	<Shield class="size-4" />
																	vouch
																</>
															}
														>
															<>
																<ShieldAlert class="size-4" />
																denounced
															</>
														</Show>
													}
												>
													<>
														<ShieldCheck class="size-4" />
														vouched
													</>
												</Show>
											</button>
										</Show>

										<div
											id={popoverId()}
											popover="auto"
											ref={popoverRef}
											class="bg-white w-[95%] md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50 space-y-2"
										>
											<form
												onSubmit={async (e) => {
													e.preventDefault();
													await props.onVouchSubmit(kind(), kind() === 'none' ? undefined : reason());
													popoverRef?.hidePopover();
												}}
												class="flex flex-col gap-6 group"
											>
												<div>
													<p class="font-semibold mb-1 text-base">Vouch for {userIdent()}</p>
													<p class="text-gray-600 dark:text-gray-400 text-sm">
														<Show
															when={props.vouchRecord}
															fallback={
																<>
																	Vouching builds a web-of-trust across Tangled. Vouch for users you
																	have had positive interactions with.{' '}
																	<a href="https://blog.tangled.org/vouching" class="inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
																		Read more <ArrowRight class="size-3" />
																	</a>
																</>
															}
														>
															{(record) => (
																<>
																	You {record().value.kind === 'vouch' ? 'vouched' : 'denounced'} {userIdent()}{' '}
																	{formatRelativeTime(record().value.createdAt)}.
																	You can change your decision below.
																</>
															)}
														</Show>
													</p>
												</div>

												<div class="grid grid-cols-3 gap-2">
													<input
														id={`vouch-input-${popoverId()}`}
														type="radio"
														name="kind"
														value="vouch"
														checked={kind() === 'vouch'}
														onChange={() => setKind('vouch')}
														class="peer/vouch hidden appearance-none"
													/>
													<input
														id={`denounce-input-${popoverId()}`}
														type="radio"
														name="kind"
														value="denounce"
														checked={kind() === 'denounce'}
														onChange={() => setKind('denounce')}
														class="peer/denounce hidden appearance-none"
													/>
													<input
														id={`none-input-${popoverId()}`}
														type="radio"
														name="kind"
														value="none"
														checked={kind() === 'none'}
														onChange={() => setKind('none')}
														class="peer/none hidden appearance-none"
													/>

													<label
														for={`vouch-input-${popoverId()}`}
														class="untangled-vouch-label items-center gap-2 rounded p-2 ring-1 ring-gray-200 dark:ring-gray-700 cursor-pointer hover:bg-gray-100 peer-checked/vouch:bg-green-200 peer-checked/vouch:text-green-800 peer-checked/vouch:ring-green-400 dark:hover:bg-white/5 dark:peer-checked/vouch:bg-green-700 dark:peer-checked/vouch:text-green-200 dark:peer-checked/vouch:ring-green-600"
													>
														<ShieldCheck class="size-5" />
														<span class="text-sm font-medium">vouch</span>
													</label>
													<label
														for={`denounce-input-${popoverId()}`}
														class="untangled-vouch-label items-center gap-2 rounded p-2 ring-1 ring-gray-200 dark:ring-gray-700 cursor-pointer hover:bg-gray-100 peer-checked/denounce:bg-red-200 peer-checked/denounce:text-red-800 peer-checked/denounce:ring-red-400 dark:hover:bg-white/5 dark:peer-checked/denounce:bg-red-700 dark:peer-checked/denounce:text-red-200 dark:peer-checked/denounce:ring-red-600"
													>
														<ShieldAlert class="size-5" />
														<span class="text-sm font-medium">denounce</span>
													</label>
													<label
														for={`none-input-${popoverId()}`}
														class="untangled-vouch-label items-center gap-2 rounded p-2 ring-1 ring-gray-200 dark:ring-gray-700 cursor-pointer hover:bg-gray-100 peer-checked/none:bg-gray-200 peer-checked/none:text-gray-800 peer-checked/none:ring-gray-400 dark:hover:bg-white/5 dark:peer-checked/none:bg-gray-700 dark:peer-checked/none:text-gray-200 dark:peer-checked/none:ring-gray-600"
													>
														<Shield class="size-5 opacity-50" />
														<span class="text-sm font-medium">none</span>
													</label>

													<textarea
														id={`reason-${popoverId()}`}
														name="reason"
														rows="1"
														maxLength="300"
														placeholder="Write a reason..."
														value={reason()}
														onInput={(e) => setReason(e.currentTarget.value)}
														class={clsx(textareaStyles(), 'resize-none col-span-3 peer-checked/none:hidden', kind() === 'none' && 'hidden')}
													/>
												</div>

												<div class="flex gap-2">
													<button
														type="button"
														onClick={() => {
															const record = props.vouchRecord;
															if (record) {
																setKind(record.value.kind);
																setReason(record.value.reason || '');
															} else {
																setKind('none');
																setReason('');
															}
															popoverRef?.hidePopover();
														}}
														class="btn flex-1 flex items-center justify-center gap-2"
													>
														<X class="size-4" />
														cancel
													</button>
													<button
														type="submit"
														disabled={props.vouchActionLoading}
														class="btn-create flex-1 flex items-center justify-center gap-2 text-white group"
													>
														<Show
															when={props.vouchActionLoading}
															fallback={
																<>
																	<Check class="size-4 inline" />
																	{props.vouchRecord ? 'update' : 'save'}
																</>
															}
														>
															<>
																<LoaderCircle class="size-4 animate-spin inline" />
																{props.vouchRecord ? 'update' : 'save'}
															</>
														</Show>
													</button>
												</div>
											</form>
										</div>
									</div>
								</Show>
							</>
						}
					>
						{/* EDIT FORM */}
						<form onSubmit={handleSubmitProfile} class="flex flex-col gap-4 my-2 max-w-full">
							<div class="flex flex-col gap-1">
								<label class="m-0 p-0 text-sm font-semibold" for="description">bio</label>
								<textarea
									id="description"
									class={clsx(textareaStyles(), 'w-full p-2')}
									rows="3"
									placeholder="write a bio"
									value={editBioText()}
									onInput={(e) => setEditBioText(e.currentTarget.value)}
								/>
							</div>

							<div class="flex flex-col gap-1">
								<label class="m-0 p-0 text-sm font-semibold" for="pronouns">pronouns</label>
								<input
									id="pronouns"
									type="text"
									class={clsx(inputStyles(), 'w-full p-2')}
									placeholder="they/them"
									value={editPronounsText()}
									onInput={(e) => setEditPronounsText(e.currentTarget.value)}
								/>
							</div>

							<div class="flex flex-col gap-1">
								<label class="m-0 p-0 text-sm font-semibold" for="location">location</label>
								<div class="flex items-center gap-2 w-full">
									<span class="flex-shrink-0"><MapPin class="size-4" /></span>
									<input
										id="location"
										type="text"
										class={clsx(inputStyles(), 'w-full p-2')}
										value={editLocationText()}
										onInput={(e) => setEditLocationText(e.currentTarget.value)}
									/>
								</div>
							</div>

							<div class="flex flex-col gap-1">
								<label class="m-0 p-0 text-sm font-semibold">social links</label>
								<div class="flex items-center gap-2 py-1">
									<input
										type="checkbox"
										id="includeBluesky"
										checked={editBluesky()}
										onChange={(e) => setEditBluesky(e.currentTarget.checked)}
										class="mr-2"
									/>
									<label for="includeBluesky" class="my-0 py-0 normal-case font-normal text-sm">link to bluesky account</label>
								</div>

								<For each={editLinks()}>
									{(link, index) => (
										<div class="flex items-center gap-2 w-full">
											<span class="flex-shrink-0"><Link class="size-4" /></span>
											<input
												type="text"
												class={clsx(inputStyles(), 'w-full p-2')}
												value={link}
												placeholder={`social link ${index() + 1}`}
												onInput={(e) => {
													const next = [...editLinks()];
													next[index()] = e.currentTarget.value;
													setEditLinks(next);
												}}
											/>
										</div>
									)}
								</For>
							</div>

							<div class="flex flex-col gap-1">
								<label class="m-0 p-0 text-sm font-semibold">vanity stats</label>
								<select
									value={editStat1()}
									onChange={(e) => setEditStat1(e.currentTarget.value)}
									class="w-full p-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 dark:text-white text-sm"
								>
									<option value="">choose stat 1</option>
									<option value="merged-pull-request-count">merged pr count</option>
									<option value="closed-pull-request-count">closed pr count</option>
									<option value="open-pull-request-count">open pr count</option>
									<option value="open-issue-count">open issue count</option>
									<option value="closed-issue-count">closed issue count</option>
									<option value="repository-count">repository count</option>
									<option value="star-count">star count</option>
								</select>

								<select
									value={editStat2()}
									onChange={(e) => setEditStat2(e.currentTarget.value)}
									class="w-full p-2 mt-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 dark:text-white text-sm"
								>
									<option value="">choose stat 2</option>
									<option value="merged-pull-request-count">merged pr count</option>
									<option value="closed-pull-request-count">closed pr count</option>
									<option value="open-pull-request-count">open pr count</option>
									<option value="open-issue-count">open issue count</option>
									<option value="closed-issue-count">closed issue count</option>
									<option value="repository-count">repository count</option>
									<option value="star-count">star count</option>
								</select>
							</div>

							<div class="flex items-center gap-2 justify-between">
								<button
									id="save-btn"
									type="submit"
									disabled={props.profileUpdateLoading}
									class="btn-create p-1 w-full flex items-center justify-center gap-2 text-white text-sm"
								>
									<Show
										when={props.profileUpdateLoading}
										fallback={
											<>
												<Check class="size-4" />
												save
											</>
										}
									>
										<>
											<LoaderCircle class="w-4 h-4 animate-spin" />
											save
										</>
									</Show>
								</button>
								<button
									id="cancel-btn"
									type="button"
									onClick={() => setIsEditing(false)}
									class="btn p-1 w-full flex items-center justify-center gap-2 text-sm"
								>
									<X class="size-4" />
									cancel
								</button>
							</div>
						</form>
					</Show>
				</div>
			</div>
		</div>
	);
};

export const FollowCardSkeleton: Component = () => (
	<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700">
		<div class="py-4 px-6 drop-shadow-sm bg-white dark:bg-gray-800 flex items-center gap-4">
			<div class="flex-shrink-0 w-24 h-24">
				<SkeletonBlock class="w-24 h-24 rounded-full p-2" />
			</div>
			<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2 w-full min-w-0">
				<div class="flex-1 min-h-0 justify-around flex flex-col space-y-2">
					<SkeletonBlock class="h-5 w-1/3" />
					<SkeletonBlock class="h-4 w-2/3" />
					<div class="flex items-center gap-2">
						<SkeletonBlock class="size-4 shrink-0 rounded-full" />
						<SkeletonBlock class="h-4 w-24" />
					</div>
				</div>
				<div class="w-full md:w-auto md:max-w-24 order-last md:order-none">
					<SkeletonBlock class="h-8 w-24 rounded" />
				</div>
			</div>
		</div>
	</div>
);

const FollowCard: Component<{
	item: FollowerFollowingItem;
	loggedInUserDid?: string;
	viewerFollows?: FollowRecord[];
	onFollowToggle?: (did: Did) => void;
}> = (props) => {
	const userIdent = () => props.item.handle || props.item.did;
	
	const profileQuery = createQuery(() => ({
		queryKey: ['profile-record', props.item.did],
		queryFn: () => getActorProfile(props.item.actor),
		staleTime: 300_000,
	}));

	const followersQuery = createQuery(() => ({
		queryKey: ['profile-followers', props.item.did],
		queryFn: () => getFollowers(props.item.did),
		staleTime: 300_000,
	}));

	const followingQuery = createQuery(() => ({
		queryKey: ['profile-following', props.item.did],
		queryFn: () => getFollowing(props.item.did),
		staleTime: 300_000,
	}));

	const isFollowing = () => {
		const list = props.viewerFollows;
		if (!list) return false;
		return list.some(follow => follow.value.subject === props.item.did);
	};

	const isSelf = () => props.loggedInUserDid === props.item.did;

	const avatarQuery = createQuery(() => ({
		queryKey: ['avatar', props.item.did],
		queryFn: async () => resolveAvatarUrl(props.item.did),
		staleTime: 300_000,
	}));

	return (
		<div class="flex flex-col divide-y divide-gray-200 dark:divide-gray-700">
			<div class="py-4 px-6 drop-shadow-sm bg-white dark:bg-gray-800 flex items-center gap-4">
				<div class="flex-shrink-0 max-h-full w-24 h-24">
					<Show
						when={avatarQuery.data}
						fallback={<PlaceholderAvatar size="w-24 h-24 p-2" iconSize="size-10" />}
					>
						<img
							class="object-cover rounded-full p-2 w-24 h-24"
							src={avatarQuery.data!}
							alt={userIdent()}
						/>
					</Show>
				</div>

				<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2 w-full min-w-0">
					<div class="flex-1 min-h-0 justify-around flex flex-col">
						<A href={`/${userIdent()}`}>
							<span class="font-bold dark:text-white overflow-hidden text-ellipsis whitespace-nowrap max-w-full">
								{userIdent()}
							</span>
						</A>
						<Show when={profileQuery.data?.description}>
							<p class="text-sm pb-2 md:pb-2 break-words">
								{profileQuery.data!.description}
							</p>
						</Show>
						<div class="text-sm flex items-center gap-2 my-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full">
							<span class="flex-shrink-0"><Users class="size-4" /></span>
							<span id="followers">
								<A href={`/${userIdent()}?tab=followers`}>
									{followersQuery.data?.length ?? 0} followers
								</A>
							</span>
							<span class="select-none after:content-['·']"></span>
							<span id="following">
								<A href={`/${userIdent()}?tab=following`}>
									{followingQuery.data?.length ?? 0} following
								</A>
							</span>
						</div>
					</div>
					<Show when={props.loggedInUserDid && !isSelf()}>
						<div class="w-full md:w-auto md:max-w-24 order-last md:order-none">
							<button
								onClick={() => props.onFollowToggle?.(props.item.did)}
								class="btn w-full flex gap-2 items-center justify-center group"
							>
								<Show
									when={isFollowing()}
									fallback={
										<>
											<UserRoundPlus class="size-4 inline" />
											Follow
										</>
									}
								>
									<>
										<UserRoundMinus class="size-4 inline" />
										Unfollow
									</>
								</Show>
							</button>
						</div>
					</Show>
				</div>
			</div>
		</div>
	);
};

export const ProfilePage: Component = () => {
	const params = useParams();
	const [searchParams, setSearchParams] = useSearchParams();
	const queryClient = useQueryClient();
	const auth = useAuth();
	const preloadString = useStringPreloader();
	
	const navigate = useNavigate();
	
	const [repoSearch, setRepoSearch] = createSignal('');
	const [actionLoading, setActionLoading] = createSignal(false);
	const [isRedirecting, setIsRedirecting] = createSignal(false);
	const [error, setError] = createSignal<string | null>(null);

	const actorParam = () => params.actor as string;

	createEffect(() => {
		actorParam();
		setIsRedirecting(false);
		setError(null);
	});

	const isDid = () => actorParam().startsWith('did:');

	const actorQuery = createQuery(() => ({
		queryKey: ['profile-actor', actorParam()],
		queryFn: () => resolveActor(actorParam()),
	}));

	const repoDidQuery = createQuery(() => ({
		queryKey: ['repo-by-did', actorParam()],
		enabled: isDid(),
		queryFn: () => getRepoByDid(actorParam() as Did),
		retry: false,
	}));

	createEffect(() => {
		const repo = repoDidQuery.data;
		if (repo) {
			setIsRedirecting(true);
			navigate(`/${repo.owner.handle}/${repo.slug}`, { replace: true });
		}
	});

	createEffect(() => {
		const err = repoDidQuery.error;
		if (err) {
			console.error('Failed to resolve DID as repo:', err);
			setError(getErrorMessage(err));
		}
	});

	const activeTab = () => {
		const tab = searchParams.tab;
		return (Array.isArray(tab) ? tab[0] : tab) || 'overview';
	};

	const profileQuery = createQuery(() => ({
		queryKey: ['profile-record', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: () => getActorProfile(actorQuery.data!),
	}));

	const followersQuery = createQuery(() => ({
		queryKey: ['profile-followers', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: () => getFollowers(actorQuery.data!.did),
	}));

	const followingQuery = createQuery(() => ({
		queryKey: ['profile-following', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: () => getFollowing(actorQuery.data!.did),
	}));

	const reposQuery = createQuery(() => ({
		queryKey: ['profile-repos', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: () => listRepoRecords(actorQuery.data!),
	}));

	const stringsQuery = createQuery(() => ({
		queryKey: ['profile-strings', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: () => listStringRecords(actorQuery.data!),
	}));

	const starsQuery = createQuery(() => ({
		queryKey: ['profile-stars', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: async () => {
			const actor = actorQuery.data!;
			const starRecords = await listStarRecordsByActor(actor);
			const repos = await Promise.all(
				starRecords.map(async (star) => {
					const subject = star.value.subject as string | { did?: string; subjectDid?: string };
					const subjectUri = typeof subject === 'string' && subject.startsWith('at://') ? subject as ResourceUri : undefined;
					const repoDid = typeof subject === 'object' ? (subject.did ?? subject.subjectDid) as Did | undefined : undefined;
					try {
						if (repoDid) return await getRepoByDid(repoDid);
						if (subjectUri) return await getRepoByUri(subjectUri);
					} catch (e) {
						console.error('failed to resolve starred repo', repoDid ?? subjectUri, e);
					}
					return null;
				})
			);
			return repos.filter(Boolean) as RepoContext[];
		},
	}));

	const viewerFollowsQuery = createQuery(() => ({
		queryKey: ['viewer-follows', auth.currentDid()],
		enabled: !!auth.currentDid(),
		queryFn: () => listFollowRecords(auth.currentDid()!),
	}));

	const issuesQuery = createQuery(() => ({
		queryKey: ['profile-issues-stat', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: async () => {
			const did = actorQuery.data!.did;
			try {
				return await listAllAppviewRecords<any>('sh.tangled.repo.listIssuesBy', did);
			} catch (e) {
				console.error('Failed to fetch issues for stat', e);
				return [];
			}
		}
	}));

	const pullsQuery = createQuery(() => ({
		queryKey: ['profile-pulls-stat', actorQuery.data?.did],
		enabled: !!actorQuery.data,
		queryFn: async () => {
			const did = actorQuery.data!.did;
			try {
				return await listAllAppviewRecords<any>('sh.tangled.repo.listPullsBy', did);
			} catch (e) {
				console.error('Failed to fetch pulls for stat', e);
				return [];
			}
		}
	}));

	const mergedPRCount = () => (pullsQuery.data as any[])?.filter((p) => p.state === 'merged').length ?? 0;
	const closedPRCount = () => (pullsQuery.data as any[])?.filter((p) => p.state === 'closed').length ?? 0;
	const openPRCount = () => (pullsQuery.data as any[])?.filter((p) => p.state === 'open').length ?? 0;
	const openIssueCount = () => (issuesQuery.data as any[])?.filter((i) => i.state === 'open').length ?? 0;
	const closedIssueCount = () => (issuesQuery.data as any[])?.filter((i) => i.state === 'closed').length ?? 0;

	const isFollowing = () => {
		const list = viewerFollowsQuery.data;
		const profileDid = actorQuery.data?.did;
		if (!list || !profileDid) return false;
		return list.some((follow) => follow.value.subject === profileDid);
	};

	const followRecordRkey = () => {
		const list = viewerFollowsQuery.data;
		const profileDid = actorQuery.data?.did;
		if (!list || !profileDid) return undefined;
		return list.find((follow) => follow.value.subject === profileDid)?.rkey;
	};

	const handleFollowToggle = async () => {
		const agent = auth.agent();
		const profileDid = actorQuery.data?.did;
		if (!agent || !profileDid) return;

		setActionLoading(true);
		setError(null);
		try {
			const rkey = followRecordRkey();
			if (rkey) {
				await deleteFollow(agent, rkey);
			} else {
				await createFollow(agent, profileDid);
			}
			queryClient.invalidateQueries({ queryKey: ['viewer-follows', auth.currentDid()] });
			queryClient.invalidateQueries({ queryKey: ['profile-followers', profileDid] });
		} catch (e) {
			console.error('Failed to toggle follow status', e);
			setError(getErrorMessage(e));
		} finally {
			setActionLoading(false);
		}
	};

	const handleItemFollowToggle = async (did: Did) => {
		const agent = auth.agent();
		if (!agent) return;

		setError(null);
		try {
			const viewerFollows = viewerFollowsQuery.data ?? [];
			const existing = viewerFollows.find((f) => f.value.subject === did);
			if (existing) {
				await deleteFollow(agent, existing.rkey);
			} else {
				await createFollow(agent, did);
			}
			queryClient.invalidateQueries({ queryKey: ['viewer-follows', auth.currentDid()] });
			if (actorQuery.data?.did === did) {
				queryClient.invalidateQueries({ queryKey: ['profile-followers', did] });
			}
		} catch (e) {
			console.error('Failed to toggle follow status for list item', e);
			setError(getErrorMessage(e));
		}
	};

	const vouchQuery = createQuery(() => ({
		queryKey: ['vouch-record', auth.currentDid(), actorQuery.data?.did],
		enabled: !!auth.currentDid() && !!actorQuery.data?.did && auth.currentDid() !== actorQuery.data?.did,
		queryFn: () => getVouchRecord(auth.currentDid()!, actorQuery.data!.did),
	}));

	const [vouchActionLoading, setVouchActionLoading] = createSignal(false);

	const handleVouchSubmit = async (kind: 'vouch' | 'denounce' | 'none', reason?: string) => {
		const agent = auth.agent();
		const profileDid = actorQuery.data?.did;
		if (!agent || !profileDid) return;

		setVouchActionLoading(true);
		setError(null);
		try {
			if (kind === 'none') {
				await deleteVouchRecord(agent, profileDid);
			} else {
				await putVouchRecord(agent, profileDid, kind, reason);
			}
			queryClient.invalidateQueries({ queryKey: ['vouch-record', auth.currentDid(), profileDid] });
		} catch (e) {
			console.error('Failed to submit vouch status', e);
			setError(getErrorMessage(e));
		} finally {
			setVouchActionLoading(false);
		}
	};

	const [profileUpdateLoading, setProfileUpdateLoading] = createSignal(false);

	const handleProfileUpdate = async (profile: ShTangledActorProfile.Main) => {
		const agent = auth.agent();
		const profileDid = actorQuery.data?.did;
		if (!agent || !profileDid) return;

		setProfileUpdateLoading(true);
		setError(null);
		try {
			await putActorProfile(agent, profile);
			queryClient.invalidateQueries({ queryKey: ['profile-record', profileDid] });
		} catch (e) {
			console.error('Failed to update profile record', e);
			setError(getErrorMessage(e));
		} finally {
			setProfileUpdateLoading(false);
		}
	};

	const pinnedRepos = createMemo(() => {
		const list = reposQuery.data ?? [];
		const pinnedDids = profileQuery.data?.pinnedRepositories ?? [];
		if (pinnedDids.length > 0) {
			return list.filter((repo) => {
				const did = repo.value.repoDid;
				const uri = repo.uri;
				return (did && pinnedDids.includes(did)) || pinnedDids.includes(uri);
			});
		}
		return list.slice(0, 4);
	});

	const filteredRepos = createMemo(() => {
		const q = repoSearch().toLowerCase().trim();
		const list = reposQuery.data ?? [];
		if (!q) return list;
		return list.filter(
			(repo) =>
				repo.value.name?.toLowerCase().includes(q) ||
				repo.value.description?.toLowerCase().includes(q)
		);
	});

	return (
		<Switch>
			<Match when={actorQuery.isLoading}>
				<LoadingState label={isDid() ? "Resolving identifier..." : "Loading profile..."} />
			</Match>

			<Match when={actorQuery.error}>
				<Show
					when={isRedirecting() || (isDid() && repoDidQuery.isLoading)}
					fallback={
						<div class="p-6 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800">
							<ErrorState message="Could not resolve user profile." />
						</div>
					}
				>
					<LoadingState label="Resolving repository DID..." />
				</Show>
			</Match>

			<Match when={actorQuery.data}>
				{(resolvedActor) => (
					<Switch>
						<Match when={!profileQuery.isLoading && !profileQuery.data}>
							{(() => {
								const unresolvedAvatarQuery = createQuery(() => ({
									queryKey: ['avatar', resolvedActor().did],
									queryFn: async () => resolveAvatarUrl(resolvedActor().did),
									staleTime: 300_000,
								}));
								return (
									<section class="bg-white dark:bg-gray-800 px-2 py-6 md:p-6 rounded w-full dark:text-white shadow-sm">
										<div class="flex items-center gap-6 p-4">
											<Show
												when={unresolvedAvatarQuery.data}
												fallback={<PlaceholderAvatar size="w-28 h-28" iconSize="size-12" />}
											>
												<img
													class="w-28 h-28 shrink-0 object-cover rounded-full"
													src={unresolvedAvatarQuery.data!}
													alt={resolvedActor().handle || resolvedActor().did}
												/>
											</Show>
											<div>
												<p class="text-lg font-bold">{resolvedActor().handle || resolvedActor().did}</p>
												<p class="text-gray-700 dark:text-gray-300 mt-2">This user hasn't joined Tangled yet.</p>
												<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Let them know we're waiting for them!</p>
											</div>
										</div>
									</section>
								);
							})()}
						</Match>
						<Match when={profileQuery.data || profileQuery.isLoading}>
							<div class="flex flex-col">
								<ProfileTabs
									activeTab={activeTab()}
									reposCount={reposQuery.data?.length ?? 0}
									starsCount={starsQuery.data?.length ?? 0}
									stringsCount={stringsQuery.data?.length ?? 0}
									reposLoading={reposQuery.isLoading}
									starsLoading={starsQuery.isLoading}
									stringsLoading={stringsQuery.isLoading}
								/>

								<section class="bg-white dark:bg-gray-800 px-2 py-6 md:p-6 rounded w-full dark:text-white shadow-sm">
									<div class="grid grid-cols-1 md:grid-cols-11 gap-4">
										{/* Sidebar */}
										<div
											class={clsx(
												activeTab() === 'overview' ? 'md:col-span-3' : 'hidden md:block md:col-span-3',
												'order-1'
											)}
										>
											<ProfileCardComponent
												actor={resolvedActor()}
												profile={profileQuery.data}
												followersCount={followersQuery.data?.length ?? 0}
												followingCount={followingQuery.data?.length ?? 0}
												loggedInUserDid={auth.currentDid() ?? undefined}
												isFollowing={isFollowing()}
												onFollowToggle={handleFollowToggle}
												actionLoading={actionLoading()}
												onTabChange={(tab) => setSearchParams({ tab })}
												vouchRecord={vouchQuery.data}
												onVouchSubmit={handleVouchSubmit}
												vouchActionLoading={vouchActionLoading()}
												onProfileUpdate={handleProfileUpdate}
												profileUpdateLoading={profileUpdateLoading()}
												reposCount={reposQuery.data?.length ?? 0}
												starsCount={starsQuery.data?.length ?? 0}
												mergedPRCount={mergedPRCount()}
												closedPRCount={closedPRCount()}
												openPRCount={openPRCount()}
												openIssueCount={openIssueCount()}
												closedIssueCount={closedIssueCount()}
												profileLoading={profileQuery.isLoading}
												followersLoading={followersQuery.isLoading}
												followingLoading={followingQuery.isLoading}
												vouchLoading={vouchQuery.isLoading}
											/>

											<Show when={error()}>
												<div class="mt-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded text-sm text-red-600 dark:text-red-400">
													{error()}
												</div>
											</Show>
										</div>

										<Switch>
											{/* Overview Tab (split into two columns) */}
											<Match when={activeTab() === 'overview'}>
												{/* Pinned / Own Repos Column */}
												<div id="all-repos" class="md:col-span-4 order-2 md:order-2">
													<div class="grid grid-cols-1 gap-4">
														<div>
															<div class="text-base font-bold uppercase tracking-wider px-2 pb-4 dark:text-white flex items-center gap-2">
																<A
																	href="?tab=repos"
																	class="flex text-black dark:text-white items-center gap-2 no-underline hover:no-underline group"
																>
																	<span>pinned repos</span>
																</A>
															</div>
															<div id="repos" class="grid grid-cols-1 gap-4 items-stretch">
																<Show
																	when={!reposQuery.isLoading}
																	fallback={
																		<>
																			<RepoCardSkeleton />
																			<RepoCardSkeleton />
																			<RepoCardSkeleton />
																		</>
																	}
																>
																	<For
																		each={pinnedRepos()}
																		fallback={
																			<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
																				<span>This user does not have any pinned repos.</span>
																			</div>
																		}
																	>
																		{(repo) => (
																			<RepoCard
																				owner={resolvedActor().handle}
																				name={repo.value.name || repo.rkey}
																				description={repo.value.description}
																				isFork={Boolean(repo.value.source)}
																				showOwner={false}
																			/>
																		)}
																	</For>
																</Show>
															</div>
														</div>
													</div>
												</div>

												{/* Activity Timeline Column */}
												<div class="md:col-span-4 order-3 md:order-3">
													<p class="text-base font-bold uppercase tracking-wider px-2 pb-4 dark:text-white">activity</p>
													<div class="flex flex-col gap-4 relative">
														<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
															<span>This user does not have any activity yet.</span>
														</div>
													</div>
												</div>
											</Match>

											{/* Repositories Tab */}
											<Match when={activeTab() === 'repos'}>
												<div id="all-repos" class="md:col-span-8 order-2 md:order-2">
													<div class="mb-4">
														<form id="search-form" class="flex relative" onSubmit={(e) => e.preventDefault()}>
															<div class="flex-1 flex relative">
																<input
																	id="search-q"
																	class="flex-1 py-1 pl-2 pr-10 mr-[-1px] rounded-r-none peer font-normal text-sm"
																	type="text"
																	placeholder="Search repos..."
																	value={repoSearch()}
																	onInput={(e) => setRepoSearch(e.currentTarget.value)}
																/>
																<Show when={repoSearch()}>
																	<button
																		type="button"
																		onClick={() => setRepoSearch('')}
																		class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
																	>
																		<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
																	</button>
																</Show>
															</div>
															<button
																type="submit"
																class="p-2 text-gray-400 border rounded-r border-gray-300 dark:border-gray-600"
															>
																<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
															</button>
														</form>
													</div>

													<div id="repos" class="grid grid-cols-1 gap-4 mb-6">
														<Show
															when={!reposQuery.isLoading}
															fallback={
																<For each={Array.from({ length: 5 })}>
																	{() => <RepoCardSkeleton />}
																</For>
															}
														>
															<For
																each={filteredRepos()}
																fallback={
																	<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
																		<span>This user does not have any repos yet.</span>
																	</div>
																}
															>
																{(repo) => (
																	<RepoCard
																		owner={resolvedActor().handle}
																		name={repo.value.name || repo.rkey}
																		description={repo.value.description}
																		isFork={Boolean(repo.value.source)}
																		showOwner={false}
																	/>
																)}
															</For>
														</Show>
													</div>
												</div>
											</Match>

											{/* Starred Tab */}
											<Match when={activeTab() === 'starred'}>
												<div id="all-repos" class="md:col-span-8 order-2 md:order-2">
													<Show
														when={!starsQuery.isLoading}
														fallback={
															<div id="repos" class="grid grid-cols-1 gap-4 mb-6">
																<For each={Array.from({ length: 4 })}>
																	{() => <RepoCardSkeleton />}
																</For>
															</div>
														}
													>
														<div id="repos" class="grid grid-cols-1 gap-4 mb-6">
															<For
																each={starsQuery.data ?? []}
																fallback={
																	<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
																		<span>This user does not have any starred repos yet.</span>
																	</div>
																}
															>
																{(repo) => (
																	<RepoCard
																		owner={repo.owner.handle}
																		name={repo.slug}
																		repoDid={repo.repoDid}
																		description={repo.record.value.description}
																		isFork={Boolean(repo.record.value.source)}
																		showOwner={true}
																	/>
																)}
															</For>
														</div>
													</Show>
												</div>
											</Match>

											{/* Strings Tab */}
											<Match when={activeTab() === 'strings'}>
												<div id="all-strings" class="md:col-span-8 order-2 md:order-2">
													<Show
														when={!stringsQuery.isLoading}
														fallback={
															<div id="strings" class="grid grid-cols-1 gap-4 mb-6">
																<For each={Array.from({ length: 3 })}>
																	{() => <StringCardSkeleton />}
																</For>
															</div>
														}
													>
														<div id="strings" class="grid grid-cols-1 gap-4 mb-6">
															<For
																each={stringsQuery.data ?? []}
																fallback={
																	<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
																		<span>This user does not have any strings yet.</span>
																	</div>
																}
															>
																{(item) => {
																	const lineCount = () => item.value.contents.split('\n').length;
																	const timeAgo = () => formatRelativeTime(item.value.createdAt);
																	return (
																		<div class="border border-gray-200 dark:border-gray-700 rounded-sm">
																			<div class="py-4 px-6 rounded bg-white dark:bg-gray-800">
																				<div class="font-medium dark:text-white flex gap-2 items-center">
																					<A
																						href={`/strings/${resolvedActor().handle || resolvedActor().did}/${item.rkey}`}
																						class="hover:underline"
																						{...preloadString(resolvedActor().handle || resolvedActor().did, item.rkey)}
																					>
																						{item.value.filename}
																					</A>																				</div>
																				{item.value.description && (
																					<div class="text-gray-600 dark:text-gray-300 text-sm">
																						{item.value.description}
																					</div>
																				)}
																				<div class="text-gray-400 pt-4 text-sm font-mono inline-flex gap-2 mt-auto">
																					<span>{lineCount()} line{lineCount() !== 1 ? 's' : ''}</span>
																					<span class="select-none [&:before]:content-['·']"></span>
																					<span>{timeAgo()}</span>
																				</div>
																			</div>
																		</div>
																	);
																}}
															</For>
														</div>
													</Show>
												</div>
											</Match>

											{/* Followers Tab */}
											<Match when={activeTab() === 'followers'}>
												<div id="all-followers" class="md:col-span-8 order-2 md:order-2">
													<p class="text-base font-bold uppercase tracking-wider p-2 dark:text-white">all followers</p>
													<Show
														when={!followersQuery.isLoading}
														fallback={
															<div id="followers" class="grid grid-cols-1 gap-4 mb-6">
																<For each={Array.from({ length: 3 })}>
																	{() => <FollowCardSkeleton />}
																</For>
															</div>
														}
													>
														<div id="followers" class="grid grid-cols-1 gap-4 mb-6">
															<For
																each={followersQuery.data ?? []}
																fallback={
																	<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
																		<span>This user does not have any followers yet.</span>
																	</div>
																}
															>
																{(item) => (
																	<FollowCard
																		item={item}
																		loggedInUserDid={auth.currentDid() ?? undefined}
																		viewerFollows={viewerFollowsQuery.data}
																		onFollowToggle={handleItemFollowToggle}
																	/>
																)}
															</For>
														</div>
													</Show>
												</div>
											</Match>

											{/* Following Tab */}
											<Match when={activeTab() === 'following'}>
												<div id="all-following" class="md:col-span-8 order-2 md:order-2">
													<p class="text-base font-bold uppercase tracking-wider p-2 dark:text-white">all following</p>
													<Show
														when={!followingQuery.isLoading}
														fallback={
															<div id="following" class="grid grid-cols-1 gap-4 mb-6">
																<For each={Array.from({ length: 3 })}>
																	{() => <FollowCardSkeleton />}
																</For>
															</div>
														}
													>
														<div id="following" class="grid grid-cols-1 gap-4 mb-6">
															<For
																each={followingQuery.data ?? []}
																fallback={
																	<div class="text-base text-gray-500 flex items-center justify-center italic p-12 border border-gray-200 dark:border-gray-700 rounded">
																		<span>This user does not follow anyone yet.</span>
																	</div>
																}
															>
																{(item) => (
																	<FollowCard
																		item={item}
																		loggedInUserDid={auth.currentDid() ?? undefined}
																		viewerFollows={viewerFollowsQuery.data}
																		onFollowToggle={handleItemFollowToggle}
																	/>
																)}
															</For>
														</div>
													</Show>
												</div>
											</Match>
										</Switch>
									</div>
								</section>
							</div>
						</Match>
					</Switch>
		)}
	</Match>
		</Switch>
	);
};
