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

import Decimal from "decimal.js";

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

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

export const ConvertFromController = ({
  render,
  renderErrorComponent,
  type,
}: ConvertFromRenderProps) => {
  const { toAsset, fromAmount, lastChangedField } = ConvertContext.useWatch();
  const { resetRefreshCount } = useUpdatePreview({
    mode: "from",
    type: type,
  });

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

  const { minimumFrom, maximumFrom, checkAmount } =
    useConvertAmountValidation();

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

  const { errors } = ConvertContext.useFormState();

  register("lastChangedField");

  const { allAssets } = useAssets();
  const canThrowError =
    errors["fromAmount"] && fromAmount !== null && fromAmount !== undefined;

  const filteredAssets = useMemo(() => {
    return (
      allAssets?.filter(
        ({ canTrade, symbol }) => canTrade && symbol !== toAsset,
      ) || []
    );
  }, [allAssets, toAsset]);
  return (
    <>
      <ConvertContext.Controller
        name="fromAmount"
        rules={{
          validate: {
            amount: (value) => {
              return checkAmount(value, "from");
            },
          },
        }}
        render={({ field: { onChange, value, ...rest } }) => {
          return render({
            field: {
              value: value,
              minQuantity: minimumFrom,
              maxQuantity: maximumFrom,
              assets: filteredAssets,
              asset: selectedCurrency,
              getMaxSize: getMaxSize,
              hasError: value ? !!errors.fromAmount : false,
              onSelect: async (asset?: Asset) => {
                if (selectedCurrency?.symbol !== asset?.symbol) {
                  onChange("");
                  setValue("toAmount", "");
                  if (type === "limit") {
                    setValue("price", "");
                  }
                  const symbol = await new Promise<string | undefined>(
                    (res) => {
                      res(asset?.symbol);
                    },
                  );
                  setValue("fromAsset", symbol);
                  clearErrors();
                }
              },
              onFocus() {
                lastChangedField !== "from" &&
                  setValue("lastChangedField", "from");
              },
              onChange: (value) => {
                if (value === "") {
                  setValue("toAmount", "");
                }
                resetRefreshCount();
                setValue("lastChangedField", "from");
                clearErrors();
                onChange(value);
              },
              stepSize:
                selectedCurrency?.currencyType === "Fiat"
                  ? filterPrice?.tickSize
                  : filterLotSize?.stepSize,
              ...rest,
            },
          });
        }}
      />
      {renderErrorComponent &&
        renderErrorComponent?.(
          canThrowError ? errors["fromAmount"]?.message : "",
        )}
    </>
  );
};
