import {
  Done,
  ErrorRounded,
  InfoRounded,
  WarningRounded,
} from '@mui/icons-material';
import { Box, Button, Skeleton, Typography } from '@mui/material';
import { useQueryClient } from '@tanstack/react-query';
import { useCallback, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { BottomSheet } from '../../components/BottomSheet/BottomSheet.js';
import type { BottomSheetBase } from '../../components/BottomSheet/types.js';
import { Token } from '../../components/Token/Token.js';
import { useAccount } from '../../hooks/useAccount.js';
import { useAvailableChains } from '../../hooks/useAvailableChains.js';
import { useNavigateBack } from '../../hooks/useNavigateBack.js';
import { getProcessMessage } from '../../hooks/useProcessMessage.js';
import { useSetContentHeight } from '../../hooks/useSetContentHeight.js';
import { useTokenBalance } from '../../hooks/useTokenBalance.js';
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
import { useFieldActions } from '../../stores/form/useFieldActions.js';
import {
  RouteExecutionStatus,
  type RouteExecution,
} from '../../stores/routes/types.js';
import { getSourceTxHash } from '../../stores/routes/utils.js';
import { hasEnumFlag } from '../../utils/enum.js';
import { formatTokenAmount } from '../../utils/format.js';
import { navigationRoutes } from '../../utils/navigationRoutes.js';
import { shortenAddress } from '../../utils/wallet.js';
import { safeBigInt } from '../../utils/utils.js';
import {
  CenterContainer,
  IconCircle,
  MessageSkeletonContainer,
} from './StatusBottomSheet.style.js';

interface StatusBottomSheetContentProps extends RouteExecution {
  onClose(): void;
}

const MessageSkeleton = () => (
  <MessageSkeletonContainer>
    <Skeleton height={24} variant="text" width="92%" />
    <Skeleton height={24} variant="text" width="56%" />
  </MessageSkeletonContainer>
);

export const StatusBottomSheet: React.FC<RouteExecution> = ({
  status,
  route,
}) => {
  const ref = useRef<BottomSheetBase>(null);

  const onClose = useCallback(() => {
    ref.current?.close();
  }, []);

  useEffect(() => {
    const hasSuccessFlag = hasEnumFlag(status, RouteExecutionStatus.Done);
    const hasFailedFlag = hasEnumFlag(status, RouteExecutionStatus.Failed);
    if ((hasSuccessFlag || hasFailedFlag) && !ref.current?.isOpen()) {
      ref.current?.open();
    }
  }, [status]);

  return (
    <BottomSheet ref={ref}>
      <StatusBottomSheetContent
        status={status}
        route={route}
        onClose={onClose}
      />
    </BottomSheet>
  );
};

export const StatusBottomSheetContent: React.FC<
  StatusBottomSheetContentProps
> = ({ status, route, onClose }) => {
  const { t } = useTranslation();
  const { navigateBack, navigate } = useNavigateBack();
  const queryClient = useQueryClient();
  const { setFieldValue } = useFieldActions();
  const {
    subvariant,
    subvariantOptions,
    contractSecondaryComponent,
    contractCompactComponent,
    feeConfig,
    handleSwapLog,
  } = useWidgetConfig();
  const { getChainById } = useAvailableChains();
  const { account } = useAccount();

  const ref = useRef<HTMLElement>();
  useSetContentHeight(ref);

  const lastStep = route?.steps?.at(-1);
  const toToken = {
    ...(lastStep?.execution?.toToken ?? route?.toToken),
    amount: safeBigInt(
      lastStep?.execution?.toAmount ??
        lastStep?.estimate?.toAmount ??
        route?.toAmount,
    ),
  };

  const toChain = getChainById(toToken.chainId);

  const { token, refetch, refetchNewBalance, refetchAllBalances } =
    useTokenBalance(route.toAddress, toToken, toChain);

  const invalidateQueries = () => {
    Promise.resolve().then(() => {
      refetchAllBalances();
      setFieldValue('fromAmount', '');
      setFieldValue('toAmount', '');
      queryClient.invalidateQueries({ queryKey: ['transaction-history'] });
      queryClient.invalidateQueries({ queryKey: ['token-balances'] });
    });
  };

  const handleDone = () => {
    invalidateQueries();
    Promise.resolve().then(() => {
      navigateBack();
    });
  };

  const handlePartialDone = () => {
    invalidateQueries();
    if (
      toToken.chainId !== route?.toToken?.chainId &&
      toToken.address !== route?.toToken?.address
    ) {
      Promise.resolve().then(() => {
        setFieldValue(
          'fromAmount',
          formatTokenAmount(toToken.amount, toToken.decimals),
          { isTouched: true },
        );
        setFieldValue('fromChain', toToken.chainId, { isTouched: true });
        setFieldValue('fromToken', toToken.address, { isTouched: true });
        setFieldValue('toChain', route?.toToken?.chainId, {
          isTouched: true,
        });
        setFieldValue('toToken', route?.toToken?.address, {
          isTouched: true,
        });
      });
    }
    navigateBack();
  };

  const handleClose = () => {
    invalidateQueries();
    Promise.resolve().then(() => {
      onClose();
    });
  };

  const handleSeeDetails = () => {
    handleClose();
    const transactionHash = getSourceTxHash(route);
    Promise.resolve().then(() => {
      navigate(navigationRoutes.transactionDetails, {
        state: {
          routeId: route.id,
          transactionHash,
        },
        replace: true,
      });
    });
  };

  const transactionType =
    route.fromChainId === route.toChainId ? 'swap' : 'bridge';

  let title: string | undefined;
  let primaryMessage;
  let secondaryMessage;
  let handlePrimaryButton = handleDone;
  switch (status) {
    case RouteExecutionStatus.Done: {
      title =
        subvariant === 'custom'
          ? t(
              `success.title.${subvariantOptions?.custom ?? 'checkout'}Successful`,
            )
          : t(`success.title.${transactionType}Successful`);
      if (token) {
        primaryMessage = t('success.message.exchangeSuccessful', {
          amount: formatTokenAmount(token.amount, token.decimals),
          tokenSymbol: token.symbol,
          chainName: toChain?.name,
          walletAddress: shortenAddress(route.toAddress),
        });
      }
      handlePrimaryButton = handleDone;
      break;
    }
    case RouteExecutionStatus.Done | RouteExecutionStatus.Partial: {
      title = t(`success.title.${transactionType}PartiallySuccessful`);
      primaryMessage = t('success.message.exchangePartiallySuccessful', {
        tool: route.steps.at(-1)?.toolDetails.name,
        tokenSymbol: route.steps.at(-1)?.action.toToken.symbol,
      });
      if (token) {
        secondaryMessage = t('success.message.exchangeSuccessful', {
          amount: formatTokenAmount(token.amount, token.decimals),
          tokenSymbol: token.symbol,
          chainName: toChain?.name,
          walletAddress: shortenAddress(route.toAddress),
        });
      }
      handlePrimaryButton = handlePartialDone;
      break;
    }
    case RouteExecutionStatus.Done | RouteExecutionStatus.Refunded: {
      title = t('success.title.refundIssued');
      primaryMessage = t('success.message.exchangePartiallySuccessful', {
        tool: route.steps.at(-1)?.toolDetails.name,
        tokenSymbol: route.steps.at(-1)?.action.toToken.symbol,
      });
      if (token) {
        secondaryMessage = t('success.message.exchangeSuccessful', {
          amount: formatTokenAmount(token.amount, token.decimals),
          tokenSymbol: token.symbol,
          chainName: toChain?.name,
          walletAddress: shortenAddress(route.toAddress),
        });
      }
      break;
    }
    case RouteExecutionStatus.Failed: {
      const step = route.steps.find(
        (step) => step.execution?.status === 'FAILED',
      );
      const process = step?.execution?.process.find(
        (process) => process.status === 'FAILED',
      );
      if (!step || !process) {
        break;
      }
      const processMessage = getProcessMessage(t, getChainById, step, process);
      title = processMessage.title;
      primaryMessage = processMessage.message;
      handlePrimaryButton = handleClose;
      break;
    }
    default:
      break;
  }

  const handleTrackingLogFail = async () => {
    try {
      const step = route.steps.find(
        (step) => step.execution?.status === 'FAILED',
      );
      const process = step?.execution?.process.find(
        (process) => process.status === 'FAILED',
      );
      if (!step || !process) {
        return;
      }

      const agg = route?.steps?.[0]?.integrator?.toLowerCase();

      const processMessage = getProcessMessage(t, getChainById, step, process);
      const transactionHash = getSourceTxHash(route);

      typeof handleSwapLog === 'function' &&
        (await handleSwapLog({
          title: processMessage.title,
          message: processMessage.message,
          address: account?.address,
          chainName: getChainById(step.action.fromChainId)?.name ?? '',
          transactionHash,
          amount: formatTokenAmount(
            BigInt(step?.action?.fromAmount || 0),
            step.action.fromToken.decimals,
          ),
          symbol: step.action.fromToken.symbol,
          agg,
        }));
    } catch (e) {
      console.error(e);
    }
  };

  useEffect(() => {
    if (RouteExecutionStatus.Failed) {
      handleTrackingLogFail();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [status]);

  useEffect(() => {
    const hasSuccessFlag = hasEnumFlag(status, RouteExecutionStatus.Done);
    if (hasSuccessFlag) {
      refetchNewBalance();
      refetch();
    }
  }, [refetch, refetchNewBalance, status]);

  const showContractComponent =
    subvariant === 'custom' &&
    hasEnumFlag(status, RouteExecutionStatus.Done) &&
    (contractCompactComponent || contractSecondaryComponent);

  const VcComponent =
    status === RouteExecutionStatus.Done ? feeConfig?._vcComponent : undefined;

  return (
    <Box p={3} ref={ref}>
      {!showContractComponent ? (
        <CenterContainer>
          <IconCircle status={status} mb={1}>
            {status === RouteExecutionStatus.Idle ? (
              <InfoRounded color="primary" />
            ) : null}
            {status === RouteExecutionStatus.Done ? (
              <Done color="success" />
            ) : null}
            {hasEnumFlag(status, RouteExecutionStatus.Partial) ||
            hasEnumFlag(status, RouteExecutionStatus.Refunded) ? (
              <WarningRounded color="warning" />
            ) : null}
            {hasEnumFlag(status, RouteExecutionStatus.Failed) ? (
              <ErrorRounded color="error" />
            ) : null}
          </IconCircle>
        </CenterContainer>
      ) : null}
      <CenterContainer>
        <Typography py={1} fontSize={18} fontWeight={700}>
          {title}
        </Typography>
      </CenterContainer>
      {showContractComponent ? (
        contractCompactComponent || contractSecondaryComponent
      ) : (
        <CenterContainer>
          {hasEnumFlag(status, RouteExecutionStatus.Done) ? (
            <Token token={toToken} py={1} disableDescription isFromRoute />
          ) : null}
        </CenterContainer>
      )}
      {!showContractComponent ? (
        primaryMessage ? (
          <Typography py={1}>{primaryMessage}</Typography>
        ) : (
          <MessageSkeleton />
        )
      ) : null}
      {secondaryMessage ? (
        <Typography py={1}>{secondaryMessage}</Typography>
      ) : null}
      {VcComponent ? <VcComponent route={route} /> : null}
      <Box sx={{ display: 'flex', marginTop: 2, gap: 1.5 }}>
        {hasEnumFlag(status, RouteExecutionStatus.Done) ? (
          <Button variant="text" onClick={handleSeeDetails} fullWidth>
            {t('button.seeDetails')}
          </Button>
        ) : null}
        <Button
          variant="contained"
          fullWidth
          onClick={() => {
            if (hasEnumFlag(status, RouteExecutionStatus.Done)) {
              invalidateQueries();
              navigate(navigationRoutes.home);
            } else {
              handlePrimaryButton();
            }
          }}
        >
          {status === RouteExecutionStatus.Idle ? t('button.ok') : null}
          {hasEnumFlag(status, RouteExecutionStatus.Done)
            ? t('button.done')
            : null}
          {status === RouteExecutionStatus.Failed
            ? t('button.seeDetails')
            : null}
        </Button>
      </Box>
    </Box>
  );
};
