import { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import { useTransactionReceipt } from "wagmi";

import { Button } from "@components/common/Button";
import type { AutoOrderData } from "@hooks/apis/auto/useAutoHistory";
import type {
	FormattedAutoQuote,
	OrderFlowType,
	TxData,
} from "@hooks/apis/common/quote/types";
import { useAccounts } from "@hooks/useAccounts";
import { useChainSwitcher } from "@hooks/useChainSwitcher";
import { useConfig as useBungeeConfig } from "@hooks/useConfig";
import { USER_OPERATIONS, useLogError } from "@hooks/useLogError";
import { useWalletCapabilities } from "@hooks/useWalletCapabilities";
import { useToast } from "@providers/ToastProvider";
import { ScreenTab, setScreenTab } from "@store/globalSlice";
import { setInflightData } from "@store/inflightSlice";
import { setIsFetchingDisabled } from "@store/quoteParamSlice";
import { NATIVE_TOKEN_ADDRESS } from "@utils/constants";
import { getCommonErrors } from "@utils/getCommonErrors";
import { isWrapUnwrapCase } from "@utils/isWrapUnwrapCase";
import { useIsApprovalRequired } from "../Approval/CheckApproval";
import { useBatchingTransaction } from "../Manual/useBatchingTransaction";
import { useRegularTransaction } from "../Manual/useRegularTransaction";
import type { QueryStatus } from "./SignOrder";

export const SendNativeTransaction = ({
	quoteData,
}: { quoteData: FormattedAutoQuote }) => {
	const [status, setStatus] = useState<QueryStatus>("idle");

	const { showToast } = useToast();
	const dispatch = useDispatch();
	const { switchToChain } = useChainSwitcher();
	const { accounts } = useAccounts();
	const bungeeConfig = useBungeeConfig();
	const isApprovalRequired = useIsApprovalRequired();
	const { logError } = useLogError();

	const { txData, output, primaryInput, primaryOutput, approvalData } = quoteData;

	const ethereumAccount = accounts.find((account) => account.chainType === "EVM");
	const sourceChainId = useMemo(
		() => primaryInput?.token?.chainId,
		[primaryInput],
	);

	const { supportsAtomicBatching } = useWalletCapabilities(sourceChainId);

	const {
		triggerAsync: triggerRegular,
		isSuccess: regularSuccess,
		error: regularError,
		txData: regularTxData, // txhash
	} = useRegularTransaction({
		txData: txData || undefined,
	});

	const {
		triggerAsync: triggerBatching,
		isSuccess: batchingSuccess,
		error: batchingError,
		txData: batchingTxData, // capabilities & id
	} = useBatchingTransaction({
		txData: txData as TxData,
		approvalData: approvalData,
		chainId: primaryInput?.token?.chainId,
		isApprovalRequired,
	});

	const {
		data: txResolved,
		error: txError,
		isSuccess: txSuccess,
		isPending: isTxStatusPending,
	} = useTransactionReceipt({
		hash: regularTxData,
		query: {
			enabled: Boolean(regularTxData) && !!isWrapUnwrapCase(quoteData),
		},
	});

	const isNativeToken = useMemo(
		() => primaryInput?.token?.address?.toLowerCase() === NATIVE_TOKEN_ADDRESS,
		[primaryInput],
	);

	const enableQuoteRefresh = useCallback(() => {
		dispatch(setIsFetchingDisabled(false));
	}, [dispatch]);

	function getButtonLabel() {
		if (regularTxData && isTxStatusPending) return "Waiting for confirmation";
		if (status === "awaiting_wallet_confirmation") return "Confirm in wallet";
		if (status === "loading") return "Swapping";

		if (isWrapUnwrapCase(quoteData)) {
			if (isNativeToken) return "Wrap";
			return "Unwrap";
		}

		return "Swap";
	}

	function loadingState() {
		return status === "awaiting_wallet_confirmation" || status === "loading";
	}

	const initiateTransaction = async () => {
		let txHash = "";
		let type: OrderFlowType = "regular";

		try {
			dispatch(setIsFetchingDisabled(true));
			const chainSwitched = await switchToChain(
				sourceChainId,
				ethereumAccount?.chainId,
			);
			if (!chainSwitched) return;

			setStatus("awaiting_wallet_confirmation");
			if (supportsAtomicBatching) {
				const tx = await triggerBatching();
				txHash = tx.id || "";
				type = "bundled";
			} else {
				const tx = await triggerRegular();
				txHash = tx || "";
				type = "regular";
			}
		} catch (err) {
			setStatus("error");

			const _errTitle = "Error while initiating transaction";
			const _errMsg = "Please try again";

			showToast(_errTitle, _errMsg, "error");
			console.error(_errTitle, err);
			logError(err, {
				operation: USER_OPERATIONS.TRANSACTION,
				action: "send-transaction",
			});
		} finally {
			// Refetch balances
			bungeeConfig?.eventHandlers?.onSwapInitiated?.({
				chainId: sourceChainId,
				hash: txHash,
				type,
			});
		}
	};

	const modifiedOutput = output.map((item) => ({
		...item,
		minAmountOut: quoteData.minOutput?.amount ?? "",
	}));

	const formattedData: AutoOrderData = {
		...quoteData,
		status: "PENDING",
		statusCode: 0,
		srcTimestamp: 0, // just a placeholder
		orderTimestamp: 0, // placeholder
		swapTime: 0, // placeholder
		output: modifiedOutput,
		primaryOutput: modifiedOutput[0],
		slippage: quoteData?.slippage || null,
		flowType: supportsAtomicBatching ? "bundled" : "regular",
	};

	/** on success */
	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if ((regularSuccess || batchingSuccess) && !isWrapUnwrapCase(quoteData)) {
			enableQuoteRefresh();

			const payload = {
				...formattedData,
			};
			setStatus("idle");

			// no need to call callsData with batching id
			// auto status api checks the status for batched txs
			// however, checking for failure cases (to capture reverts and onchain failures);

			if (batchingSuccess && batchingTxData) {
				payload.batchId = batchingTxData.id;
			}

			dispatch(setInflightData({ data: payload }));
			dispatch(setScreenTab(ScreenTab.INFLIGHT));
		}
	}, [regularSuccess, batchingSuccess, batchingTxData, regularTxData]);

	/** For wrap/unwrap transactions */
	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if (isWrapUnwrapCase(quoteData) && (txResolved || txSuccess)) {
			setStatus("idle");
			const destTokenSymbol = primaryOutput?.token?.symbol;
			const srcTokenSymbol = primaryInput?.token?.symbol;

			const _label = isNativeToken ? "Wrapping Successful" : "Unwrapping Successful";
			// biome-ignore format:
			const _subtext = isNativeToken ? `Wrapped ${srcTokenSymbol} to ${destTokenSymbol}` : `Unwrapped ${srcTokenSymbol} to ${destTokenSymbol}`

			showToast(_label, _subtext);
			bungeeConfig?.eventHandlers?.onEvent?.("success", {
				title: _label,
				message: _subtext,
				action: "TRANSACTION",
			});

			dispatch(setScreenTab(ScreenTab.HOME));
		}
	}, [txResolved, dispatch]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		const error = batchingError || regularError || txError;

		if (error) {
			console.log("Error: ", error);
			setStatus("error");
			const { name, shortMessage } = getCommonErrors(error);
			showToast(name, shortMessage, "error");

			enableQuoteRefresh();

			bungeeConfig?.eventHandlers?.onEvent?.("error", {
				title: name,
				message: shortMessage,
				action: "TRANSACTION",
			});

			logError(error, {
				operation: USER_OPERATIONS.TRANSACTION,
				action: "native-tx",
				flowType: supportsAtomicBatching ? "bundled" : "regular",
				routeType: "auto",
			});
		}
	}, [regularError, txError]);

	return (
		<Button
			label={getButtonLabel()}
			disabled={loadingState()}
			isLoading={loadingState()}
			onClick={initiateTransaction}
		/>
	);
};
