import { useCallback, useEffect, useRef } from "react";

import Decimal from "decimal.js";
import { useTranslation } from "react-i18next";
import starkString from "starkstring";
import { usePostOtcV1PublicOtcConvertPreview } from "../../services";
import { useAssets } from "../useAssets";
import { useCharge } from "../useCharge";
import { useDisplayDecimalDigits } from "../useDisplayDecimalDigits";
import { BuySellContext } from "./context";
import { useBuySellAmountValidation } from "./useBuySellAmountValidation";

import debounce from "lodash-es/debounce";
import { useAuth } from "react-oidc-js";

const REFRESH_TIME = 8000;
const MAX_REFRESH_COUNT = 5;

export const useUpdatePreview = ({
  mode,
  type,
}: {
  mode: "from" | "to";
  type: "buy" | "sell";
}) => {
  const { userData } = useAuth();

  const { setValue, setError, getValues, getFieldState } =
    BuySellContext.useFormContext();
  const { lastChangedField, fromAsset } = BuySellContext.useWatch();
  const lastUpdated = useRef<number | null>(null);
  const refreshCount = useRef(0);
  const { t } = useTranslation();
  const { minDeposit, getValidAmount, maxDeposit } = useCharge({
    currency: fromAsset,
  });
  const { truncFix, truncFixToCurrency } = useDisplayDecimalDigits();
  const { getAssetBySymbol } = useAssets();
  const { maximumFrom, minimumFrom, maximumTo, minimumTo } =
    useBuySellAmountValidation();

  const { mutate: requestPreview } = usePostOtcV1PublicOtcConvertPreview({
    onSuccess: ({
      finalDestinationQuantity,
      finalSourceQuantity,
      sourceCurrencySymbol,
      destinationCurrencySymbol,
    }) => {
      lastUpdated.current = Date.now();
      const availableRemain = sourceCurrencySymbol
        ? getAssetBySymbol(sourceCurrencySymbol)?.availableRemain || 0
        : 0;

      const fromAmount = truncFix(
        finalSourceQuantity,
        sourceCurrencySymbol,
      ).toString();
      const toAmount = truncFix(
        finalDestinationQuantity,
        destinationCurrencySymbol,
      ).toString();
      if (mode === "from") {
        setValue("toAmount", toAmount);
      } else {
        setValue("fromAmount", fromAmount);
      }

      if (type === "buy") {
        if (availableRemain < finalSourceQuantity) {
          const chargeAmount = getValidAmount(
            new Decimal(finalSourceQuantity).minus(availableRemain).toNumber(),
          );
          const hasError = !!getFieldState("fromAmount").error;
          if (hasError) return;
          if (maxDeposit && chargeAmount > maxDeposit) {
            setValue("shouldCharge", false);
            return setError("fromAmount", {
              message: t("insufficientBalanceAndLessThanMaxDeposit"),
            });
          } else if (chargeAmount < minDeposit) {
            setValue("shouldCharge", false);
            return setError("fromAmount", {
              message: t("insufficientBalanceAndLessThanMinDeposit"),
            });
          } else if (
            userData !== null &&
            mode === "from" &&
            Number(fromAmount) <= maximumFrom &&
            Number(fromAmount) > minimumFrom
          ) {
            setValue("shouldCharge", true);
            setValue("chargeAmount", chargeAmount);
            return setError("fromAmount", {
              message: t("insufficientBalance"),
            });
          } else if (
            userData !== null &&
            mode === "to" &&
            starkString(toAmount).toNumber() <= maximumTo &&
            starkString(toAmount).toNumber() >= minimumTo &&
            availableRemain < finalSourceQuantity
          ) {
            setValue("shouldCharge", true);
            setValue("chargeAmount", chargeAmount);
            return setError("toAmount", {
              message: t("insufficientBalance"),
            });
          }
        } else {
          return setValue("shouldCharge", false);
        }
      }

      if (type === "sell") {
        if (
          userData !== null &&
          mode === "to" &&
          starkString(toAmount).toNumber() <= maximumTo &&
          starkString(toAmount).toNumber() >= minimumTo &&
          availableRemain < finalSourceQuantity
        ) {
          return setError("toAmount", {
            message: t("insufficientBalance"),
          });
        }
        if (
          userData !== null &&
          mode === "from" &&
          starkString(fromAmount).toNumber() <= maximumFrom &&
          starkString(fromAmount).toNumber() >= minimumFrom &&
          availableRemain < finalSourceQuantity
        ) {
          return setError("fromAmount", {
            message: t("insufficientBalance"),
          });
        } else {
          return;
        }
      }
    },
  });

  const request = useCallback(() => {
    const {
      isPreviewOpen,
      toAsset,
      fromAsset,
      lastChangedField,
      fromAmount,
      toAmount,
    } = getValues();
    const amount = new Decimal(
      (mode === "from" ? fromAmount : toAmount) || 0,
    ).toNumber();

    if (
      fromAsset &&
      toAsset &&
      lastChangedField === mode &&
      !isPreviewOpen &&
      amount > 0
    ) {
      requestPreview({
        requestBody: {
          validityDurationInSecond: 10,
          sourceQuantity: lastChangedField === "from" ? amount : undefined,
          destinationQunatity: lastChangedField === "to" ? amount : undefined,
          destinationCurrencySymbol: toAsset,
          sourceCurrencySymbol: fromAsset,
        },
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mode]);
  const resetRefreshCount = useCallback(() => (refreshCount.current = 0), []);
  const debounceRequest = debounce(request, 500);

  useEffect(() => {
    const interval = setInterval(() => {
      if (refreshCount.current >= MAX_REFRESH_COUNT) {
        return;
      }
      debounceRequest();
      refreshCount.current = refreshCount.current + 1;
    }, REFRESH_TIME);

    return () => {
      if (interval) clearInterval(interval);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return {
    resetRefreshCount,
    request: debounceRequest,
  };
};
