'use no memo'; // composition props usually invalidate the generated wrapper caches

import type { ReactNode } from 'react';

import { assignInlineVars } from '@vanilla-extract/dynamic';
import { clsx } from 'clsx';

import * as styles from '#/components/web/Skeleton.css';

import type { RecipeVariants } from '#/styles/recipe';
import { borderRadius } from '#/styles/tokens.css';

const GOLDEN_RATIO = 1.618;

/** Contrast level of a placeholder, so a group of them can be tuned to the surface they sit on. */
export type Color = RecipeVariants<typeof styles.text>['color'];

/** Loading placeholder for a line of {@link Text}: a rounded bar sized to the matching `size`'s line box. */
export function Text({
	color = 'contrast_50',
	size = 'md',
	width,
}: {
	/** The color level of this line placeholder. */
	color?: Color;
	/** The `Text` size the line will render at, so the placeholder matches its height. */
	size?: RecipeVariants<typeof styles.text>['size'];
	/** Bar width; capped to the container. Omit to fill the available width. */
	width?: number | string;
}) {
	return (
		<div
			className={styles.text({ color, size })}
			style={
				width !== undefined
					? assignInlineVars({ [styles.widthVar]: typeof width === 'number' ? `${width}px` : width })
					: undefined
			}
		>
			<div className={styles.bar} />
		</div>
	);
}

/** A horizontal flex group of placeholders. */
export function Row({
	align,
	children,
	className,
	gap,
}: {
	align?: RecipeVariants<typeof styles.row>['align'];
	children?: ReactNode;
	className?: string;
	gap?: RecipeVariants<typeof styles.row>['gap'];
}) {
	return <div className={clsx(styles.row({ align, gap }), className)}>{children}</div>;
}

/** A vertical flex group of placeholders. */
export function Col({
	children,
	className,
	gap,
	grow,
}: {
	children?: ReactNode;
	className?: string;
	gap?: RecipeVariants<typeof styles.col>['gap'];
	grow?: boolean;
}) {
	return <div className={clsx(styles.col({ gap, grow }), className)}>{children}</div>;
}

/** Loading placeholder for a circular element (e.g. a user avatar), sized to `size` pixels. */
export function Circle({
	blend,
	children,
	color = 'contrast_50',
	size,
}: {
	/** When true, lowers the placeholder's opacity so it recedes behind foreground content. */
	blend?: boolean;
	children?: ReactNode;
	color?: Color;
	size: number;
}) {
	return (
		<div
			className={styles.circle({ blend, color })}
			style={assignInlineVars({ [styles.boxSizeVar]: `${size}px` })}
		>
			{children}
		</div>
	);
}

/**
 * Loading placeholder for a pill-shaped element (e.g. a stat/count bar): a rounded-full bar `size` pixels
 * tall, with a fixed height-to-width ratio.
 */
export function Pill({
	blend,
	color = 'contrast_50',
	size,
}: {
	/** When true, lowers the placeholder's opacity so it recedes behind foreground content. */
	blend?: boolean;
	color?: Color;
	size: number;
}) {
	return (
		<div
			className={styles.pill({ blend, color })}
			style={assignInlineVars({
				[styles.boxSizeVar]: `${size}px`,
				[styles.pillWidthVar]: `${size * GOLDEN_RATIO}px`,
			})}
		/>
	);
}

/**
 * Loading placeholder for a rounded-square element (e.g. a feed/list avatar), sized to `size` pixels with a
 * `radius` corner (defaults to the small token).
 */
export function Square({ radius = borderRadius.xs, size }: { radius?: number; size: number }) {
	return (
		<div
			className={styles.square({ color: 'contrast_50' })}
			style={assignInlineVars({ [styles.boxSizeVar]: `${size}px`, [styles.squareRadiusVar]: `${radius}px` })}
		/>
	);
}

/** paragraph of text-line placeholders that closes on a shorter final line. renders inside a {@link Col}. */
export function Lines({
	color = 'contrast_25',
	count,
	lastWidth,
	size = 'md',
}: {
	/** The color level of the lines. Defaults to contrast_25. */
	color?: Color;
	/** Number of lines to render. */
	count: number;
	/** Width of the final, partial line, as a percentage. */
	lastWidth: number;
	/** The {@link Text} size every line renders at. */
	size?: RecipeVariants<typeof styles.text>['size'];
}) {
	return (
		<Col>
			{Array.from({ length: count }, (_, i) => (
				<Text key={i} color={color} size={size} width={i === count - 1 ? `${lastWidth}%` : '100%'} />
			))}
		</Col>
	);
}
