import type { ReactNode } from 'react';

import type { AnyStarterPackView } from '@atcute/bluesky';
import { parseCanonicalResourceUri } from '@atcute/lexicons/syntax';

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

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

import { getStarterPackRecord } from '#/lib/api/record-casts';
import { starterPackTarget } from '#/lib/routes/targets';
import { getStarterPackOgCard } from '#/lib/starter-pack';

import { precacheResolvedUri } from '#/state/queries/resolve-uri';
import { precacheStarterPack } from '#/state/queries/starter-packs';
import { useSession } from '#/state/session';

import { EmbedThumb } from '#/components/EmbedThumb';
import { useNavigationDisabled } from '#/components/NavigationDisabled';
import { Text } from '#/components/Text';
import { Link as WebLink } from '#/components/web/Link';
import * as Skeleton from '#/components/web/Skeleton';

import StarterPackIcon from '#/icons/original/StarterPackSky.svg';
import { m } from '#/paraglide/messages';
import { borderRadius } from '#/styles/tokens.css';

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

type DefaultProps = {
	starterPack?: AnyStarterPackView;
	topBorder?: boolean;
};

/** A list-row starter pack card */
export function Default({ starterPack, topBorder }: DefaultProps) {
	if (!starterPack) {
		return null;
	}
	return (
		<Link className={css.defaultRow({ topBorder })} starterPack={starterPack}>
			<Card starterPack={starterPack} />
		</Link>
	);
}

/** A compact starter pack card for notifications. */
export function Notification({ starterPack }: { starterPack?: AnyStarterPackView }) {
	if (!starterPack) {
		return null;
	}
	return (
		<Outer>
			<Header>
				<TitleAndByline starterPack={starterPack} />
			</Header>
			<JoinedCount starterPack={starterPack} />
		</Outer>
	);
}

/** The full card body */
export function Card({ starterPack }: { starterPack: AnyStarterPackView }) {
	return (
		<Outer>
			<Header>
				<Icon />
				<TitleAndByline starterPack={starterPack} />
			</Header>
			<Description starterPack={starterPack} />
			<JoinedCount starterPack={starterPack} />
		</Outer>
	);
}

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

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

function Icon() {
	return <StarterPackIcon className={css.starterPackIcon} />;
}

function TitleAndByline({ starterPack }: { starterPack: AnyStarterPackView }) {
	const { currentAccount } = useSession();
	const { creator } = starterPack;
	const record = getStarterPackRecord(starterPack);

	return (
		<div className={css.titleColumn}>
			<Text size="md" weight="semiBold" numberOfLines={2}>
				{record.name}
			</Text>
			<Text size="md_sub" color="textContrastMedium" numberOfLines={1}>
				{creator?.did === currentAccount?.did
					? m['common.starterPack.byYou']()
					: m['components.starterPack.card.byline']({ handle: creator.handle })}
			</Text>
		</div>
	);
}

function Description({ starterPack }: { starterPack: AnyStarterPackView }) {
	const record = getStarterPackRecord(starterPack);
	if (!record.description) {
		return null;
	}
	return (
		<Text size="md_sub" numberOfLines={3}>
			{record.description}
		</Text>
	);
}

function JoinedCount({ starterPack }: { starterPack: AnyStarterPackView }) {
	const { joinedAllTimeCount } = starterPack;
	if (!joinedAllTimeCount || joinedAllTimeCount < 50) {
		return null;
	}
	return (
		<Text size="md_sub" weight="semiBold" color="textContrastMedium">
			{m['components.starterPack.card.joinedCount']({ count: joinedAllTimeCount })}
		</Text>
	);
}

/**
 * Builds a navigable link to the starter pack screen plus a precache helper for the creator and starter pack
 * queries.
 */
export function useStarterPackLink({ view }: { view: AnyStarterPackView }) {
	const qc = useQueryClient();
	const rkey = parseCanonicalResourceUri(view.uri).rkey;
	const did = view.creator.did;
	const record = getStarterPackRecord(view);
	const precache = () => {
		precacheResolvedUri(qc, view.creator.handle, view.creator.did);
		precacheStarterPack(qc, view);
	};

	return {
		to: starterPackTarget(did, rkey),
		label: m['components.starterPack.card.navigate']({ name: record.name }),
		precache,
	};
}

/** A clickable wrapper that navigates to the starter pack screen and precaches its data. */
export function Link({
	starterPack,
	children,
	className,
	onPress,
}: {
	starterPack: AnyStarterPackView;
	children: ReactNode;
	className?: string;
	onPress?: () => void;
}) {
	const queryClient = useQueryClient();
	const navigationDisabled = useNavigationDisabled();
	const record = getStarterPackRecord(starterPack);
	const rkey = parseCanonicalResourceUri(starterPack.uri).rkey;
	const did = starterPack.creator.did;

	return (
		<WebLink
			to={starterPackTarget(did, rkey)}
			label={m['components.starterPack.card.navigate']({ name: record.name })}
			className={clsx(css.link({ interactive: !navigationDisabled }), className)}
			onPress={() => {
				precacheResolvedUri(queryClient, starterPack.creator.handle, starterPack.creator.did);
				precacheStarterPack(queryClient, starterPack);
				onPress?.();
			}}
		>
			{children}
		</WebLink>
	);
}

/** A starter pack embedded in a post: OG card image on top and the card body below. */
export function Embed({ starterPack }: { starterPack: AnyStarterPackView }) {
	const navigationDisabled = useNavigationDisabled();
	const imageUri = getStarterPackOgCard(starterPack);

	return (
		<Link starterPack={starterPack} className={css.embedCard({ interactive: !navigationDisabled })}>
			<EmbedThumb src={imageUri} />
			<div className={css.embedBody}>
				<Card starterPack={starterPack} />
			</div>
		</Link>
	);
}

/** Placeholder for the header's 40×40 starter-pack icon block. */
function IconPlaceholder() {
	return <Skeleton.Square radius={borderRadius.sm} size={40} />;
}

/** Placeholder for the title + byline stack that sits beside the icon. */
function TitleAndBylinePlaceholder() {
	return (
		<Skeleton.Col grow>
			<Skeleton.Text size="md" width="60%" />
			<Skeleton.Text color="contrast_25" size="md_sub" width="40%" />
		</Skeleton.Col>
	);
}

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

function LoadingRow({
	descriptionLines,
	joinedCount,
	topBorder,
}: {
	descriptionLines: number;
	joinedCount: boolean;
	topBorder: boolean;
}) {
	return (
		<Skeleton.Col className={css.loadingRow({ topBorder })} gap="sm">
			<Skeleton.Row align="start" gap="sm">
				<IconPlaceholder />
				<TitleAndBylinePlaceholder />
			</Skeleton.Row>
			{descriptionLines > 0 && <Skeleton.Lines count={descriptionLines} lastWidth={70} size="md_sub" />}
			{joinedCount && <Skeleton.Text size="md_sub" width="35%" />}
		</Skeleton.Col>
	);
}

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

// ~10% of packs have 50+ all-time joins and show a "joined" line; the rest omit it.
const JOINED_COUNT_WEIGHT = [9, 1];

/**
 * loading placeholders for starter pack cards.
 *
 * @param count number of placeholder 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 }, () => ({
		// starter pack descriptions are often empty or short; weight toward 0–1 lines with a tail toward 2–3.
		descriptionLines: weightedIndex(DESCRIPTION_LINE_WEIGHTS),
		joinedCount: weightedIndex(JOINED_COUNT_WEIGHT) === 1,
	}));

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