import { PoweredBy, Swap } from "@src/components";
import { getWagmiConfig, waitForInitialization, xpayRoot } from "@src/config";
import {
  HistoryProvider,
  SwapProvider,
  TransactionProvider,
  TxUIWrapper,
  useHistory,
} from "@src/context";
import { Chain, ModalIntegrationPayload, UnifiedRoute } from "@src/models";
import { FC, MouseEvent, useEffect, useMemo } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { changeHostVariableColor } from "../../../localTheme";
import { setApiUrl } from "@src/services/api";
import { WalletProvider } from "@src/context/WalletProvider";
import { AllWalletsConfig } from "../AllWalletsConfig";

export type TxConfigFormPayload = Omit<
  ModalIntegrationPayload,
  "dstChain" | "dstToken" | "dstDisplayToken" | "srcChain" | "srcToken"
> & {
  dstChainId?: string;
  dstTokenAddr?: string;
  srcChainId?: string;
  srcTokenAddr?: string;
  dstDisplayTokenAddr?: string;
  supportedChains: Chain[];
  integratorFee?: number;
  integratorFeeReceiverAddress?: string;
  highlightedDstTokens?: string[];
};

type TxConfigFormProps = TxConfigFormPayload &
  (
    | {
        onSubmit: (route: UnifiedRoute) => void;
        onClose: () => void;
        overlay: true;
      }
    | {
        onSubmit?: undefined;
        onClose?: undefined;
        overlay: false;
      }
  );

const normalizeTxConfigFormProps = (props: TxConfigFormProps) => {
  return {
    ...props,
    dstTokenAddr: props.dstTokenAddr?.toLowerCase(),
    srcTokenAddr: props.srcTokenAddr?.toLowerCase(),
  };
};

const PendingTransactionsEmitter: FC<{
  onPendingTransactionsChange: ModalIntegrationPayload["onPendingTransactionsChange"];
}> = ({ onPendingTransactionsChange }) => {
  const { history } = useHistory();

  const pendingTransactions = useMemo(
    () => history.filter((entry) => entry.status === "IN_PROGRESS"),
    [history],
  );

  useEffect(() => {
    if (onPendingTransactionsChange) {
      onPendingTransactionsChange(pendingTransactions);
    } else if (window.xPayOnPendingTransactionsChange) {
      window.xPayOnPendingTransactionsChange(pendingTransactions);
    }
  }, [onPendingTransactionsChange, pendingTransactions]);

  return <></>;
};

