import type { AnyProfileView, AppBskyGraphGetStarterPacksWithMembership } from '@atcute/bluesky';
import type { Did } from '@atcute/lexicons';

import { getStarterPackRecord } from '#/lib/api/record-casts';

import { useModerationOpts } from '#/state/moderation/moderation-opts';
import { useActorStarterPacksWithMembershipsQuery } from '#/state/queries/actor-starter-packs';
import {
	useListMembershipAddMutation,
	useListMembershipRemoveMutation,
} from '#/state/queries/list-memberships';
import { useProfileQuery } from '#/state/queries/profile';
import { useSession } from '#/state/session';

import { AvatarStack } from '#/components/AvatarStack';
import { CenteredSpinner } from '#/components/CenteredSpinner';
import * as Dialog from '#/components/Dialog';
import {
	markStarterPackWizardLaunched,
	useStarterPackDialogReopen,
} from '#/components/dialogs/starter-pack-dialog-reopen';
import * as css from '#/components/dialogs/StarterPackDialog.css';
import { Spinner } from '#/components/Spinner';
import { Text } from '#/components/Text';
import * as Toast from '#/components/Toast';
import { Button, ButtonIcon, ButtonText } from '#/components/web/Button';

import XIcon from '#/icons/central/CrossLarge_round_outlined_radius1_stroke2.svg';
import PlusIcon from '#/icons/central/PlusLarge_round_outlined_radius1_stroke2.svg';
import StarterPack from '#/icons/original/StarterPack.svg';
import { m } from '#/paraglide/messages';
import { useLocation, useRouter } from '#/router';

type StarterPackWithMembership = AppBskyGraphGetStarterPacksWithMembership.StarterPackWithMembership;

type StarterPackDialogProps = {
	handle: Dialog.DialogHandle;
	targetDid: Did;
};

export function StarterPackDialog({ handle, targetDid }: StarterPackDialogProps) {
	// the dialog is where the wizard is launched from, so it is also where the user is brought back to
	useStarterPackDialogReopen(handle, targetDid);

	return (
		<Dialog.Root handle={handle}>
			<Dialog.Popup className={css.popup} scroll="body" label={m['common.starterPack.action.add']()}>
				<DialogInner handle={handle} targetDid={targetDid} />
			</Dialog.Popup>
		</Dialog.Root>
	);
}

const STARTER_PACK_ITEM_HEIGHT_ESTIMATE = 74;

type Item = { type: 'pack'; pack: StarterPackWithMembership } | { type: 'create' };

function keyExtractor(item: Item): string {
	return item.type === 'pack' ? item.pack.starterPack.uri : item.type;
}

function DialogInner({ handle, targetDid }: StarterPackDialogProps) {
	const router = useRouter();
	// capture the origin history entry so the reopen coordinator can key the reopen to it.
	const { key: originKey } = useLocation();
	const { data: subject } = useProfileQuery({ did: targetDid });

	const { data, isError, isLoading, hasNextPage, isFetchingNextPage, fetchNextPage } =
		useActorStarterPacksWithMembershipsQuery({ did: targetDid });

	const membershipItems = data?.pages.flatMap((page) => page.starterPacksWithMembership) ?? [];

	// a "create new" row leads the list, sharing the same row chrome as the packs below it.
	const items: Item[] = membershipItems.map((pack): Item => ({ type: 'pack', pack }));
	if (membershipItems.length > 0) {
		items.unshift({ type: 'create' });
	}

	const onStartWizard = () => {
		handle.close();
		markStarterPackWizardLaunched(originKey, targetDid);
		router.navigate({ to: { name: 'StarterPackWizard', targetDid } });
	};

	const onEndReached = () => {
		if (isFetchingNextPage || !hasNextPage || isError) {
			return;
		}
		void fetchNextPage();
	};

	return (
		<>
			<div className={css.header}>
				<Text className={css.title} numberOfLines={1} size="lg" weight="semiBold">
					{m['common.starterPack.action.add']()}
				</Text>
				<Button
					className={css.closeButton}
					color="secondary"
					label={m['common.action.close']()}
					onClick={() => handle.close()}
					shape="round"
					size="small"
					variant="ghost"
				>
					<ButtonIcon icon={XIcon} />
				</Button>
			</div>
			<Dialog.List
				data={items}
				estimateHeight={STARTER_PACK_ITEM_HEIGHT_ESTIMATE}
				keyExtractor={keyExtractor}
				renderItem={({ index, item }) => {
					switch (item.type) {
						case 'create': {
							return <CreateRow onStartWizard={onStartWizard} topBorder={index !== 0} />;
						}
						case 'pack': {
							return (
								<StarterPackItem
									starterPackWithMembership={item.pack}
									subject={subject}
									targetDid={targetDid}
									topBorder={index !== 0}
								/>
							);
						}
					}
				}}
				onEndReached={onEndReached}
				ListFooterComponent={
					isFetchingNextPage ? <CenteredSpinner label={m['common.status.loading']()} size="xl" /> : null
				}
				ListEmptyComponent={
					isLoading ? (
						<div className={css.loading}>
							<CenteredSpinner label={m['common.status.loading']()} size="xl" />
						</div>
					) : (
						<Empty onStartWizard={onStartWizard} />
					)
				}
			/>
		</>
	);
}

