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

declare module "react-select/base" {
	export interface Props<
		Option,
		IsMulti extends boolean,
		Group extends GroupBase<Option>,
	> {
		autoComplete?: HTMLInputAutoCompleteAttribute;
	}
}
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[];
	autoComplete?: HTMLInputAutoCompleteAttribute;
};

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,
		estimateSize: () => 45,
		getScrollElement: () => parentRef.current,
	});
	const items = virtualizer.getVirtualItems();

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

const CustomInput = (
	props: InputProps<ListOption, boolean, GroupBase<ListOption>>,
) => {
	return (
		<components.Input
			{...props}
			// This pulls the value from the main ReactSelect wrapper

			autoComplete={props.selectProps.autoComplete}
		/>
	);
};
/**
 *
 * @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,
	autoComplete,
}: MultiSelectProps) => {
	const [menuOpen, setMenuOpen] = useState(false);

	return (
		<ReactSelect
			autoComplete={autoComplete}
			classNamePrefix="select"
			classNames={{
				control: () =>
					cn(
						"mt-2 w-full rounded-lg h-10 min-h-10! bg-card shadow-xs outline-hidden text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
						disabled && "bg-muted opacity-50 cursor-not-allowed",
					),
				input: () => cn("*:ring-0 focus:*:ring-0"),
				menu: () =>
					cn(
						`bg-black text-foreground  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",
					),
				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",
					),
				multiValueLabel: () =>
					cn(
						"dark:bg-black rounded-lg text-primary bg-white dark:text-white outline-hidden",
					),
				multiValueRemove: () => cn("rounded-s-none rounded-e-md"),
				option: () =>
					cn(
						"bg-card dark:bg-input border-b border-foreground/50 shadow-xs outline-hidden hover:text-primary hover:bg-neutral-200 dark:hover:bg-neutral-700",
					),
				singleValue: () => cn("text-primary font-medium"),
				valueContainer: () => cn("min-h-[36px]"),
			}}
			components={{
				MenuList: VirtualizedMenuList,
				Input: CustomInput,
			}}
			defaultValue={defaultValue}
			inputId={elementId}
			instanceId={elementId}
			isDisabled={disabled}
			isMulti={isMulti}
			menuPlacement={menuPlacement}
			name={elementName}
			onChange={(values) => {
				if (isMulti) {
					const valore = values as ListOption[];
					onValueChange(valore);
				} else {
					const valore = values as ListOption;
					onValueChange(valore);
				}
			}}
			onMenuClose={() => setMenuOpen(false)}
			onMenuOpen={() => setMenuOpen(true)}
			options={options}
			placeholder={placeholder}
			styles={{
				container: (base, state) =>
					({
						...base,

						":hover .select__dropdown-indicator": {
							color: state.isFocused ? "rgb(161,161,170)" : "rgb(161,161,170)",
						},

						":hover .select__indicator-separator": {
							backgroundColor: state.isFocused
								? "rgb(161,161,170)"
								: "rgb(161,161,170)",
						},
					}) as CSSObjectWithLabel,
				control: (base, state) => {
					const {
						backgroundColor: _bg,
						border: _border,
						borderColor: _bc,
						...rest
					} = base;
					return {
						...rest,

						"& .select__dropdown-indicator": {
							color: state.isFocused
								? "rgb(161,161,170)"
								: "rgb(212 ,212 ,216)",
							transition: "none",
						},

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

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

						border: state.isFocused && "1px solid rgb(161,161,170)",
						borderRadius: "0.5rem",
						boxShadow: undefined,
						minHeight: "42px",
					} as CSSObjectWithLabel;
				},
				singleValue: (base) =>
					({
						...base,
						color: "var(--color-primary)",
						fontWeight: 500,
					}) as CSSObjectWithLabel,
				menu: (base) =>
					({
						...base,
						borderRadius: "0.5rem",
					}) as CSSObjectWithLabel,
				multiValue: (base) =>
					({
						...base,
						color: "var(--color-primary)",
						backgroundColor: undefined,
						borderRadius: "0.5rem",
					}) as CSSObjectWithLabel,
				multiValueLabel: (base) =>
					({
						...base,
						borderRadius: "0.5rem",
						color: "var(--color-primary)",
					}) as CSSObjectWithLabel,
				option: (base, state) =>
					({
						...base,

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

						"&:focus": {
							backgroundColor: undefined,
						},

						"&:hover": {
							backgroundColor: undefined,
							color: state.isSelected ? undefined : undefined,
						},
						backgroundColor: undefined,
						color: state.isSelected ? undefined : undefined,
					}) as CSSObjectWithLabel,
			}}
		/>
	);
};

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