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

import { Button } from "@components/common/Button";
import type { ManualOrderData } from "@hooks/apis/auto/useAutoHistory";
import type {
	FormattedManualQuote,
	OrderFlowType,
	TxData,
} from "@hooks/apis/common/quote/types";
import { useBuildTransaction } from "@hooks/apis/manual/useBuildTransaction";
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 { useWallet } from "@solana/wallet-adapter-react";
import {
	ScreenTab,
	setScreenTab,
	updateManualHistory,
} from "@store/globalSlice";
import { setInflightData } from "@store/inflightSlice";
import { setIsFetchingDisabled } from "@store/quoteParamSlice";
import type { SOLANA_CHAIN_ID } from "@utils/constants";
import { getCommonErrors } from "@utils/getCommonErrors";
import { isSolana } from "@utils/isSolana";
import { signAndSendTransaction } from "@utils/sendTransaction";
import { onInitiateSwap } from "../../../analytics/events";
import { useIsApprovalRequired } from "../Approval/CheckApproval";
import type { QueryStatus } from "../Auto/SignOrder";
import { useBatchingTransaction } from "./useBatchingTransaction";
import { useRegularTransaction } from "./useRegularTransaction";

export const ManualFlow = ({ data }: { data: FormattedManualQuote }) => {
	const dispatch = useDispatch();
	const { logError } = useLogError();
	const { rpcs, eventHandlers } = useBungeeConfig();
	const isApprovalRequired = useIsApprovalRequired();

	const {
		data: buildData,
		isLoading: isBuildingtx,
		isError: isBuildErr,
		error: buildErr,
	} = useBuildTransaction({
		routeId: data.routeId,
	});
	const { accounts } = useAccounts();

	const ethereumAccount = accounts.find((account) => account.chainType === "EVM");
	const solanaAccount = accounts.find((account) => account.chainType === "SVM");

	const targetChainId = data?.primaryInput?.token?.chainId;
	const isSolanaTx = isSolana(targetChainId);

	const { supportsAtomicBatching } = useWalletCapabilities(targetChainId);

	const { switchToChain } = useChainSwitcher();
	const { showToast } = useToast();
	const [status, setStatus] = useState<QueryStatus>("idle");

	const { sendTransaction: sendSolanaTransaction } = useWallet();

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if (isBuildErr && buildErr) {
			const { name, shortMessage } = getCommonErrors(buildErr);
			setStatus("idle");
			showToast(name, shortMessage, "error");
			eventHandlers?.onEvent?.("error", {
				title: name,
				message: shortMessage,
				action: "TRANSACTION",
			});
		}
	}, [isBuildErr, buildErr]);

	const {
		triggerAsync: triggerRegular,
		isSuccess: regularSuccess,
		error: regularError,
		txData: regularTxData, // txhash
	} = useRegularTransaction({ txData: buildData?.txData });
	const {
		triggerAsync: triggerBatching,
		isSuccess: batchingSuccess,
		error: batchingError,
		txData: batchingTxData, // id and capabilities
	} = useBatchingTransaction({
		approvalData: buildData?.approvalData,
		txData: buildData?.txData,
		chainId: data?.primaryInput?.token?.chainId,
		isApprovalRequired,
	});

	const [solanaTxData, setSolanaTxData] = useState<string | null>(null);

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

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	const initiateTransaction = useCallback(async () => {
		let txHash = "";
		let type: OrderFlowType<"solana"> = "regular";
		try {
			dispatch(setIsFetchingDisabled(true));

			if (isSolanaTx) {
				setStatus("awaiting_wallet_confirmation");
				const tx = await signAndSendTransaction({
					address: solanaAccount?.address || data.sender.toLowerCase(),
					txData: buildData.txData as TxData<typeof SOLANA_CHAIN_ID>,
					sendTransaction: sendSolanaTransaction,
					rpc: rpcs?.solana,
				});
				setSolanaTxData(tx);
				txHash = tx;
				type = "solana";
				return;
			}

			const targetChainId = data?.primaryInput?.token?.chainId;
			const chainSwitched = await switchToChain(
				targetChainId,
				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("idle");
			const _errTitle = "Failed to initiate transaction";

			showToast(_errTitle, "Please try again", "error");
			console.error(_errTitle, err);
			logError(err, {
				operation: USER_OPERATIONS.TRANSACTION,
				action: "manual-transaction",
			});
		} finally {
			// Refetch balances
			eventHandlers?.onSwapInitiated?.({
				chainId: data?.primaryInput?.token?.chainId,
				hash: txHash,
				type,
			});

			const isReceiverConnectedWallet = [
				ethereumAccount?.address?.toLowerCase(),
				solanaAccount?.address?.toLowerCase(),
			].includes(data.receiver.toLowerCase());

			// Emit event
			onInitiateSwap({
				source_chain_id: data?.primaryInput?.token?.chainId,
				destination_chain_id: data?.primaryOutput?.token?.chainId,
				source_token_symbol: data?.primaryInput?.token?.symbol,
				destination_token_symbol: data?.primaryOutput?.token?.symbol,
				input_amount_usd: Number(data?.primaryInput?.amount),
				request_hash: txHash,
				provider_name: data?.protocol?.dexDetails?.protocol.name ?? "",
				has_refuel: !!data?.refuel,
				has_custom_recipient: !isReceiverConnectedWallet,
			});
		}
	}, [
		buildData,
		data,
		dispatch,
		ethereumAccount,
		solanaAccount,
		supportsAtomicBatching,
		switchToChain,
		triggerBatching,
		triggerRegular,
	]);

	const modifiedOutput = data.output.map((item) => ({
		...item,
		minAmountOut: data.minOutput?.amount || "", // used to show output in history and inflight
	}));

	const formattedData: ManualOrderData = {
		...data,
		requestHash: regularTxData as string,
		output: modifiedOutput,
		orderTimestamp: Math.floor(Date.now() / 1000), // when the order is placed
		srcTimestamp: 0, // after extraction, so add it when tx receipt returns success
		swapTime: data.estimatedFulfilTime,
		primaryOutput: modifiedOutput?.[0],
		statusCode: 0, // setting to pending
		status: "PENDING",
		isSwapRoute: data.isSwapRoute,
		flowType: supportsAtomicBatching ? "bundled" : "regular",
	};

	// On Success
	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if (regularSuccess || batchingSuccess || solanaTxData) {
			const payload = {
				...formattedData,
			};
			enableQuoteRefresh();
			setStatus("idle");

			if (regularTxData) {
				payload.requestHash = regularTxData;
			}

			if (batchingTxData) {
				payload.requestHash = batchingTxData.id;
			}

			if (solanaTxData) {
				payload.requestHash = solanaTxData;
			}

			dispatch(setInflightData({ data: payload }));
			dispatch(setScreenTab(ScreenTab.INFLIGHT));

			const address = isSolanaTx ? solanaAccount?.address : ethereumAccount?.address;

			// updating the localstorage - only for manual txs
			dispatch(
				updateManualHistory({
					data: formattedData,
					address: address?.toLowerCase() || data.sender.toLowerCase(),
				}),
			);
		}
	}, [
		isSolanaTx,
		dispatch,
		solanaAccount,
		ethereumAccount,
		regularTxData,
		batchingTxData,
		data.sender,
		regularSuccess,
		batchingSuccess,
		solanaTxData,
	]);

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

		if (error) {
			setStatus("idle");
			const { name, shortMessage } = getCommonErrors(error);
			showToast(name, shortMessage, "error");
			enableQuoteRefresh();
			logError(error, {
				operation: USER_OPERATIONS.TRANSACTION,
				action: "manual-tx",
				flowType: supportsAtomicBatching ? "bundled" : "regular",
				routeType: "manual",
			});
		}
	}, [regularError, batchingError]);

	function getButtonLabel() {
		if (status === "awaiting_wallet_confirmation") return "Confirm in wallet";
		// if (status === "loading") return "Submitting request";
		if (isBuildingtx) return "Building";

		return "Swap";
	}

	const disable = isBuildingtx || status === "awaiting_wallet_confirmation";

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