import { useDebounce } from "@uidotdev/usehooks";
import { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";

import { SettingsItem } from "@components/Settings/SettingsItem";
import { MIcon } from "@components/common/MIcon";
import type { SelectItem } from "@components/common/Select";
import { useActiveRoute } from "@hooks/apis/common/quote/useActiveRoute";
import { isAutoQuote } from "@hooks/apis/common/quote/utils";
import { useTheme } from "@hooks/useTheme";
import { setSlippage, toggleDynamicSlippage } from "@store/settingsSlice";
import type { ReduxStateType } from "@store/types";
import { getBorderRadius } from "@utils/getBorderRadius";
import { truncateDecimalValue } from "@utils/truncateDecimalValue";

const slippageOptions: SelectItem[] = [
	{
		id: "dynamic",
		label: "suggested",
	},
	{
		id: "0.5",
		label: "0.5%",
	},
	{
		id: "1",
		label: "1%",
	},
];

export const Slippage = () => {
	const dispatch = useDispatch();
	const theme = useTheme();
	const currentSlippageId = useSelector(
		(state: ReduxStateType) => state.settings.slippage,
	);
	const currentSlippageItem = slippageOptions.filter(
		(item) => item.id === currentSlippageId,
	)[0];

	const [showSlippageOptions, setShowSlippageOptions] =
		useState<boolean>(false);

	const { data: activeRoute } = useActiveRoute();
	const slippage = isAutoQuote(activeRoute)
		? activeRoute.suggestedClientSlippage
		: activeRoute?.slippage;

	const setItem = useCallback(
		(_value: string) => {
			const isDynamic = _value === "dynamic";
			dispatch(toggleDynamicSlippage(isDynamic));
			dispatch(setSlippage(_value));
		},
		[dispatch],
	);

	const [inputValue, setInputValue] = useState<string>("");

	const debouncedValue = useDebounce(inputValue, 300);

	useEffect(() => {
		if (debouncedValue) {
			if (Number(debouncedValue) === 0) {
				setItem("0.1");
			} else setItem(debouncedValue);
		}
	}, [debouncedValue, setItem]);

	function setValue(value: string) {
		if (value === "") {
			setInputValue("");
			return;
		}

		// Allow only numbers with up to 2 decimal places
		if (!/^\d*(?:\.\d{0,2})?$/.test(value)) return;

		const formattedValue = truncateDecimalValue(value, 2);
		if (!formattedValue) return;

		// Don't convert to number if the value ends with just a decimal point
		// This allows typing "0." without it being converted to 0.1
		if (value.endsWith(".") && !value.includes("..")) {
			setInputValue(value);
			return;
		}

		const numericValue = Number(formattedValue);

		// Cap at 50 maximum
		if (numericValue > 50) {
			setInputValue("50");
			return;
		}

		// Only enforce minimum when the user has finished typing a complete number
		// Allow intermediate values like "0." or "0.0" for typing "0.5"
		if (numericValue > 0 && numericValue < 0.1) {
			setInputValue("0.1");
			return;
		}

		setInputValue(formattedValue);
	}

	useEffect(() => {
		const _isCustomValue =
			currentSlippageId !== "dynamic" &&
			currentSlippageId !== "0.5" &&
			currentSlippageId !== "1";

		if (_isCustomValue) {
			setInputValue(currentSlippageId);
		} else setInputValue("");
	}, [currentSlippageId]);

	const disclaimer = (() => {
		if (currentSlippageId === "dynamic") {
			return {
				warn: false,
				message: "Bungee will find the best slippage for a successful swap.",
			};
		}

		if (slippage && Number(currentSlippageId) < slippage)
			return {
				warn: true,
				message:
					"Using a lower slippage than suggested might prevent your swap from going through.",
			};

		return undefined;
	})();

	const currentValueLabel =
		currentSlippageId === "dynamic" ? "Suggested" : `${currentSlippageId}%`;

	return (
		<div className="bn-relative bn-mb-4">
			<div
				className="bn-mb-4 last:bn-mb-0 bn-bg-layer-2"
				style={{ borderRadius: getBorderRadius(theme.borderRadius, 8) }}
			>
				<div className="bn-flex bn-items-center bn-justify-between bn-w-full bn-text-primary bn-text-base bn-font-medium bn-px-4 bn-py-[14px]">
					<span>Slippage:</span>

					<div className="bn-flex bn-items-center bn-gap-2 bn-text-sm">
						{currentValueLabel && <span>{currentValueLabel}</span>}
						<button
							type="button"
							className="bn-text-base bn-font-medium bn-size-6 bn-flex-shrink-0"
							onClick={() => setShowSlippageOptions(!showSlippageOptions)}
						>
							<MIcon
								name={
									showSlippageOptions
										? "keyboard_arrow_up"
										: "keyboard_arrow_down"
								}
								classNames="bn-text-primary"
							/>
						</button>
					</div>
				</div>

				{showSlippageOptions && (
					<>
						<div className="bn-flex bn-items-center bn-justify-normal bn-space-x-1 bn-border-t bn-border-solid bn-border-subtle bn-px-4 bn-py-2.5 bn-overflow-hidden">
							{slippageOptions.map((item) => {
								return (
									<SlippageButton
										key={item.id}
										label={item.label}
										active={currentSlippageId === item.id}
										onClick={() => {
											setItem(item.id);
										}}
									/>
								);
							})}
							<div
								className="bn-bg-layer-3 bn-px-2.5 bn-flex bn-items-center bn-flex-1 bn-h-[40px] bn-text-sm bn-font-semibold bn-text-secondary"
								style={{ borderRadius: getBorderRadius(theme.borderRadius, 8) }}
							>
								<input
									className="bn-bg-transparent bn-px-1 bn-border-0 bn-flex-1 bn-text-primary bn-text-sm bn-text-right"
									type="number"
									min={0.1}
									max={50}
									step={0.01}
									value={inputValue}
									onChange={(e) => setValue(e.target.value)}
									id="bungee-custom-slippage-input"
									placeholder="Custom"
								/>
								<span className="bn-flex-shrink-0">%</span>
							</div>
						</div>
						{disclaimer && (
							<div
								className={`bn-pb-2 bn-leading-[19px] bn-font-normal bn-text-sm bn-px-4 ${
									disclaimer.warn ? "bn-text-accent-1" : "bn-text-secondary"
								}`}
							>
								{disclaimer.message}
							</div>
						)}
					</>
				)}
			</div>
		</div>
	);
};

const SlippageButton = ({
	label,
	onClick,
	active = false,
}: {
	label: string;
	onClick: () => void;
	active?: boolean;
}) => {
	const theme = useTheme();

	// @fixme: hardcoding text color for active variant
	const style = `bn-text-sm bn-cursor-pointer bn-font-semibold bn-border bn-border-solid bn-capitalize bn-h-[40px] bn-px-4 bn-min-w-[62px] ${
		active
			? "bn-bg-accent-1 bn-border-accent-1 bn-text-accent-1"
			: "bn-text-primary bn-bg-layer-3 bn-border-transparent"
	}`;
	return (
		<button
			type="button"
			className={style}
			style={{ borderRadius: getBorderRadius(theme.borderRadius, 8) }}
			onClick={onClick}
		>
			{label}
		</button>
	);
};
