import * as React from "react";
import { isColorDark } from "~/lib/color";
import { FACES } from "./faces";

// from https://github.com/cossistantcom/cossistant/tree/main/packages/facehash

type Intensity3D = "none" | "subtle" | "medium" | "dramatic";
type Variant = "gradient" | "solid";

interface FacehashProps
	extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
	/**
	 * String to generate a deterministic face from.
	 * Same string always produces the same face.
	 */
	name: string;

	/**
	 * Size in pixels or CSS units.
	 * @default 40
	 */
	size?: number | string;

	/**
	 * Background style.
	 * - "gradient": Adds gradient overlay (default)
	 * - "solid": Plain background color
	 * @default "gradient"
	 */
	variant?: Variant;

	/**
	 * 3D effect intensity.
	 * @default "dramatic"
	 */
	intensity3d?: Intensity3D;

	/**
	 * Enable hover interaction.
	 * When true, face "looks straight" on hover.
	 * @default true
	 */
	interactive?: boolean;

	/**
	 * Show first letter of name below the face.
	 * @default true
	 */
	showInitial?: boolean;

	initialsCount?: number;

	/**
	 * Hex color array for inline styles.
	 * Use this OR colorClasses, not both.
	 */
	colors?: string[];

	/**
	 * Tailwind class array for background colors.
	 * Example: ["bg-pink-500 dark:bg-pink-600", "bg-blue-500 dark:bg-blue-600"]
	 * Use this OR colors, not both.
	 */
	colorClasses?: string[];

	/**
	 * Custom gradient overlay class (Tailwind).
	 * When provided, replaces the default pure CSS gradient.
	 * Only used when variant="gradient".
	 */
	gradientOverlayClass?: string;
}

// ============================================================================
// Constants
// ============================================================================

const INTENSITY_PRESETS = {
	none: {
		rotateRange: 0,
		translateZ: 0,
		perspective: "none",
	},
	subtle: {
		rotateRange: 5,
		translateZ: 4,
		perspective: "800px",
	},
	medium: {
		rotateRange: 10,
		translateZ: 8,
		perspective: "500px",
	},
	dramatic: {
		rotateRange: 15,
		translateZ: 12,
		perspective: "300px",
	},
} as const;

const SPHERE_POSITIONS = [
	{ x: -1, y: 1 }, // down-right
	{ x: 1, y: 1 }, // up-right
	{ x: 1, y: 0 }, // up
	{ x: 0, y: 1 }, // right
	{ x: -1, y: 0 }, // down
	{ x: 0, y: 0 }, // center
	{ x: 0, y: -1 }, // left
	{ x: -1, y: -1 }, // down-left
	{ x: 1, y: -1 }, // up-left
] as const;

// Default gradient as pure CSS (works without Tailwind)
// Matches: bg-[radial-gradient(ellipse_100%_100%_at_50%_50%,_COLOR_0%,_transparent_60%)]
// Light mode: white glow in center, Dark mode: dark overlay in center
const DEFAULT_GRADIENT_STYLE: React.CSSProperties = {
	background:
		"radial-gradient(ellipse 100% 100% at 50% 50%, rgba(255,255,255,0.15) 0%, transparent 60%)",
};

/**
 * Facehash - Deterministic avatar faces from any string.
 *
 * @example
 * ```tsx
 * // With Tailwind classes
 * <Facehash
 *   name="John"
 *   colorClasses={["bg-pink-500", "bg-blue-500"]}
 * />
 *
 * // With hex colors
 * <Facehash
 *   name="John"
 *   colors={["#ec4899", "#3b82f6"]}
 * />
 *
 * // Plain color (no gradient)
 * <Facehash name="John" variant="solid" />
 * ```
 */
