import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { type Address, erc20Abi, maxUint256 } from "viem";
import {
	useAccount,
	useWaitForTransactionReceipt,
	useWriteContract,
} from "wagmi";

import { Button } from "@components/common/Button";
import { useActiveRoute } from "@hooks/apis/common/quote/useActiveRoute";
import { isAutoQuote, isManualQuote } from "@hooks/apis/common/quote/useQuote";
import { useChainSwitcher } from "@hooks/useChainSwitcher";
import { useConfig } from "@hooks/useConfig";
import { USER_OPERATIONS, useLogError } from "@hooks/useLogError";
import { useToast } from "@providers/ToastProvider";
import { setApprovalData } from "@store/approvalSlice";
import { setIsFetchingDisabled } from "@store/quoteParamSlice";
import { getCommonErrors } from "@utils/getCommonErrors";
import { onTokenApproved } from "../../../analytics/events";

export const ApproveToken = ({
	checkingApproval,
	isLoading,
}: {
	checkingApproval: boolean;
	isLoading: boolean;
}) => {
	const { chainId } = useAccount();
	const dispatch = useDispatch();
	const { logError } = useLogError();
	const { switchToChain } = useChainSwitcher();
	const bungeeConfig = useConfig();
	const {
		writeContract,
		data: hash,
		status,
		error: contractError,
		isError: isContractError,
	} = useWriteContract();
	const { showToast } = useToast();

	const { data: activeRoute } = useActiveRoute();

	/** Checking Tx Receipt to make sure the tx is not reverted on chain */
	const {
		data: receiptData,
		error: txReceiptError,
		isError: isTxReceiptError,
	} = useWaitForTransactionReceipt({
		hash,
		chainId: activeRoute?.primaryInput.token.chainId ?? chainId,
		query: {
			enabled: Boolean(hash),
			retry: 5,
			retryDelay: 3000,
		},
	});

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if (receiptData?.status === "success") {
			// Emit event
			onTokenApproved({
				token_symbol: token.symbol,
				chain_id: token.chainId,
				contract: isManualQuote(activeRoute) ? "gateway" : "permit2",
			});
		}
		if (receiptData?.status === "success" && showToast) {
			// Decode the approved amount from hex
			const approvedAmountHex = receiptData.logs[0].data;
			const approvedAmount = BigInt(approvedAmountHex); // Convert hex to BigInt

			// Check if approved amount is greater than or equal to approvalAmount
			// do not check with max (suggested) approval - check with minimum approval required
			if (
				approvedAmount >= BigInt(activeRoute?.approvalData?.amount ?? approvalAmount)
			) {
				const toastTitle = "Token Approval Successful";
				// biome-ignore format:
				const toastMsg = `Approval given to ${token.symbol}`;

				showToast(toastTitle, toastMsg);

				bungeeConfig?.eventHandlers?.onEvent?.("success", {
					title: toastTitle,
					message: toastMsg,
					action: "APPROVAL",
				});

				dispatch(
					setApprovalData({
						isVisible: false,
						isApprovalGiven: true,
					}),
				);
			} else {
				const toastTitle = "Token Approval Failed";
				// biome-ignore format:
				const toastMsg = `Approved amount is less than required for ${token.symbol}`;

				showToast(toastTitle, toastMsg, "error");

				bungeeConfig?.eventHandlers?.onEvent?.("warning", {
					title: toastTitle,
					message: toastMsg,
					action: "APPROVAL",
				});

				dispatch(
					setApprovalData({
						isVisible: false,
						isApprovalGiven: false,
					}),
				);
			}

			dispatch(setIsFetchingDisabled(false));
		}
	}, [receiptData]);

	/** @todo fix the error messages. check if getCommonErrors still works */
	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if ((isContractError || isTxReceiptError) && showToast) {
			dispatch(
				setApprovalData({
					isVisible: false,
					isApprovalGiven: false,
				}),
			);
			dispatch(setIsFetchingDisabled(false));
			const error = isContractError ? contractError : txReceiptError;
			if (error) {
				const { name, shortMessage } = getCommonErrors(error);
				bungeeConfig?.eventHandlers?.onEvent?.("error", {
					title: name ?? "Token approval failed",
					message: shortMessage,
					action: "APPROVAL",
				});
				showToast(name ?? "Token Approval Failied", shortMessage, "error");
				logError(error, { operation: USER_OPERATIONS.APPROVAL, token: primaryInput });
			}
		}
	}, [
		contractError,
		txReceiptError,
		isTxReceiptError,
		isContractError,
		dispatch,
	]);

	if (!activeRoute || !activeRoute?.approvalData) return <></>;

	const { primaryInput } = activeRoute || {};
	const token = primaryInput.token;

	function getButtonLabel() {
		if (checkingApproval) return "Checking for approval";

		if (status === "pending") {
			return "Confirm in wallet";
		}

		//biome-ignore format:
		return `Approve ${token.symbol}`;
	}

	const targetAddress = activeRoute?.approvalData.spenderAddress as Address;

	// for auto route & userOp='sign', set the amount to max
	const approvalAmount = (() => {
		if (isAutoQuote(activeRoute) && activeRoute.userOp === "sign")
			return maxUint256.toString();
		return activeRoute?.approvalData.amount;
	})();

	async function approveToken() {
		dispatch(setIsFetchingDisabled(true));
		const targetChainId = primaryInput?.token?.chainId;
		const chainSwitched = await switchToChain(targetChainId, chainId);
		if (!chainSwitched) return;

		openApproval();

		writeContract({
			abi: erc20Abi,
			address: primaryInput?.token.address as Address,
			functionName: "approve",
			chainId: primaryInput.token.chainId,
			args: [targetAddress, BigInt(approvalAmount)],
		});
	}

	const openApproval = () => {
		dispatch(
			setApprovalData({
				isVisible: true,
				approvalType: isManualQuote(activeRoute) ? "erc20" : "permit2",
			}),
		);
	};

	return (
		<Button
			label={getButtonLabel()}
			onClick={approveToken}
			disabled={status === "pending" || isLoading}
			isLoading={status === "pending" || isLoading}
			id="approve-btn"
		/>
	);
};
