"use client";
import { useVirtualizer } from "@tanstack/react-virtual";
import { CheckIcon, ChevronsUpDown, Globe } from "lucide-react";
import * as React from "react";
import * as RPNInput from "react-phone-number-input";
import flags from "react-phone-number-input/flags";
import EN from "react-phone-number-input/locale/en";
import { Button } from "~/components/ui/button";
import {
	Command,
	CommandEmpty,
	CommandGroup,
	CommandInput,
	CommandItem,
} from "~/components/ui/command";
import { Input } from "~/components/ui/input";
import {
	Popover,
	PopoverContent,
	PopoverTrigger,
} from "~/components/ui/popover";
import { ScrollArea } from "~/components/ui/scroll-area";
import { cn } from "~/lib/utils";

type PhoneInputProps = Omit<
	React.ComponentProps<"input">,
	"onChange" | "value" | "ref"
> &
	Omit<RPNInput.Props<typeof RPNInput.default>, "onChange"> & {
		onChange?: (value: RPNInput.Value) => void;
	};

const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> = React.memo(
	React.forwardRef<
		React.ComponentRef<typeof RPNInput.default>,
		PhoneInputProps
	>(({ className, onChange, ...props }, ref) => {
		return (
			<RPNInput.default
				className={cn(
					"[&_input]:ph-input-fix flex h-auto w-full flex-1 **:h-auto!",
					className,
				)}
				countrySelectComponent={CountrySelect}
				defaultCountry="IT"
				flagComponent={FlagComponent}
				focusInputOnCountrySelection
				inputComponent={InputComponent}
				international
				labels={EN}
				onChange={(value) => onChange?.(value || ("" as RPNInput.Value))}
				ref={ref}
				/**
				 * Handles the onChange event.
				 *
				 * react-phone-number-input might trigger the onChange event as undefined
				 * when a valid phone number is not entered. To prevent this,
				 * the value is coerced to an empty string.
				 *
				 * @param {E164Number | undefined} value - The entered value
				 */
				smartCaret={false}
				{...props}
			/>
		);
	}),
);
PhoneInput.displayName = "PhoneInput";

const InputComponent = React.forwardRef<
	HTMLInputElement,
	React.ComponentProps<"input">
>(({ className, ...props }, ref) => (
	<Input
		className={cn("w-full flex-1 rounded-s-none rounded-e-md", className)}
		{...props}
		ref={ref}
	/>
));
InputComponent.displayName = "InputComponent";

type CountryEntry = { label: string; value: RPNInput.Country | undefined };

type CountrySelectProps = {
	disabled?: boolean;
	value: RPNInput.Country;
	options: CountryEntry[];
	onChange: (country: RPNInput.Country) => void;
};

const CountrySelect = React.memo(
	({
		disabled,
		value: selectedCountry,
		options: countryList,
		onChange,
	}: CountrySelectProps) => {
		const [filteredCountries, setFilteredCountries] = React.useState<
			CountryEntry[]
		>(countryList || []);
		const [open, setOpen] = React.useState(false);
		const [parentNode, setParentNode] = React.useState<HTMLDivElement | null>(
			null,
		);

		const refCallback = React.useCallback((node: HTMLDivElement) => {
			if (node) {
				setParentNode(node);
			}
		}, []);

		const virtualizer = useVirtualizer({
			count: filteredCountries.length,
			estimateSize: () => 35,
			getScrollElement: () => parentNode,
			overscan: 5,
		});

		const handleSearch = (search: string) => {
			if (!search.trim()) {
				setFilteredCountries(countryList);
			} else {
				setFilteredCountries(
					countryList.filter((country) =>
						country.label.toLowerCase().includes(search.toLowerCase()),
					),
				);
			}
		};

		return (
			<Popover onOpenChange={setOpen} open={open}>
				<PopoverTrigger asChild>
					<Button
						aria-expanded={open}
						className="flex gap-1 rounded-md rounded-e-none border-r-0 px-3 py-2 focus:z-10"
						role="combobox"
						variant="outline"
					>
						<FlagComponent
							country={selectedCountry}
							countryName={
								countryList.find((c) => c.value === selectedCountry)?.label ||
								""
							}
						/>
						<ChevronsUpDown
							className={cn(
								"-mr-2 size-4 opacity-50",
								disabled ? "hidden" : "opacity-100",
							)}
						/>
					</Button>
				</PopoverTrigger>
				<PopoverContent className="w-75 p-0">
					<Command shouldFilter={false}>
						<CommandInput
							className="border-none ring-0 focus:ring-0 focus-visible:ring-0"
							onValueChange={handleSearch}
							placeholder="Search country..."
						/>

						<ScrollArea className="h-100 overflow-auto">
							<CommandEmpty>No countries found</CommandEmpty>
							<CommandGroup ref={refCallback}>
								<div
									style={{
										height: `${virtualizer.getTotalSize()}px`,
										position: "relative",
										width: "100%",
									}}
								>
									{virtualizer
										.getVirtualItems()
										.map(({ index, size, start }) => {
											const country = filteredCountries[index];
											if (!country?.value) return null;
											return (
												<CommandItem
													key={country.value}
													// biome-ignore lint/style/noNonNullAssertion: <exists>
													onSelect={() => onChange(country.value!)}
													style={{
														height: `${size}px`,
														left: 0,
														position: "absolute",
														top: 0,
														transform: `translateY(${start}px)`,
														width: "100%",
													}}
													value={country.value}
												>
													<FlagComponent
														country={country.value}
														countryName={country.label}
													/>
													<span className="flex-1 text-sm">
														{country.label}
													</span>
													<span className="text-foreground/50 text-sm">{`+${RPNInput.getCountryCallingCode(
														country.value,
													)}`}</span>
													<CheckIcon
														className={`ml-auto size-4 ${
															country.value === selectedCountry
																? "opacity-100"
																: "opacity-0"
														}`}
													/>
												</CommandItem>
											);
										})}
								</div>
							</CommandGroup>
						</ScrollArea>
					</Command>
				</PopoverContent>
			</Popover>
		);
	},
);
CountrySelect.displayName = "CountrySelect";

const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
	const Flag = flags[country];

	return (
		<span className="flex size-6 items-center justify-center overflow-hidden rounded-sm">
			{Flag ? <Flag title={countryName} /> : <Globe className="size-4" />}
		</span>
	);
};

export { PhoneInput };
