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

import type { SelectItem } from "@components/common/Select";
import { useActiveRoute } from "@hooks/apis/common/quote/useActiveRoute";
import { isAutoQuote } from "@hooks/apis/common/quote/useQuote";
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";
import { SettingsItem } from "../SettingsItem";

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 { 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;
  })();

  return (
    <div className="bn-relative bn-mb-4">
      <SettingsItem
        label="Slippage Tolerance"
        key="slippage-tolerance"
        icon="surfing"
      >
        <span className="bn-text-sm bn-font-medium bn-aeonik-font bn-text-theme bn-capitalize">
          {currentSlippageId === "dynamic" ? (
            <span>
              {slippage && <span>{slippage}% ~ </span>}{" "}
              <span className="bn-text-secondary">Suggested</span>
            </span>
          ) : (
            <span>{`${currentSlippageId}%`}</span>
          )}
        </span>
      </SettingsItem>
      <div className="bn-flex bn-space-x-1 bn-items-center bn-justify-normal 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-justify-between bn-w-[134px] bn-h-[40px] bn-text-sm bn-font-semibold bn-text-secondary"
          style={{ borderRadius: getBorderRadius(theme.borderRadius, 8) }}
        >
          <label htmlFor="bungee-custom-slippage-input">Custom</label>
          <input
            className="bn-bg-transparent bn-px-1 bn-border-0 bn-w-[51px] 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"
          />
          <span>%</span>
        </div>
      </div>
      {disclaimer && (
        <div
          className={`bn-mt-3 bn-leading-[19px] bn-font-normal bn-text-sm ${
            disclaimer.warn ? "bn-text-accent-1" : "bn-text-secondary"
          }`}
        >
          {disclaimer.message}
        </div>
      )}
    </div>
  );
};

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

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