import { type Address, erc20Abi, isAddress } from "viem";
import { usePublicClient } from "wagmi";

import type { Token } from "@/types/index";
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
import { NATIVE_TOKEN_ADDRESS } from "@utils/constants";
import { getValidAddress } from "@utils/getValidAddress";
import { isDummyAddress } from "@utils/isDummyAddress";
import { isPublicKey } from "@utils/isPublicKey";
import { useAccounts } from "./useAccounts";
import { useConfig } from "./useConfig";
import { getSolanaBalance } from "./useSolanaBalance";

type TokenBalance = {
	balance: string | bigint;
	isLoading: boolean;
};

const REFETCH_INTERVAL = 60000;

type TokenBalanceParams = {
	token: Token;
	shouldFetch?: boolean;
	query?: Omit<
		UseQueryOptions<string | bigint, Error, string | bigint, readonly unknown[]>,
		"queryKey" | "queryFn" | "enabled"
	>;
};

export const useTokenBalance = ({
	token,
	shouldFetch = true,
	query,
}: TokenBalanceParams): TokenBalance => {
	const { accounts } = useAccounts();

	const chainId = token?.chainId;
	// biome-ignore format:
	const tokenAddress = token?.address.toLowerCase() as `0x${string}`;
	const isNativeToken = tokenAddress === NATIVE_TOKEN_ADDRESS;

	const evmAddress = accounts.find(
		(account) => account.chainType === "EVM",
	)?.address;
	const solanaAddress = accounts.find(
		(account) => account.chainType === "SVM",
	)?.address;

	const userAddress = getValidAddress({
		chainId,
		address: evmAddress || null,
		fallbackAddress: solanaAddress || null,
	});

	const isValidEvmAddress = Boolean(
		userAddress && isAddress(userAddress) && !isDummyAddress(userAddress),
	);

	const isValidSolanaAddress = Boolean(
		userAddress && isPublicKey(userAddress) && !isDummyAddress(userAddress),
	);

	const publicClient = usePublicClient({ chainId });

	const { rpcs } = useConfig();

	const {
		data: balance = "0",
		isLoading,
		isFetching,
	} = useQuery<string | bigint>({
		queryKey: ["tokenBalance", tokenAddress, chainId, userAddress],
		enabled: !!token && shouldFetch && !!userAddress,
		refetchInterval: REFETCH_INTERVAL,
		queryFn: async () => {
			if (!userAddress || !token) return "0";

			// 🟣 Solana
			if (isValidSolanaAddress) {
				const balances = await getSolanaBalance({
					userAddress,
					rpc: rpcs?.solana,
					tokenAddress,
				});
				return isNativeToken ? balances.nativeBalance : balances.tokenBalance || "0";
			}

			// 🔵 EVM Native ETH
			if (isNativeToken && isValidEvmAddress) {
				const balance = await publicClient?.getBalance({
					address: userAddress as Address,
				});
				return balance?.toString() || "0";
			}

			// 🟡 EVM ERC20 Token
			if (!isNativeToken && isValidEvmAddress) {
				const balance = await publicClient?.readContract({
					abi: erc20Abi,
					address: tokenAddress,
					functionName: "balanceOf",
					args: [userAddress as Address],
				});

				return balance?.toString() || "0";
			}

			return "0";
		},
		...query,
	});

	return {
		balance,
		isLoading: isLoading || isFetching,
	};
};
