import { useVirtualizer } from "@tanstack/react-virtual";
import { type JSX, useRef, useState } from "react";
import ReactSelect, {
	type CSSObjectWithLabel,
	type GroupBase,
	type MenuListProps,
} from "react-select";
import { cn } from "~/lib/utils";

export type ListOption = {
	label: string;
	value: string;
};

type BaseProps = {
	elementId: string;
	elementName: string;
	options: ListOption[];
	placeholder: string;
	disabled?: boolean;
	menuPlacement?: "auto" | "bottom" | "top";
	defaultValue?: ListOption | ListOption[];
};

type MultiProps = {
	isMulti: true;
	onValueChange: (value: ListOption[]) => void;
};
type SigletProps = {
	isMulti: false;
	onValueChange: (value: ListOption) => void;
};
type MultiSelectProps = BaseProps & (MultiProps | SigletProps);

const VirtualizedMenuList = (
	props: MenuListProps<
		ListOption,
		MultiSelectProps["isMulti"],
		GroupBase<ListOption>
	>,
) => {
	const rows = props.children as JSX.Element[];
	const parentRef = useRef<HTMLDivElement>(null);
	const count = rows.length;

	const virtualizer = useVirtualizer({
		count,
		getScrollElement: () => parentRef.current,
		estimateSize: () => 45,
	});
	const items = virtualizer.getVirtualItems();

	return (
		<div
			ref={parentRef}
			className="List"
			style={{
				height: `${virtualizer.getTotalSize()}px`,
				maxHeight: "300px",
				width: "auto",
				overflowY: "auto",
				contain: "strict",
			}}
		>
			<div
				style={{
					height: virtualizer.getTotalSize(),
					width: "100%",
					position: "relative",
				}}
			>
				<div
					style={{
						position: "absolute",
						top: 0,
						left: 0,
						width: "100%",
						transform: `translateY(${items[0]?.start ?? 0}px)`,
					}}
				>
					{items.map((virtualRow) => (
						<div
							key={virtualRow.key}
							data-index={virtualRow.index}
							ref={virtualizer.measureElement}
							className={virtualRow.index % 2 ? "ListItemOdd" : "ListItemEven"}
						>
							{rows[virtualRow.index]}
						</div>
					))}
				</div>
			</div>
		</div>
	);
};

/**
 *
 * @example
 * <MultiSelect
 *   elementId="select-naz"
 *   elementName="nazioneNascita"
 *   options={UnpackOptions({ options: nazioni_options })}
 *   defaultValue={RepackValues({
 *     options: nazioni_options,
 *     values: field.value,
 *   })}
 *   onValueChange={(value) => {
 *     field.onChange(value.value);
 *     //if isMulti
 *     field.onChange(value.map((v) => v.value));
 *   }}
 *   placeholder="Seleziona..."
 *   isMulti={false}
 * />
 */
