"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResolveTokenByContractUseCase = void 0;
const not_found_error_1 = require("../../../domain/errors/not-found-error");
const getDefaultTokenDecimals = (networkAddressType) => {
    switch (networkAddressType) {
        case 'bitcoin':
            return 8;
        case 'solana':
        case 'ton':
            return 9;
        case 'ripple':
            return 6;
        case 'cosmos':
            return 6;
        case 'cardano':
            return 6;
        case 'algorand':
            return 6;
        case 'aptos':
            return 8;
        case 'sui':
            return 9;
        case 'near':
            return 24;
        case 'tron':
        case 'tezos':
        case 'hedera':
            return 6;
        case 'eos':
            return 4;
        case 'neo':
        case 'ontology':
            return 0;
        case 'nano':
            return 30;
        case 'nervos':
            return 8;
        case 'chia':
            return 12;
        case 'qtum':
        case 'waves':
            return 8;
        case 'evm':
        default:
            return 18;
    }
};
const normalizeContractAddress = (address, addressType) => {
    if (addressType === 'evm') {
        return address.toLowerCase();
    }
    return address;
};
class ResolveTokenByContractUseCase {
    networkRepository;
    tokenRepository;
    createTokenUseCase;
    coinMarketCapClient;
    networkRpcClient;
    logger;
    constructor(networkRepository, tokenRepository, createTokenUseCase, coinMarketCapClient, networkRpcClient, logger) {
        this.networkRepository = networkRepository;
        this.tokenRepository = tokenRepository;
        this.createTokenUseCase = createTokenUseCase;
        this.coinMarketCapClient = coinMarketCapClient;
        this.networkRpcClient = networkRpcClient;
        this.logger = logger;
    }
    async execute(input) {
        const network = await this.resolveNetwork(input.chainId);
        if (!input.contractAddress) {
            return this.resolveNativeToken(network);
        }
        const normalizedAddress = normalizeContractAddress(input.contractAddress, network.addressType);
        const existingToken = await this.tokenRepository.findByAddressAndNetworkId(normalizedAddress, network.id);
        if (existingToken) {
            return { token: existingToken, network };
        }
        const token = await this.createToken(network, normalizedAddress);
        return { token, network };
    }
    async resolveNativeToken(network) {
        const tokens = await this.tokenRepository.findByNetworkId(network.id);
        const nativeToken = tokens.find((token) => token.isNative);
        if (nativeToken) {
            return { token: nativeToken, network };
        }
        const token = await this.createNativeToken(network);
        return { token, network };
    }
    async resolveNetwork(chainId) {
        const existingNetwork = await this.networkRepository.findByChainId(chainId);
        if (existingNetwork) {
            return existingNetwork;
        }
        throw new not_found_error_1.NotFoundError('Network', String(chainId));
    }
    async createToken(network, contractAddress) {
        const entry = await this.coinMarketCapClient.getTokenByContractAddress(network.name, contractAddress);
        if (entry) {
            const metadata = await this.coinMarketCapClient.getCoin(entry.id).catch((error) => {
                this.logger.warn('Failed to fetch token metadata from CoinMarketCap', {
                    contractAddress,
                    networkName: network.name,
                    error,
                });
                return null;
            });
            const thumbnail = metadata?.logo ?? null;
            return this.createTokenUseCase.execute({
                isNative: false,
                address: contractAddress,
                networkId: network.id,
                thumbnail,
                name: entry.name,
                symbol: entry.symbol,
                decimals: getDefaultTokenDecimals(network.addressType),
                code: entry.slug,
                cmcId: entry.id,
                info: metadata?.description ?? null,
                isActive: true,
            });
        }
        this.logger.info('CoinMarketCap lookup missed, falling back to on-chain metadata', {
            contractAddress,
            networkName: network.name,
            chainId: network.chainId,
        });
        const metadata = await this.fetchOnChainMetadata(network, contractAddress);
        if (!metadata) {
            throw new not_found_error_1.NotFoundError('Token', contractAddress);
        }
        return this.createTokenUseCase.execute({
            isNative: false,
            address: contractAddress,
            networkId: network.id,
            thumbnail: null,
            name: metadata.name,
            symbol: metadata.symbol,
            decimals: metadata.decimals,
            code: metadata.symbol.toLowerCase(),
            cmcId: undefined,
            info: null,
            isActive: true,
        });
    }
    async fetchOnChainMetadata(network, contractAddress) {
        if (!network.chainId || network.addressType !== 'evm') {
            return null;
        }
        return this.networkRpcClient.getErc20TokenMetadata(network.chainId, contractAddress);
    }
    async createNativeToken(network) {
        const cmcId = await this.coinMarketCapClient.getCoinIdByToken(network.name, network.code, true);
        if (!cmcId) {
            throw new not_found_error_1.NotFoundError('Native token', network.name);
        }
        const metadata = await this.coinMarketCapClient.getCoin(cmcId).catch((error) => {
            this.logger.warn('Failed to fetch native token metadata from CoinMarketCap', {
                networkName: network.name,
                networkCode: network.code,
                error,
            });
            return null;
        });
        const thumbnail = metadata?.logo ?? null;
        return this.createTokenUseCase.execute({
            isNative: true,
            address: null,
            networkId: network.id,
            thumbnail,
            name: metadata?.name ?? network.name,
            symbol: metadata?.symbol ?? network.code,
            decimals: getDefaultTokenDecimals(network.addressType),
            code: metadata?.slug ?? network.code.toLowerCase(),
            cmcId,
            info: metadata?.description ?? null,
            isActive: true,
        });
    }
}
exports.ResolveTokenByContractUseCase = ResolveTokenByContractUseCase;
//# sourceMappingURL=resolve-token-by-contract.use-case.js.map