export const TxConfigForm = (props: TxConfigFormProps) => {
  const normalizedProps = useMemo(
    () => normalizeTxConfigFormProps(props),
    [props],
  );

  const {
    integratorId,
    dstChainId,
    dstTokenAddr,
    srcChainId,
    srcTokenAddr,
    customContractCalls,
    desc,
    dstDisplayTokenAddr,
    supportedChains,
    onSubmit,
    onClose,
    overlay,
    lightTheme,
    defaultWalletPicker,
    styles,
    onPendingTransactionsChange,
    dstTokenLocked,
    dstChainLocked,
    srcTokenLocked,
    srcChainLocked,
    onDstTokenChange,
    onDstChainChange,
    onSrcTokenChange,
    onSrcChainChange,
    bridge,
    onConnectWallet,
    override,
    integratorFee,
    integratorFeeReceiverAddress,
    highlightedDstTokens,
  } = normalizedProps;

  const onBackdropClick = (e: MouseEvent) => {
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();

    if (e.target === e.currentTarget && overlay) {
      onClose();
    }
  };

  const wagmiConfig = useMemo(
    () => getWagmiConfig(supportedChains),
    [supportedChains],
  );

  useEffect(() => {
    changeHostVariableColor(lightTheme, styles);
  }, [lightTheme, styles]);

  useEffect(() => {
    if (override?.apiUrl) {
      setApiUrl(override?.apiUrl);
    }
  }, [override?.apiUrl]);

  return (
    <ErrorBoundary
      FallbackComponent={() => (
        <div>Something went wrong, please contact XSwap.</div>
      )}
    >
      <AllWalletsConfig wagmiConfig={wagmiConfig}>
        <WalletProvider>
          <HistoryProvider>
            <PendingTransactionsEmitter
              onPendingTransactionsChange={onPendingTransactionsChange}
            />
            <SwapProvider
              integrationConfig={{
                integratorId,
                dstChainId,
                dstTokenAddr,
                srcChainId,
                srcTokenAddr,
                customContractCalls,
                desc,
                dstDisplayTokenAddr,
                defaultWalletPicker,
                dstTokenLocked,
                dstChainLocked,
                srcTokenLocked,
                srcChainLocked,
                onDstTokenChange,
                onDstChainChange,
                onSrcTokenChange,
                onSrcChainChange,
                bridge,
                onConnectWallet,
                integratorFee,
                integratorFeeReceiverAddress,
                highlightedDstTokens,
              }}
              supportedChains={supportedChains}
              overlay={overlay}
            >
              <TransactionProvider>
                <TxUIWrapper>
                  {overlay ? (
                    <div
                      className="top-0 left-0 right-0 bottom-0 bg-[rgba(0,0,0,0.8)] fixed flex flex-col items-center justify-center z-10"
                      onMouseDown={onBackdropClick}
                    >
                      <div className="relative bg-t_bg_primary rounded-2xl overflow-auto text-t_text_primary border-[1px] border-solid border-[rgba(255,255,255,0.1)] w-x_modal min-w-x_modal max-w-x_modal min-h-[532px]">
                        <Swap onSubmit={onSubmit} onClose={onClose} />
                        <PoweredBy />
                      </div>
                    </div>
                  ) : (
                    <div className="bg-t_bg_primary rounded-2xl overflow-auto text-t_text_primary border-[1px] border-solid border-[rgba(255,255,255,0.1)] min-h-[532px] w-x_modal min-w-x_modal max-w-x_modal flex flex-col">
                      <Swap onSubmit={() => {}} onClose={() => {}} />
                      <PoweredBy />
                    </div>
                  )}
                </TxUIWrapper>
              </TransactionProvider>
            </SwapProvider>
          </HistoryProvider>
        </WalletProvider>
      </AllWalletsConfig>
    </ErrorBoundary>
  );
};

export const openTxConfigForm = async ({
  integratorId,
  dstChainId,
  dstTokenAddr,
  srcChainId,
  srcTokenAddr,
  customContractCalls,
  desc,
  dstDisplayTokenAddr,
  supportedChains,
  lightTheme,
  defaultWalletPicker,
  styles,
  onPendingTransactionsChange,
  dstTokenLocked,
  dstChainLocked,
  srcTokenLocked,
  srcChainLocked,
  override,
}: TxConfigFormPayload): Promise<UnifiedRoute> => {
  try {
    if (xpayRoot === null) {
      throw new Error("XPay was incorrectly initialized");
    }

    await waitForInitialization();

    return await new Promise(() => {
      if (xpayRoot === null) {
        throw new Error("XPay was incorrectly initialized");
      }

      xpayRoot.render(
        <TxConfigForm
          integratorId={integratorId}
          dstChainId={dstChainId}
          dstTokenAddr={dstTokenAddr}
          srcChainId={srcChainId}
          srcTokenAddr={srcTokenAddr}
          customContractCalls={customContractCalls}
          desc={desc}
          dstDisplayTokenAddr={dstDisplayTokenAddr}
          supportedChains={supportedChains}
          lightTheme={lightTheme}
          styles={styles}
          onSubmit={() => {
            if (xpayRoot) {
              xpayRoot.render("");
            }
          }}
          onClose={() => {
            if (xpayRoot) {
              xpayRoot.render("");
            }
          }}
          overlay={true}
          defaultWalletPicker={defaultWalletPicker}
          onPendingTransactionsChange={onPendingTransactionsChange}
          dstTokenLocked={dstTokenLocked}
          dstChainLocked={dstChainLocked}
          srcTokenLocked={srcTokenLocked}
          srcChainLocked={srcChainLocked}
          override={override}
        />,
      );
    });
  } catch (err) {
    throw new Error(`XSwap component error, ${err}`);
  }
};