function CreateButton({ onStartWizard }: { onStartWizard: () => void }) {
	return (
		<Button
			color="secondary_inverted"
			label={m['components.dialogs.starterPack.createTitle']()}
			onClick={onStartWizard}
			size="small"
		>
			<ButtonText>{m['common.action.create']()}</ButtonText>
			<ButtonIcon icon={PlusIcon} />
		</Button>
	);
}

function CreateRow({ onStartWizard, topBorder }: { onStartWizard: () => void; topBorder: boolean }) {
	return (
		<div className={css.item({ topBorder })}>
			<div className={css.itemInfo}>
				<Text numberOfLines={1} size="md" weight="semiBold">
					{m['components.dialogs.starterPack.newTitle']()}
				</Text>
			</div>
			<CreateButton onStartWizard={onStartWizard} />
		</div>
	);
}

function Empty({ onStartWizard }: { onStartWizard: () => void }) {
	return (
		<div className={css.empty}>
			<div className={css.emptyText}>
				<StarterPack className={css.starterPackIcon} />
				<Text align="center">{m['components.dialogs.starterPack.empty']()}</Text>
			</div>
			<CreateButton onStartWizard={onStartWizard} />
		</div>
	);
}

function StarterPackItem({
	starterPackWithMembership,
	subject,
	targetDid,
	topBorder,
}: {
	starterPackWithMembership: StarterPackWithMembership;
	subject?: AnyProfileView;
	targetDid: Did;
	topBorder: boolean;
}) {
	const { currentAccount } = useSession();
	const moderationOpts = useModerationOpts();
	const isSelf = subject?.did === currentAccount?.did;

	const starterPack = starterPackWithMembership.starterPack;
	const isInPack = !!starterPackWithMembership.listItem;

	const { mutate: addMembership, isPending: isPendingAdd } = useListMembershipAddMutation({
		onSuccess: () => {
			Toast.show(m['components.dialogs.starterPack.addedToast']());
		},
		onError: (err) => {
			console.error('Failed to add to starter pack', err);
			Toast.show(m['components.dialogs.starterPack.error.add'](), { type: 'error' });
		},
	});

	const { mutate: removeMembership, isPending: isPendingRemove } = useListMembershipRemoveMutation({
		onSuccess: () => {
			Toast.show(m['components.dialogs.starterPack.removedToast']());
		},
		onError: (err) => {
			console.error('Failed to remove from starter pack', err);
			Toast.show(m['components.dialogs.starterPack.error.remove'](), { type: 'error' });
		},
	});

	const isPending = isPendingAdd || isPendingRemove;

	const handleToggleMembership = () => {
		if (!starterPack.list?.uri || isPending) {
			return;
		}

		const listUri = starterPack.list.uri;

		if (!isInPack) {
			addMembership({ actorDid: targetDid, listUri, subject });
		} else {
			if (!starterPackWithMembership.listItem?.uri) {
				console.error('Cannot remove from starter pack: missing membership URI');
				return;
			}
			removeMembership({
				actorDid: targetDid,
				listUri,
				membershipUri: starterPackWithMembership.listItem.uri,
			});
		}
	};

	const record = getStarterPackRecord(starterPack);
	const sample = starterPack.listItemsSample ?? [];
	const listItemCount = starterPack.list?.listItemCount ?? 0;

	return (
		<div className={css.item({ topBorder })}>
			<div className={css.itemInfo}>
				<Text numberOfLines={1} size="md" weight="semiBold">
					{record.name}
				</Text>
				{sample.length > 0 && (
					<div className={css.itemMeta}>
						<AvatarStack
							moderationOpts={moderationOpts}
							profiles={sample.slice(0, 4).map((p) => p.subject as AnyProfileView)}
							size={24}
						/>
						{listItemCount > 4 && (
							<Text className={css.moreCount} color="textContrastMedium" size="sm">
								{m['components.dialogs.list.moreCount']({ count: listItemCount - 4 })}
							</Text>
						)}
					</div>
				)}
			</div>
			<Button
				color={isInPack ? 'secondary' : 'primary_subtle'}
				disabled={isPending || isSelf}
				label={isInPack ? m['common.action.remove']() : m['common.action.add']()}
				onClick={handleToggleMembership}
				size="small"
			>
				{isPending && <Spinner color="default" label={m['common.status.saving']()} size="sm" />}
				<ButtonText>
					{isSelf
						? m['components.dialogs.list.owner']()
						: isInPack
							? m['common.action.remove']()
							: m['common.action.add']()}
				</ButtonText>
			</Button>
		</div>
	);
}
