"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FetchTokenDataUseCase = void 0;
const not_found_error_1 = require("../../../domain/errors/not-found-error");
const validation_error_1 = require("../../../domain/errors/validation-error");
const getDefaultTokenDecimals = (networkAddressType) => {
    switch (networkAddressType) {
        case 'bitcoin':
            return 8;
        case 'solana':
        case 'ton':
            return 9;
        case 'ripple':
        case 'cosmos':
        case 'cardano':
        case 'algorand':
        case 'tron':
        case 'tezos':
        case 'hedera':
            return 6;
        case 'aptos':
        case 'nervos':
        case 'qtum':
        case 'waves':
            return 8;
        case 'sui':
            return 9;
        case 'near':
            return 24;
        case 'eos':
            return 4;
        case 'neo':
        case 'ontology':
            return 0;
        case 'nano':
            return 30;
        case 'chia':
            return 12;
        case 'evm':
        default:
            return 18;
    }
};
const normalizeContractAddress = (address, addressType) => {
    if (addressType === 'evm') {
        return address.toLowerCase();
    }
    return address;
};
class FetchTokenDataUseCase {
    networkRepository;
    tokenRepository;
    coinMarketCapClient;
    logger;
    constructor(networkRepository, tokenRepository, coinMarketCapClient, logger) {
        this.networkRepository = networkRepository;
        this.tokenRepository = tokenRepository;
        this.coinMarketCapClient = coinMarketCapClient;
        this.logger = logger;
    }
    async execute(input) {
        const network = await this.networkRepository.findById(input.networkId);
        if (!network) {
            throw new not_found_error_1.NotFoundError('Network', input.networkId);
        }
        if (!input.contractAddress && !input.cmcId) {
            throw new validation_error_1.ValidationError('Either contract address or CoinMarketCap ID is required', {
                contractAddress: ['Contract address or CoinMarketCap ID is required'],
                cmcId: ['Contract address or CoinMarketCap ID is required'],
            });
        }
        if (input.contractAddress) {
            return this.fetchByContractAddress(network, input.contractAddress);
        }
        return this.fetchByCmcId(network, input.cmcId);
    }
    async fetchByContractAddress(network, contractAddress) {
        const normalizedAddress = normalizeContractAddress(contractAddress, network.addressType);
        const existingToken = await this.tokenRepository.findByAddressAndNetworkId(normalizedAddress, network.id);
        if (existingToken) {
            return {
                isNative: existingToken.isNative,
                address: existingToken.address,
                networkId: existingToken.networkId,
                thumbnail: existingToken.thumbnail,
                name: existingToken.name,
                symbol: existingToken.symbol,
                decimals: existingToken.decimals,
                code: existingToken.code,
                info: existingToken.info,
                isActive: existingToken.isActive,
            };
        }
        const entry = await this.coinMarketCapClient.getTokenByContractAddress(network.name, normalizedAddress);
        if (!entry) {
            throw new not_found_error_1.NotFoundError('Token', normalizedAddress);
        }
        const metadata = await this.coinMarketCapClient.getCoin(entry.id).catch((error) => {
            this.logger.warn('Failed to fetch token metadata from CoinMarketCap', {
                contractAddress: normalizedAddress,
                networkName: network.name,
                error,
            });
            return null;
        });
        return {
            isNative: false,
            address: normalizedAddress,
            networkId: network.id,
            thumbnail: metadata?.logo ?? null,
            name: entry.name,
            symbol: entry.symbol,
            decimals: getDefaultTokenDecimals(network.addressType),
            code: entry.slug,
            cmcId: entry.id,
            info: metadata?.description ?? null,
            isActive: true,
        };
    }
    async fetchByCmcId(network, cmcId) {
        const coin = await this.coinMarketCapClient.getCoinWithContractAddresses(cmcId);
        if (!coin) {
            throw new not_found_error_1.NotFoundError('CoinMarketCap coin', String(cmcId));
        }
        const matchingContract = coin.contract_address.find((contract) => this.matchesNetworkName(contract.platform.name, network.name)) ?? coin.contract_address[0];
        const address = matchingContract?.contract_address ?? null;
        return {
            isNative: !address,
            address,
            networkId: network.id,
            thumbnail: coin.logo,
            name: coin.name,
            symbol: coin.symbol,
            decimals: getDefaultTokenDecimals(network.addressType),
            code: coin.slug,
            cmcId: coin.id,
            info: coin.description ?? null,
            isActive: true,
        };
    }
    matchesNetworkName(platformName, networkName) {
        const normalizedPlatform = platformName.toLowerCase().replace(/[^a-z0-9]+/g, '');
        const normalizedNetwork = networkName.toLowerCase().replace(/[^a-z0-9]+/g, '');
        if (normalizedPlatform === normalizedNetwork) {
            return true;
        }
        return normalizedPlatform.startsWith(normalizedNetwork) || normalizedNetwork.startsWith(normalizedPlatform);
    }
}
exports.FetchTokenDataUseCase = FetchTokenDataUseCase;
//# sourceMappingURL=fetch-token-data.use-case.js.map