import { useEffect, useMemo, useState } from "react"
import { ArrowRightLeftIcon } from "lucide-react"
import {
  CommonInputProps,
  InputBase,
  SIGNED_NUMBER_REGEX,
} from "~~/components/xchange"
import { useGlobalState } from "~~/services/store/store"

const MAX_DECIMALS_USD = 2

function etherValueToDisplayValue(
  usdMode: boolean,
  etherValue: string,
  nativeCurrencyPrice: number
) {
  if (usdMode && nativeCurrencyPrice) {
    const parsedEthValue = parseFloat(etherValue)
    if (Number.isNaN(parsedEthValue)) {
      return etherValue
    } else {
      // We need to round the value rather than use toFixed,
      // since otherwise a user would not be able to modify the decimal value
      return (
        Math.round(
          parsedEthValue * nativeCurrencyPrice * 10 ** MAX_DECIMALS_USD
        ) /
        10 ** MAX_DECIMALS_USD
      ).toString()
    }
  } else {
    return etherValue
  }
}

function displayValueToEtherValue(
  usdMode: boolean,
  displayValue: string,
  nativeCurrencyPrice: number
) {
  if (usdMode && nativeCurrencyPrice) {
    const parsedDisplayValue = parseFloat(displayValue)
    if (Number.isNaN(parsedDisplayValue)) {
      // Invalid number.
      return displayValue
    } else {
      // Compute the ETH value if a valid number.
      return (parsedDisplayValue / nativeCurrencyPrice).toString()
    }
  } else {
    return displayValue
  }
}

/**
 * Input for ETH amount with USD conversion.
 *
 * onChange will always be called with the value in ETH
 */
export const EtherInput = ({
  value,
  name,
  placeholder,
  onChange,
  disabled,
  usdMode,
}: CommonInputProps & { usdMode?: boolean }) => {
  const [transitoryDisplayValue, setTransitoryDisplayValue] = useState<string>()
  const nativeCurrencyPrice = useGlobalState(state => state.nativeCurrencyPrice)
  const [internalUsdMode, setInternalUSDMode] = useState(
    nativeCurrencyPrice > 0 ? Boolean(usdMode) : false
  )

  useEffect(() => {
    setInternalUSDMode(nativeCurrencyPrice > 0 ? Boolean(usdMode) : false)
  }, [usdMode, nativeCurrencyPrice])

  // The displayValue is derived from the ether value that is controlled outside of the component
  // In usdMode, it is converted to its usd value, in regular mode it is unaltered
  const displayValue = useMemo(() => {
    const newDisplayValue = etherValueToDisplayValue(
      internalUsdMode,
      value,
      nativeCurrencyPrice
    )
    if (
      transitoryDisplayValue &&
      parseFloat(newDisplayValue) === parseFloat(transitoryDisplayValue)
    ) {
      return transitoryDisplayValue
    }
    // Clear any transitory display values that might be set
    setTransitoryDisplayValue(undefined)
    return newDisplayValue
  }, [nativeCurrencyPrice, transitoryDisplayValue, internalUsdMode, value])

  const handleChangeNumber = (newValue: string) => {
    if (newValue && !SIGNED_NUMBER_REGEX.test(newValue)) {
      return
    }

    // Following condition is a fix to prevent usdMode from experiencing different display values
    // than what the user entered. This can happen due to floating point rounding errors that are introduced in the back and forth conversion
    if (internalUsdMode) {
      const decimals = newValue.split(".")[1]
      if (decimals && decimals.length > MAX_DECIMALS_USD) {
        return
      }
    }

    // Since the display value is a derived state (calculated from the ether value), usdMode would not allow introducing a decimal point.
    // This condition handles a transitory state for a display value with a trailing decimal sign
    if (newValue.endsWith(".") || newValue.endsWith(".0")) {
      setTransitoryDisplayValue(newValue)
    } else {
      setTransitoryDisplayValue(undefined)
    }

    const newEthValue = displayValueToEtherValue(
      internalUsdMode,
      newValue,
      nativeCurrencyPrice
    )
    onChange(newEthValue)
  }

  const toggleMode = () => {
    if (nativeCurrencyPrice > 0) {
      setInternalUSDMode(!internalUsdMode)
    }
  }

  return (
    <InputBase
      name={name}
      value={displayValue}
      placeholder={placeholder}
      onChange={handleChangeNumber}
      disabled={disabled}
      prefix={
        <span className="text-accent -mr-2 self-center pl-4">
          {internalUsdMode ? "$" : "Ξ"}
        </span>
      }
      suffix={
        <div
          className={`${
            nativeCurrencyPrice > 0
              ? ""
              : "tooltip tooltip-secondary before:left-auto before:right-[-10px] before:transform-none before:content-[attr(data-tip)]"
          }`}
          data-tip="Unable to fetch price"
        >
          <button
            className="btn btn-primary h-[2.2rem] min-h-[2.2rem]"
            onClick={toggleMode}
            disabled={!internalUsdMode && !nativeCurrencyPrice}
          >
            <ArrowRightLeftIcon
              className="h-3 w-3 cursor-pointer"
              aria-hidden="true"
            />
          </button>
        </div>
      }
    />
  )
}
