import { ReactNode, useEffect, useMemo } from "react";
import { ControllerRenderProps } from "react-hook-form";

import Decimal from "decimal.js";

import { Asset, useAssets } from "../useAssets";
import { useDisplayDecimalDigits } from "../useDisplayDecimalDigits";
import { useMarkets } from "../useMarkets";
import { ConvertContext, ConvertFormProps } from "./context";
import { useConvertAmountValidation } from "./useConvertAmountValidation";
import { useConvertConfigMarket } from "./useConvertConfigs";
import { useUpdatePreview } from "./useUpdatePreview";

type ConvertToRenderProps = {
  render: (state: {
    field: ControllerRenderProps<ConvertFormProps, "toAmount"> & {
      onSelect: (asset?: Asset) => void;
      assets: Asset[];
      asset?: Asset;
      fromAsset?: string;
      minQuantity?: number;
      maxQuantity?: number;
      hasError?: boolean;
      getMaxSize?: (value: string | number | Decimal) => string;
      onFocus?: () => void;
      stepSize?: number;
    };
  }) => React.ReactElement;
  type: "market" | "limit";
  renderErrorComponent?: (message?: string) => ReactNode;
};

export const ConvertToController = ({
  render,
  renderErrorComponent,
  type,
}: ConvertToRenderProps) => {
  const { toAsset, toAmount, fromAsset, lastChangedField, price, fromAmount } =
    ConvertContext.useWatch();

  const { resetRefreshCount } = useUpdatePreview({ mode: "to", type: type });

  const { marketsSymbols } = useMarkets();

  const { setValue, clearErrors, register } = ConvertContext.useFormContext();
  const { truncFix } = useDisplayDecimalDigits();

  const { errors } = ConvertContext.useFormState();

  const { filterLotSize, filterPrice, getMaxSize, selectedCurrency } =
    useConvertConfigMarket({ mode: "to", type });

  register("lastChangedField");

  const { checkAmount, minimumTo, maximumTo } = useConvertAmountValidation();

  const { allAssets } = useAssets();

  const filteredAssets = useMemo(() => {
    return (
      allAssets?.filter(
        ({ canTrade, symbol }) => canTrade && symbol !== fromAsset,
      ) || []
    );
  }, [allAssets, fromAsset]);
  useEffect(() => {
    if (selectedCurrency) return;

    const _asset = allAssets?.find(
      (item) => item.symbol?.toLowerCase() === toAsset?.toLowerCase(),
    );
    setValue("toAsset", _asset?.name);

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [marketsSymbols, selectedCurrency, fromAsset]);

  const canThrowError =
    errors["toAmount"] && toAmount !== null && toAmount !== undefined;

  useEffect(() => {
    !toAsset &&
      filteredAssets.length &&
      setValue("toAsset", filteredAssets?.[0].symbol);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filteredAssets, toAsset]);

  return (
    <>
      <ConvertContext.Controller
        name="toAmount"
        rules={{
          validate: {
            amount: (value) => {
              return checkAmount(value, "to");
            },
          },
        }}
        render={({ field: { onChange, value, ...rest } }) => {
          const multiple = new Decimal(Number(fromAmount)).mul(
            new Decimal(Number(price)),
          );
          const result =
            truncFix(Number(multiple), toAsset).toString() === "0"
              ? undefined
              : truncFix(Number(multiple), toAsset).toString();
          return render({
            field: {
              value: type === "limit" ? result : value,
              minQuantity: type === "limit" ? undefined : minimumTo,
              maxQuantity: type === "limit" ? undefined : maximumTo,
              assets: filteredAssets || [],
              asset: selectedCurrency,
              getMaxSize: getMaxSize,
              fromAsset,
              hasError: value ? !!errors.toAmount : false,
              onSelect: async (asset?: Asset) => {
                if (selectedCurrency?.symbol !== asset?.symbol) {
                  onChange("");
                  setValue("fromAmount", "");
                  type === "limit" && setValue("price", "");
                  const symbol = await new Promise<string | undefined>(
                    (res) => {
                      res(asset?.symbol);
                    },
                  );
                  setValue("toAsset", symbol);
                }
              },
              onFocus() {
                lastChangedField !== "to" && setValue("lastChangedField", "to");
              },
              onChange: (value) => {
                if (value === "") {
                  setValue("fromAmount", "");
                }

                resetRefreshCount();
                setValue("lastChangedField", "to");
                clearErrors("fromAmount");
                onChange(value);
              },
              stepSize:
                selectedCurrency?.currencyType === "Fiat"
                  ? filterPrice?.tickSize
                  : filterLotSize?.stepSize,
              ...rest,
            },
          });
        }}
      />
      {renderErrorComponent &&
        renderErrorComponent?.(
          canThrowError ? errors["toAmount"]?.message : "",
        )}
    </>
  );
};
