"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoralisAdapter = void 0;
const axios_1 = __importDefault(require("axios"));
const config_1 = __importDefault(require("../../../../config"));
const transaction_1 = require("../../evm/models/transaction");
const apiStream_1 = require("../streams/apiStream");
const errors_1 = require("./errors");
const moralis_utils_1 = require("./moralis-utils");
const TX_HASH_REGEX = /^0x[0-9a-fA-F]{64}$/;
class MoralisAdapter {
    constructor(providerConfig) {
        this.name = 'Moralis';
        this.baseUrl = 'https://deep-index.moralis.io/api/v2.2';
        const apiKey = config_1.default.externalProviders?.moralis?.apiKey;
        if (!apiKey)
            throw new Error('MoralisAdapter: apiKey is required in config.externalProviders.moralis');
        this.apiKey = apiKey;
        this.requestTimeout = providerConfig.requestTimeout ?? 30000;
        this.headers = {
            'Content-Type': 'application/json',
            'X-API-Key': this.apiKey
        };
    }
    async getTransaction(params) {
        const { chain, network, chainId, txId } = params;
        if (!TX_HASH_REGEX.test(txId)) {
            throw new errors_1.AdapterError(this.name, errors_1.AdapterErrorCode.INVALID_REQUEST, `invalid txId format: ${txId}`);
        }
        const query = (0, moralis_utils_1.buildMoralisQueryString)({
            chain: (0, moralis_utils_1.formatMoralisChainId)(chainId),
            include: 'internal_transactions'
        });
        try {
            const response = await axios_1.default.get(`${this.baseUrl}/transaction/${txId}${query}`, { headers: this.headers, timeout: this.requestTimeout });
            if (!response.data)
                return undefined;
            return (0, moralis_utils_1.transformMoralisTransaction)({ chain, network, ...response.data });
        }
        catch (error) {
            if (error instanceof errors_1.AdapterError)
                throw error;
            if (axios_1.default.isAxiosError(error) && error.response?.status === 404) {
                return undefined;
            }
            this._classifyAndThrow(error);
        }
    }
    streamAddressTransactions(params) {
        const { chainId, chain, network, address, args } = params;
        const query = (0, moralis_utils_1.transformMoralisQueryParams)({ chainId, args });
        const queryStr = (0, moralis_utils_1.buildMoralisQueryString)({
            ...query,
            order: args.order ?? query.order ?? 'DESC',
            limit: args.pageSize || 10,
            include: 'internal_transactions'
        });
        const streamArgs = {
            ...args,
            transform: (tx) => {
                const _tx = (0, moralis_utils_1.transformMoralisTransaction)({ chain, network, ...tx });
                const confirmations = args.tipHeight && Number.isFinite(_tx.blockHeight) ? args.tipHeight - _tx.blockHeight + 1 : 0;
                return transaction_1.EVMTransactionStorage._apiTransform({ ..._tx, confirmations }, { object: true });
            }
        };
        return new apiStream_1.ExternalApiStream(`${this.baseUrl}/${address}${queryStr}`, this.headers, streamArgs);
    }
    streamERC20Transfers(params) {
        const { chainId, chain, network, address, tokenAddress, args } = params;
        const query = (0, moralis_utils_1.transformMoralisQueryParams)({ chainId, args });
        const queryStr = (0, moralis_utils_1.buildMoralisQueryString)({
            ...query,
            order: args.order ?? query.order ?? 'DESC',
            limit: args.pageSize || 10,
            contract_addresses: [tokenAddress]
        });
        const streamArgs = {
            ...args,
            transform: (tx) => {
                const _tx = (0, moralis_utils_1.transformMoralisTokenTransfer)({ chain, network, ...tx });
                const confirmations = args.tipHeight && Number.isFinite(_tx.blockHeight) ? args.tipHeight - _tx.blockHeight + 1 : 0;
                return transaction_1.EVMTransactionStorage._apiTransform({ ..._tx, confirmations }, { object: true });
            }
        };
        return new apiStream_1.ExternalApiStream(`${this.baseUrl}/${address}/erc20/transfers${queryStr}`, this.headers, streamArgs);
    }
    async getBlockNumberByDate(params) {
        const { chainId, date } = params;
        const queryStr = (0, moralis_utils_1.buildMoralisQueryString)({
            chain: (0, moralis_utils_1.formatMoralisChainId)(chainId),
            date: new Date(date).getTime()
        });
        try {
            const response = await axios_1.default.get(`${this.baseUrl}/dateToBlock${queryStr}`, { headers: this.headers, timeout: this.requestTimeout });
            return response.data.block;
        }
        catch (error) {
            this._classifyAndThrow(error);
        }
    }
    async healthCheck() {
        try {
            await axios_1.default.get(`${this.baseUrl}/web3/version`, {
                headers: this.headers,
                timeout: 5000
            });
            return true;
        }
        catch {
            return false;
        }
    }
    _classifyAndThrow(error) {
        if (axios_1.default.isAxiosError(error)) {
            const status = error.response?.status;
            if (status === 401 || status === 403) {
                throw new errors_1.AdapterError(this.name, errors_1.AdapterErrorCode.AUTH);
            }
            if (status === 429) {
                throw new errors_1.AdapterError(this.name, errors_1.AdapterErrorCode.RATE_LIMIT);
            }
            if (error.code === 'ECONNABORTED') {
                throw new errors_1.AdapterError(this.name, errors_1.AdapterErrorCode.TIMEOUT, `timed out after ${this.requestTimeout}ms`);
            }
            if (status && status >= 500) {
                throw new errors_1.AdapterError(this.name, errors_1.AdapterErrorCode.UPSTREAM, `HTTP ${status}`);
            }
        }
        throw new errors_1.AdapterError(this.name, errors_1.AdapterErrorCode.UPSTREAM, error?.message);
    }
}
exports.MoralisAdapter = MoralisAdapter;
//# sourceMappingURL=moralis.js.map