import { useEffect, useMemo, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";

import { useQuote } from "@hooks/apis/common/quote/useQuote";
import { useAccounts } from "@hooks/useAccounts";
import { useRegisterEventHandlers } from "@hooks/useRegisterEventHandlers";
import { useSetInitialValues } from "@hooks/useSetInitialValues";
import { ScreenTab, setScreenTab } from "../store/globalSlice";
import type { ReduxStateType } from "../store/types";
import { useHistoryCleanup } from "./History/useHistoryCleanup";
import { Home } from "./Home";
import { InFlight } from "./InFlight";
import { BgGrid } from "./InFlight/layout/BgGrid";
import { RecipientAddressModal } from "./RecipientAddressModal";
import { Review } from "./Review";
import { ActionStatusProvider } from "./Review/Auto/ActionStatusProvider";
import { SettingsModal } from "./Settings";
import { ToastContainer } from "./Toast/ToastContainer";
import { SwapWrap } from "./common/SwapWrap";

import { fetchTokens } from "@hooks/apis/common/useTokens";
import { useConfig } from "@hooks/useConfig";
import { setReceiverAddress, setSenderAddress } from "@store/quoteParamSlice";
import { isSolana } from "@utils/isSolana";
import { onConnectWallet } from "../analytics/events";

export const Swap = () => {
  useQuote();
  useSetInitialValues(); // to set values from the config
  useRegisterEventHandlers();
  useHistoryCleanup();

  const { apiKey, baseUrl } = useConfig();

  const { accounts } = useAccounts();
  const dispatch = useDispatch();

  const screenTab = useSelector(
    (state: ReduxStateType) => state.global.screenTab
  );

  const fromChainId = useSelector(
    (state: ReduxStateType) => state.quoteParams.fromChainId
  );

  const toChainId = useSelector(
    (state: ReduxStateType) => state.quoteParams.toChainId
  );

  // We only need to trigger the home screen on account change & disconnection
  const accountConnectionArray = useMemo(() => {
    return accounts.map((account) => ({
      chainType: account.chainType,
      address: account.address,
      isConnected: account.isConnected,
    }));
  }, [accounts]);

  const getAccountKey = (account: (typeof accountConnectionArray)[number]) =>
    `${account.chainType}:${account.address}:${account.isConnected}`;

  const prevAccountKeys = useRef(new Set());

  const getWalletName = (address: string | undefined) => {
    if (!address) return undefined;
    return accounts.find((account) => account.address === address)?.connector
      ?.name;
  };

  const emitWalletConnectEvent = async (
    account: (typeof accountConnectionArray)[number]
  ) => {
    if (!account.address) return;
    const tokenRecords = await fetchTokens({
      address: account.address,
      baseUrl,
      apiKey,
    });
    const tokens = Object.values(tokenRecords.result)
      .flat()
      .filter((token) => Boolean(token.balanceInUsd));

    onConnectWallet({
      wallet_type: account.chainType,
      wallet_name: getWalletName(account.address) || "",
      total_balance: tokens.reduce(
        (acc, token) => acc + (token.balanceInUsd || 0),
        0
      ),
    });
  };

  // biome-ignore lint/correctness/useExhaustiveDependencies: tracking `accountConnectionArray` instead of individual variables
  useEffect(() => {
    const currentKeys = new Set(accountConnectionArray.map(getAccountKey));
    const hasChanged =
      currentKeys.size !== prevAccountKeys.current.size ||
      [...currentKeys].some((key) => !prevAccountKeys.current.has(key));

    if (hasChanged && dispatch) {
      dispatch(setScreenTab(ScreenTab.HOME));

      // Emit onConnectWallet event for newly connected accounts
      const previousKeys = prevAccountKeys.current;
      const newlyConnected = accountConnectionArray.filter((account) => {
        const key = getAccountKey(account);
        return account.isConnected && !previousKeys.has(key);
      });

      for (const account of newlyConnected) {
        emitWalletConnectEvent(account);
      }

      prevAccountKeys.current = currentKeys;
    }
  }, [accountConnectionArray, dispatch]);

  useEffect(() => {
    if (!fromChainId) return;
    let sender: string | undefined;
    if (isSolana(fromChainId)) {
      sender = accounts.find((account) => account.chainType === "SVM")?.address;
    } else {
      sender = accounts.find((account) => account.chainType === "EVM")?.address;
    }
    dispatch(setSenderAddress(sender || null));
  }, [accounts, dispatch, fromChainId]);

  useEffect(() => {
    if (!toChainId) return;
    let receiver: string | undefined;
    if (isSolana(toChainId)) {
      receiver = accounts.find(
        (account) => account.chainType === "SVM"
      )?.address;
    } else {
      receiver = accounts.find(
        (account) => account.chainType === "EVM"
      )?.address;
    }

    dispatch(setReceiverAddress(receiver || null));
  }, [accounts, dispatch, toChainId]);

  return (
    <SwapWrap>
      {screenTab === ScreenTab.HOME && <Home />}
      {screenTab === ScreenTab.REVIEW && (
        <ActionStatusProvider>
          <Review />
        </ActionStatusProvider>
      )}
      {screenTab === ScreenTab.INFLIGHT && (
        <div>
          <BgGrid />
          <InFlight />
        </div>
      )}

      <RecipientAddressModal />
      <SettingsModal />
      <ToastContainer />
    </SwapWrap>
  );
};
