import type { WalletAdapter } from "@solana/wallet-adapter-base";
import { useWallet } from "@solana/wallet-adapter-react";
import { SOLANA_CHAIN_ID } from "@utils/constants.js";
import { useMemo } from "react";
import type { Connector } from "wagmi";
import { useAccount as useAccountInternal } from "wagmi";
import { create } from "zustand";

type SupportedChainType = "EVM" | "SVM";

export interface AccountBase<
	CT extends SupportedChainType,
	ConnectorType = undefined,
> {
	address?: string;
	addresses?: readonly string[];
	chainId?: number;
	chainType: CT;
	connector?: ConnectorType;
	isConnected: boolean;
	isConnecting: boolean;
	isDisconnected: boolean;
	isReconnecting: boolean;
	status: "connected" | "reconnecting" | "connecting" | "disconnected";
}

export type EVMAccount = AccountBase<"EVM", Connector>;
export type SVMAccount = AccountBase<"SVM", WalletAdapter>;
export type DefaultAccount = AccountBase<SupportedChainType>;

export type Account = EVMAccount | SVMAccount | DefaultAccount;

export interface AccountResult {
	account: Account;
	/**
	 * Connected accounts
	 */
	accounts: Account[];
	/**
	 * If at least one account is connected
	 */
	isAnyConnected: boolean;
}

interface UseAccountArgs {
	chainType?: SupportedChainType;
}

const defaultAccount: AccountBase<SupportedChainType> = {
	chainType: "EVM",
	isConnected: false,
	isConnecting: false,
	isReconnecting: false,
	isDisconnected: true,
	status: "disconnected",
};

export type LastConnectedAccount = WalletAdapter | Connector | null;

interface LastConnectedAccountStore {
	lastConnectedAccount: LastConnectedAccount;
	setLastConnectedAccount: (account: LastConnectedAccount) => void;
}

export const useLastConnectedAccount = create<LastConnectedAccountStore>(
	(set) => ({
		lastConnectedAccount: null,
		setLastConnectedAccount: (account) => set({ lastConnectedAccount: account }),
	}),
);

/**
 * @param args When we provide args we want to return either account with corresponding chainType or default disconnected one
 * @returns - Account result
 */
export const useAccounts = (args?: UseAccountArgs): AccountResult => {
	const wagmiAccount = useAccountInternal();
	const { wallet } = useWallet();
	const { lastConnectedAccount } = useLastConnectedAccount();

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	return useMemo(() => {
		const svm: Account = wallet?.adapter.publicKey
			? {
					address: wallet.adapter.publicKey.toString(),
					chainId: SOLANA_CHAIN_ID,
					chainType: "SVM",
					connector: wallet.adapter,
					isConnected: Boolean(wallet.adapter.publicKey),
					isConnecting: false,
					isReconnecting: false,
					isDisconnected: !wallet,
					status: "connected",
				}
			: {
					chainType: "SVM",
					isConnected: false,
					isConnecting: false,
					isReconnecting: false,
					isDisconnected: true,
					status: "disconnected",
				};
		const evm: Account = { ...wagmiAccount, chainType: "EVM" };
		const accounts = [evm, svm];
		const connectedAccounts = accounts.filter(
			(account) => account.isConnected && account.address,
		);

		// If a chainType argument is provided, attempt to find a connected account with the matching chainType.
		// If no matching account is found, fallback to the default account.
		// If no chainType argument, selectedAccount should be used.
		const selectedChainTypeAccount = args?.chainType
			? connectedAccounts.find((account) => account.chainType === args?.chainType) ||
				defaultAccount
			: undefined;

		// If lastConnectedAccount exists, attempt to find a connected account with a matching connector ID or name.
		// If no matching account is found, fallback to the first connected account.
		// If lastConnectedAccount is not present, simply select the first connected account.
		const selectedAccount = lastConnectedAccount
			? connectedAccounts.find((account) => {
					const connectorIdMatch =
						(lastConnectedAccount as Connector)?.id ===
						(account.connector as Connector)?.id;
					const connectorNameMatch =
						!(lastConnectedAccount as Connector)?.id &&
						(lastConnectedAccount as WalletAdapter)?.name === account.connector?.name;
					return connectorIdMatch || connectorNameMatch;
				}) || connectedAccounts[0]
			: connectedAccounts[0];

		return {
			account: selectedChainTypeAccount || selectedAccount || defaultAccount,
			accounts: connectedAccounts,
			isAnyConnected: connectedAccounts.some((account) => account.isConnected),
		};
	}, [
		wallet,
		wallet?.adapter.publicKey,
		wagmiAccount,
		args?.chainType,
		lastConnectedAccount,
	]);
};