export const MultiSelect = ({
	elementId,
	defaultValue,
	elementName,
	isMulti,
	onValueChange,
	options,
	placeholder,
	disabled,
	menuPlacement,
}: MultiSelectProps) => {
	const [menuOpen, setMenuOpen] = useState(false);

	return (
		<ReactSelect
			menuPlacement={menuPlacement}
			components={{ MenuList: VirtualizedMenuList }}
			inputId={elementId}
			isMulti={isMulti}
			isDisabled={disabled}
			instanceId={elementId}
			name={elementName}
			options={options}
			onChange={(values) => {
				if (isMulti) {
					const valore = values as ListOption[];
					onValueChange(valore);
				} else {
					const valore = values as ListOption;
					onValueChange(valore);
				}
			}}
			defaultValue={defaultValue}
			placeholder={placeholder}
			onMenuOpen={() => setMenuOpen(true)}
			onMenuClose={() => setMenuOpen(false)}
			styles={{
				container: (base, state) =>
					({
						...base,

						":hover .select__indicator-separator": {
							backgroundColor: state.isFocused
								? "rgb(161,161,170)"
								: "rgb(161,161,170)",
						},

						":hover .select__dropdown-indicator": {
							color: state.isFocused ? "rgb(161,161,170)" : "rgb(161,161,170)",
						},
					}) as CSSObjectWithLabel,
				control: (base, state) =>
					({
						...base,
						minHeight: "42px",
						borderRadius: "0.5rem",
						boxShadow: undefined,

						border: state.isFocused
							? "1px solid rgb(161,161,170)"
							: "1px solid rgb(212 ,212 ,216)",

						"&:hover": {
							borderColor: state.isFocused
								? "rgb(161,161,170)"
								: "rgb(161 ,161 ,170)",
						},

						"& .select__indicator-separator": {
							backgroundColor: state.isFocused
								? "rgb(161,161,170)"
								: "rgb(212 ,212 ,216)",
						},

						"& .select__dropdown-indicator": {
							color: state.isFocused
								? "rgb(161,161,170)"
								: "rgb(212 ,212 ,216)",
							transition: "none",
						},
					}) as CSSObjectWithLabel,
				multiValue: (base) =>
					({
						...base,
						backgroundColor: undefined,
						borderRadius: "0.5rem",
					}) as CSSObjectWithLabel,
				multiValueLabel: (base) =>
					({
						...base,
						borderRadius: "0.5rem",
					}) as CSSObjectWithLabel,
				menu: (base) =>
					({
						...base,
						borderRadius: "0.5rem",
					}) as CSSObjectWithLabel,
				option: (base, state) =>
					({
						...base,
						backgroundColor: undefined,
						color: state.isSelected ? undefined : undefined,

						"&:hover": {
							backgroundColor: undefined,
							color: state.isSelected ? undefined : undefined,
						},

						"&:active": {
							backgroundColor: "rgb(161, 161, 170)",
							color: "rgb(255, 255, 255)",
						},

						"&:focus": {
							backgroundColor: undefined,
						},
					}) as CSSObjectWithLabel,
			}}
			classNames={{
				valueContainer: () => cn("min-h-[38px]"),
				control: () =>
					cn(
						"mt-2 w-full rounded-lg h-min-[42px] dark:bg-primary bg-white shadow-xs outline-hidden text-foreground dark:text-white",
					),
				option: () =>
					cn(
						"dark:bg-black text-foreground bg-white dark:text-white border-b border-neutral-300 dark:border-foreground shadow-xs outline-hidden hover:text-primary hover:bg-neutral-200 dark:hover:bg-neutral-700",
					),
				menu: () =>
					cn(
						`dark:bg-black text-foreground bg-white dark:text-white border shadow-xs outline-hidden
                          dark:border-foreground border-neutral-300 overflow-hidden`,
						menuOpen
							? "animate-in fade-in-0 zoom-in-90"
							: "animate-out fade-out-0 zoom-out-90",
					),
				multiValueLabel: () =>
					cn(
						"dark:bg-black rounded-lg text-primary bg-white dark:text-white outline-hidden",
					),
				multiValue: () =>
					cn(
						"overflow-hidden dark:bg-black rounded-lg text-primary bg-white dark:text-white border border-neutral-300 dark:border-foreground shadow-xs outline-hidden",
					),
				multiValueRemove: () => cn("rounded-s-none rounded-e-md"),
				input: () => cn("*:ring-0 focus:*:ring-0"),
				singleValue: () => cn("text-primary font-medium"),
			}}
			classNamePrefix="select"
		/>
	);
};

type UnpackOptions = {
	options: string[];
};
export const UnpackOptions = ({ options }: UnpackOptions) => {
	return options.map((v, idx) => ({ value: idx.toString(), label: v }));
};

type RepackValues = {
	values: string | string[];
	options: string[];
};
export const RepackValues = ({ values, options }: RepackValues) => {
	if (Array.isArray(values)) {
		return values.map((v) => ({
			// biome-ignore lint/style/noNonNullAssertion: <intended>
			label: options[Number.parseInt(v)]!,
			value: v,
		}));
	}
	return {
		// biome-ignore lint/style/noNonNullAssertion: <intended>
		label: options[Number.parseInt(values)]!,
		value: values,
	};
};
