import { useEffect, useMemo, useState } from "react";
import { type Address, getAddress, isAddress } from "viem";
import { normalize } from "viem/ens";
import { useBytecode, useEnsAddress } from "wagmi";

import { isPDA } from "@utils/isPDA";
import { isPublicKey } from "@utils/isPublicKey";
import { isSolana } from "@utils/isSolana";
import { isValidENS } from "@utils/isValidENS";

export interface Validity {
	isValid: boolean;
	isContract: boolean;
	isChecking: boolean;
	message?: string;
}

export interface UseValidateAddressReturn {
	state: Validity;
	address: string | undefined;
}

/**
 * Checks the validity of ENS or Account Address
 */
export const useValidateAddress = ({
	value,
	toChainId,
}: {
	value: string;
	toChainId?: number;
}): UseValidateAddressReturn => {
	const [address, setAddress] = useState<string>();

	// Auto-detect if address is Solana format or EVM format
	const isAddressSolanaFormat = useMemo(() => {
		if (!address) return false;
		return isPublicKey(address);
	}, [address]);

	const isAddressEvmFormat = useMemo(() => {
		if (!address) return false;
		return isAddress(address);
	}, [address]);

	// Determine if we should treat this as Solana based on chainId or address format
	const isChainSolana = useMemo(() => {
		if (toChainId !== undefined) {
			return isSolana(toChainId);
		}
		// Auto-detect based on address format when chainId is undefined
		return isAddressSolanaFormat;
	}, [toChainId, isAddressSolanaFormat]);

	// getting ensAddress if value is in valid ens format
	const { data: ensAddress, isPending } = useEnsAddress({
		name: isValidENS(value) ? normalize(value) : "",
		chainId: toChainId,
		query: {
			enabled: isValidENS(value) && Boolean(toChainId) && !isChainSolana,
		},
	});

	useEffect(() => {
		// For Solana addresses or non-ENS addresses, set address immediately
		// Check original value to decide if we should resolve ENS
		const isValueSolanaFormat = value ? isPublicKey(value) : false;
		if (isValueSolanaFormat || !isValidENS(value)) {
			setAddress(value);
		} else if (!isPending) {
			// For ENS addresses, wait for resolution
			setAddress(ensAddress || value);
		}
	}, [ensAddress, value, isPending]);

	const result = useBytecode({
		address: address as Address,
		chainId: toChainId,
		query: {
			// Only call useBytecode for EVM addresses (not Solana)
			enabled: Boolean(address) && isAddressEvmFormat && !isChainSolana,
		},
	});

	const state = useMemo(() => {
		if (!address) {
			return {
				isValid: false,
				isContract: false,
				isChecking: false,
			};
		}

		// Validate Solana addresses
		if (isAddressSolanaFormat) {
			return {
				isValid: isPublicKey(address),
				isContract: isPDA(address),
				isChecking: false,
			};
		}

		// Validate EVM addresses
		const addressStatus = getChecksumStatus(address);
		const isValid = addressStatus === "valid";

		return {
			isValid,
			isContract: Boolean(result?.data),
			isChecking: isValidENS(value) ? isPending : false,
			//   We only pass a message when the address is invalid and the reason is the checksum mismatch
			message:
				addressStatus === "invalid-checksum" ? "Invalid checksum" : undefined,
		};
	}, [address, result, value, isPending, isAddressSolanaFormat]);

	return { state, address };
};

const getChecksumStatus = (
	address: string,
): "valid" | "invalid-checksum" | "invalid-address" => {
	try {
		const normalized = getAddress(address);
		// ✅ If the address is all lowercase, treat as valid (non-checksummed but fine)
		if (address === address.toLowerCase()) {
			return "valid";
		}

		// ✅ If the address matches the correct checksum format, it’s valid
		if (address === normalized) {
			return "valid";
		}
		return "invalid-checksum";
	} catch {
		return "invalid-address";
	}
};