export const Facehash = React.forwardRef<HTMLDivElement, FacehashProps>(
	(
		{
			name,
			size = 40,
			variant = "gradient",
			intensity3d = "dramatic",
			interactive = true,
			showInitial = true,
			initialsCount = 1,
			colors,
			colorClasses,
			gradientOverlayClass,
			className,
			style,
			onMouseEnter,
			onMouseLeave,
			...props
		},
		ref,
	) => {
		const [isHovered, setIsHovered] = React.useState(false);

		// Generate deterministic values from name
		const { FaceComponent, colorIndex, rotation } = React.useMemo(() => {
			const hash = stringHash(name);
			const faceIndex = hash % FACES.length;
			const colorsLength = colorClasses?.length ?? colors?.length ?? 1;
			const _colorIndex = hash % colorsLength;
			const positionIndex = hash % SPHERE_POSITIONS.length;
			const position = SPHERE_POSITIONS[positionIndex] ?? { x: 0, y: 0 };

			return {
				FaceComponent: FACES[faceIndex] ?? FACES[0],
				colorIndex: _colorIndex,
				rotation: position,
			};
		}, [name, colors?.length, colorClasses?.length]);

		// Get intensity preset
		const preset = INTENSITY_PRESETS[intensity3d];

		// Calculate 3D transform
		const transform = React.useMemo(() => {
			if (intensity3d === "none") {
				return;
			}

			const rotateX =
				isHovered && interactive ? 0 : rotation.x * preset.rotateRange;
			const rotateY =
				isHovered && interactive ? 0 : rotation.y * preset.rotateRange;

			return `rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateZ(${preset.translateZ}px)`;
		}, [intensity3d, isHovered, interactive, rotation, preset]);

		// Size style
		const sizeValue = typeof size === "number" ? `${size}px` : size;

		// Initial letter
		const initial = name.trim().substring(0, initialsCount).toUpperCase();

		// Background: either hex color (inline) or class
		const bgColorClass = colorClasses?.[colorIndex];
		const bgColorHex = colors?.[colorIndex];

		// Event handlers
		const handleMouseEnter = React.useCallback(
			(e: React.MouseEvent<HTMLDivElement>) => {
				if (interactive) {
					setIsHovered(true);
				}
				onMouseEnter?.(e);
			},
			[interactive, onMouseEnter],
		);

		const handleMouseLeave = React.useCallback(
			(e: React.MouseEvent<HTMLDivElement>) => {
				if (interactive) {
					setIsHovered(false);
				}
				onMouseLeave?.(e);
			},
			[interactive, onMouseLeave],
		);

		return (
			// biome-ignore lint/a11y/noNoninteractiveElementInteractions: Hover effect is purely cosmetic
			// biome-ignore lint/a11y/noStaticElementInteractions: This is a decorative avatar component
			<div
				className={[bgColorClass, className].filter(Boolean).join(" ")}
				data-facehash=""
				data-interactive={interactive || undefined}
				onMouseEnter={handleMouseEnter}
				onMouseLeave={handleMouseLeave}
				ref={ref}
				style={{
					// Size
					width: sizeValue,
					height: sizeValue,
					// Layout
					position: "relative",
					display: "flex",
					alignItems: "center",
					justifyContent: "center",
					overflow: "hidden",
					// Container for cqw units
					containerType: "size",
					// 3D setup
					...(intensity3d !== "none" && {
						perspective: preset.perspective,
						transformStyle: "preserve-3d",
					}),
					// Background color (hex) - only if no colorClasses
					...(bgColorHex && !bgColorClass && { backgroundColor: bgColorHex }),
					// User styles (last to allow overrides)
					...style,
				}}
				{...props}
			>
				{/* Gradient overlay */}
				{variant === "gradient" && (
					<div
						className={gradientOverlayClass}
						data-facehash-gradient=""
						style={{
							position: "absolute",
							inset: 0,
							pointerEvents: "none",
							zIndex: 1,
							// Use default pure CSS gradient if no class provided
							...(gradientOverlayClass ? {} : DEFAULT_GRADIENT_STYLE),
						}}
					/>
				)}

				{/* Face container with 3D transform */}
				<div
					data-facehash-face=""
					style={{
						position: "absolute",
						inset: 0,
						display: "flex",
						flexDirection: "column",
						alignItems: "center",
						justifyContent: "center",
						zIndex: 2,
						transform,
						transformStyle: intensity3d !== "none" ? "preserve-3d" : undefined,
						transition: interactive
							? "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
							: undefined,
						// Default to black text/icons for contrast on colored backgrounds
						//color: "#000000",
						color: bgColorHex
							? isColorDark(bgColorHex, 100)
								? "#FFFFFF"
								: "#000000"
							: "#000000",
					}}
				>
					{/* Face SVG */}
					<FaceComponent
						style={{
							width: "60%",
							height: "auto",
							maxWidth: "90%",
							maxHeight: "40%",
						}}
					/>

					{/* Initial letter */}
					{showInitial && (
						<span
							data-facehash-initial=""
							style={{
								marginTop: "8%",
								fontSize: "26cqw",
								lineHeight: 1,
								fontFamily: "monospace",
								fontWeight: "bold",
							}}
						>
							{initial}
						</span>
					)}
				</div>
			</div>
		);
	},
);

Facehash.displayName = "Facehash";

/**
 * Generates a consistent numeric hash from a string.
 * Used to deterministically select faces and colors for avatars.
 *
 * @param str - The input string to hash
 * @returns A positive 32-bit integer hash
 */
function stringHash(str: string): number {
	let hash = 0;
	for (let i = 0; i < str.length; i++) {
		const char = str.charCodeAt(i);
		hash = (hash << 5) - hash + char;
		hash &= hash; // Convert to 32bit integer
	}
	return Math.abs(hash);
}
