import { type ReactNode, useEffect } from 'react';

import type { AnyProfileView, AppBskyGraphDefs } from '@atcute/bluesky';
import {
	DisplayContext,
	type DisplayRestrictions,
	getDisplayRestrictions,
	moderateList,
} from '@atcute/bluesky-moderation';
import { parseCanonicalResourceUri } from '@atcute/lexicons/syntax';

import { weightedIndex } from '@mary/array-fns';

import { useQueryClient } from '@tanstack/react-query';
import { clsx } from 'clsx';

import { listTarget } from '#/lib/routes/targets';

import { useModerationOpts } from '#/state/moderation/moderation-opts';
import { precacheList } from '#/state/queries/feed';
import { useSession } from '#/state/session';

import { BlockLink } from '#/components/BlockLink';
import * as Hider from '#/components/moderation/Hider';
import { useNavigationDisabled } from '#/components/NavigationDisabled';
import { RichText } from '#/components/RichText';
import { Text } from '#/components/Text';
import { UserAvatar } from '#/components/UserAvatar';
import * as Skeleton from '#/components/web/Skeleton';

import { m } from '#/paraglide/messages';
import type { RouteTarget } from '#/router';
import { borderRadius } from '#/styles/tokens.css';

import * as css from './ListCard.css';

const CURATELIST = 'app.bsky.graph.defs#curatelist';
const MODLIST = 'app.bsky.graph.defs#modlist';

type Props = {
	view: AppBskyGraphDefs.ListView;
};

export function Default({
	topBorder,
	view,
}: Props & {
	topBorder?: boolean;
}) {
	const moderationOpts = useModerationOpts();
	const moderation = moderationOpts ? moderateList(view, moderationOpts) : undefined;

	return (
		<Link className={css.defaultRow({ topBorder })} view={view}>
			<Outer>
				<Header>
					<Avatar src={view.avatar} />
					<TitleAndByline
						creator={view.creator}
						modUi={moderation ? getDisplayRestrictions(moderation, DisplayContext.ContentView) : undefined}
						purpose={view.purpose}
						title={view.name}
					/>
				</Header>
				<Description description={view.description} />
			</Outer>
		</Link>
	);
}

export function Link({
	children,
	className,
	onPress,
	view,
}: {
	children: ReactNode;
	className?: string;
	onPress?: () => void;
	view: AppBskyGraphDefs.ListView;
}) {
	const queryClient = useQueryClient();
	const navigationDisabled = useNavigationDisabled();

	const target = createProfileListTarget({ list: view });

	useEffect(() => {
		precacheList(queryClient, view);
	}, [queryClient, view]);

	return (
		<BlockLink
			className={clsx(css.link({ interactive: !navigationDisabled }), className)}
			label={view.name}
			onBeforePress={onPress}
			to={target}
		>
			<div>{children}</div>
		</BlockLink>
	);
}

export function Outer({ children, className }: { children: ReactNode; className?: string }) {
	return <div className={clsx(css.outer, className)}>{children}</div>;
}

export function Header({ children }: { children: ReactNode }) {
	return <div className={css.header}>{children}</div>;
}

export type AvatarProps = { size?: number; src: string | undefined };

export function Avatar({ size = 40, src }: AvatarProps) {
	return <UserAvatar avatar={src} size={size} type="list" />;
}

export function AvatarPlaceholder({ size = 40 }: Omit<AvatarProps, 'src'>) {
	return <Skeleton.Square radius={borderRadius.sm} size={size} />;
}

export function TitleAndByline({
	byline,
	creator,
	modUi,
	purpose = CURATELIST,
	title,
}: {
	/** Secondary line under the title; defaults to a "by @creator" byline derived from {@link creator}. */
	byline?: string;
	creator?: AnyProfileView;
	modUi?: DisplayRestrictions;
	purpose?: AppBskyGraphDefs.ListView['purpose'];
	title: string;
}) {
	const { currentAccount } = useSession();

	const subtitle =
		byline ??
		(creator
			? purpose === MODLIST
				? m['common.list.moderationBy']({ handle: creator.handle })
				: m['common.list.byCreator']({ handle: creator.handle })
			: undefined);

	return (
		<div className={css.titleColumn}>
			<Hider.Outer
				allowOverride={creator && currentAccount?.did === creator.did}
				isContentVisibleInitialState={creator && currentAccount?.did === creator.did}
				modui={modUi}
			>
				<Hider.Mask>
					<Text className={css.italic} numberOfLines={1} weight="medium">
						{m['common.list.hidden']()}
					</Text>
				</Hider.Mask>
				<Hider.Content>
					<Text numberOfLines={1} weight="medium">
						{title}
					</Text>
				</Hider.Content>
			</Hider.Outer>
			{subtitle && (
				<Text color="textContrastMedium" numberOfLines={1} size="md_sub">
					{subtitle}
				</Text>
			)}
		</div>
	);
}

export function TitleAndBylinePlaceholder({ creator }: { creator?: boolean }) {
	return (
		<Skeleton.Col grow>
			<Skeleton.Text size="md" width="60%" />
			{creator && <Skeleton.Text color="contrast_25" size="md" width="40%" />}
		</Skeleton.Col>
	);
}

export function Description({ description }: { description?: string }) {
	if (!description) {
		return null;
	}
	return <RichText disableLinks value={description} />;
}

// weighted description-line counts: most list descriptions are short (1 line) or empty, with a long
// tail toward 2–3 lines. index = line count (0–3).
const DESCRIPTION_LINE_WEIGHTS = [12, 10, 4, 1];

function LoadingRow({ descriptionLines, topBorder }: { descriptionLines: number; topBorder: boolean }) {
	return (
		<Skeleton.Col className={css.loadingRow({ topBorder })} gap="md">
			<Skeleton.Row align="center" gap="md">
				<AvatarPlaceholder />
				<TitleAndBylinePlaceholder creator />
			</Skeleton.Row>
			{descriptionLines > 0 && <Skeleton.Lines count={descriptionLines} lastWidth={70} size="md" />}
		</Skeleton.Col>
	);
}

// fallback row count when the caller doesn't know how many lists to expect, and a cap so a profile with
// many lists doesn't render dozens of placeholder rows.
const DEFAULT_LOADING_ROW_COUNT = 3;
const MAX_LOADING_ROW_COUNT = 10;

/**
 * stack of list-card placeholders for the loading state, mirroring {@link Default}'s layout.
 *
 * @param count number of placeholder rows. defaults to a small value and is capped to prevent rendering
 *   excessive rows.
 */
export function LoadingPlaceholder({ count }: { count?: number }): ReactNode {
	const rowCount = Math.min(count ?? DEFAULT_LOADING_ROW_COUNT, MAX_LOADING_ROW_COUNT);
	const rows = Array.from({ length: rowCount }, () => ({
		// list descriptions are often empty or short; weight toward 0–1 lines with a tail toward 2–3.
		descriptionLines: weightedIndex(DESCRIPTION_LINE_WEIGHTS),
	}));

	return (
		<>
			{rows.map((row, i) => (
				// oxlint-disable-next-line react/no-array-index-key -- static skeleton
				<LoadingRow key={i} descriptionLines={row.descriptionLines} topBorder={i !== 0} />
			))}
		</>
	);
}

function createProfileListTarget({ list }: { list: AppBskyGraphDefs.ListView }): RouteTarget {
	const urip = parseCanonicalResourceUri(list.uri);
	return listTarget(list.creator.did, urip.rkey);
}
