import { ChevronDown, ChevronUp } from "lucide-react";
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import { NumericFormat, type NumericFormatProps } from "react-number-format";
import Input from "~/components/ui/input";
import { cn } from "~/lib/utils";
import { Button } from "../ui/button";

interface NumberInputProps
	extends Omit<NumericFormatProps, "value" | "onValueChange"> {
	stepper?: number;
	thousandSeparator?: string;
	placeholder?: string;
	defaultValue?: number;
	min?: number;
	max?: number;
	value?: number; // Controlled value
	suffix?: string;
	prefix?: string;
	onValueChange?: (value: number | undefined) => void;
	fixedDecimalScale?: boolean;
	decimalScale?: number;
	height?: string;
}

export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
	(
		{
			stepper,
			thousandSeparator,
			placeholder,
			defaultValue,
			min = -Infinity,
			max = Infinity,
			onValueChange,
			fixedDecimalScale = false,
			decimalScale = 0,
			suffix,
			prefix,
			value: controlledValue,
			className,
			height = "h-10",
			...props
		},
		externalRef,
	) => {
		const internalRef = useRef<HTMLInputElement>(null);
		const inputRef =
			(externalRef as React.RefObject<HTMLInputElement>) || internalRef;
		const [value, setValue] = useState<number | undefined>(
			controlledValue ?? defaultValue,
		);

		const handleIncrement = useCallback(() => {
			setValue((prev) =>
				prev === undefined
					? (stepper ?? 1)
					: Math.min(prev + (stepper ?? 1), max),
			);
		}, [stepper, max]);

		const handleDecrement = useCallback(() => {
			setValue((prev) =>
				prev === undefined
					? -(stepper ?? 1)
					: Math.max(prev - (stepper ?? 1), min),
			);
		}, [stepper, min]);

		useEffect(() => {
			const handleKeyDown = (e: KeyboardEvent) => {
				if (!inputRef.current) return;

				if (document.activeElement === inputRef.current) {
					if (e.key === "ArrowUp") {
						handleIncrement();
					} else if (e.key === "ArrowDown") {
						handleDecrement();
					}
				}
			};

			window.addEventListener("keydown", handleKeyDown);

			return () => {
				window.removeEventListener("keydown", handleKeyDown);
			};
		}, [handleIncrement, handleDecrement, inputRef]);

		useEffect(() => {
			if (controlledValue !== undefined) {
				setValue(controlledValue);
			}
		}, [controlledValue]);

		const handleChange = (values: {
			value: string;
			floatValue: number | undefined;
		}) => {
			const newValue =
				values.floatValue === undefined ? undefined : values.floatValue;
			setValue(newValue);
			if (onValueChange) {
				onValueChange(newValue);
			}
		};

		const handleBlur = () => {
			if (value !== undefined) {
				if (value < min) {
					setValue(min);
					if (inputRef.current) {
						inputRef.current.value = String(min);
					}
				} else if (value > max) {
					setValue(max);
					if (inputRef.current) {
						inputRef.current.value = String(min);
					}
				}
			}
		};

		return (
			<div className={cn("flex items-stretch bg-card", height)}>
				<NumericFormat
					allowNegative={min < 0}
					className={cn(
						"relative rounded-r-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
						height,
						className,
					)}
					customInput={Input}
					decimalScale={decimalScale}
					fixedDecimalScale={fixedDecimalScale}
					getInputRef={inputRef}
					max={max}
					min={min}
					onBlur={handleBlur}
					onValueChange={handleChange}
					placeholder={placeholder}
					prefix={prefix}
					suffix={suffix}
					thousandSeparator={thousandSeparator}
					value={value}
					valueIsNumericString
					{...props}
				/>

				<div className="flex flex-col self-stretch">
					<Button
						aria-label="Increase value"
						className="h-1/2 rounded-l-none rounded-br-none border-input border-b-[0.5px] border-l-0 bg-card px-2 focus-visible:relative"
						disabled={value === max}
						onClick={handleIncrement}
						type="button"
						variant="outline"
					>
						<ChevronUp size={15} />
					</Button>
					<Button
						aria-label="Decrease value"
						className="h-1/2 rounded-l-none rounded-tr-none border-input border-t-[0.5px] border-l-0 bg-card px-2 focus-visible:relative"
						disabled={value === min}
						onClick={handleDecrement}
						type="button"
						variant="outline"
					>
						<ChevronDown size={15} />
					</Button>
				</div>
			</div>
		);
	},
);
