"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SVMSpokePoolClient = void 0;
const svm_1 = require("../../arch/svm");
const utils_1 = require("../../utils");
const BaseAbstractClient_1 = require("../BaseAbstractClient");
const SpokePoolClient_1 = require("./SpokePoolClient");
const types_1 = require("./types");
class SVMSpokePoolClient extends SpokePoolClient_1.SpokePoolClient {
    svmEventsClient;
    programId;
    statePda;
    type = types_1.SVM_SPOKE_POOL_CLIENT_TYPE;
    constructor(logger, hubPoolClient, chainId, deploymentSlot, eventSearchConfig, svmEventsClient, programId, statePda) {
        super(logger, hubPoolClient, chainId, Number(deploymentSlot), eventSearchConfig);
        this.svmEventsClient = svmEventsClient;
        this.programId = programId;
        this.statePda = statePda;
        this.spokePoolAddress = utils_1.SvmAddress.from(programId.toString());
    }
    static async create(logger, hubPoolClient, chainId, deploymentSlot, eventSearchConfig = { from: 0, maxLookBack: 0 }, rpc) {
        const svmEventsClient = await svm_1.SvmCpiEventsClient.create(rpc);
        const programId = svmEventsClient.getProgramAddress();
        const statePda = await (0, svm_1.getStatePda)(programId);
        return new SVMSpokePoolClient(logger, hubPoolClient, chainId, deploymentSlot, eventSearchConfig, svmEventsClient, programId, statePda);
    }
    static async createWithExistingEventClient(logger, hubPoolClient, chainId, deploymentSlot, eventSearchConfig = { from: 0, maxLookBack: 0 }, eventClient) {
        const programId = eventClient.getProgramAddress();
        const statePda = await (0, svm_1.getStatePda)(programId);
        return new SVMSpokePoolClient(logger, hubPoolClient, chainId, deploymentSlot, eventSearchConfig, eventClient, programId, statePda);
    }
    _queryableEventNames() {
        const internalEventNames = Object.values(svm_1.SVMEventNames);
        return internalEventNames.filter((e) => SpokePoolClient_1.knownEventNames.includes(e));
    }
    async _update(eventsToQuery) {
        const searchConfig = await this.updateSvmSearchConfig(this.svmEventsClient.getRpc(), this.logger);
        if ((0, BaseAbstractClient_1.isUpdateFailureReason)(searchConfig)) {
            const reason = searchConfig;
            return { success: false, reason };
        }
        const eventSearchConfigs = eventsToQuery.map((eventName) => {
            if (!this._queryableEventNames().includes(eventName)) {
                throw new Error(`SpokePoolClient: Cannot query unrecognised SpokePool event name: ${eventName}`);
            }
            const _searchConfig = { ...searchConfig };
            return _searchConfig;
        });
        const spokePoolAddress = this.svmEventsClient.getProgramAddress();
        this.log("debug", `Updating SpokePool client for chain ${this.chainId}`, {
            eventsToQuery,
            searchConfig,
            spokePool: spokePoolAddress,
        });
        const timerStart = Date.now();
        const [currentTime, ...eventsQueried] = await Promise.all([
            this.getTimeAt(searchConfig.to),
            ...eventsToQuery.map(async (eventName, idx) => {
                const config = eventSearchConfigs[idx];
                const events = await this.svmEventsClient.queryEvents(eventName, BigInt(config.from), BigInt(config.to), {
                    limit: config.maxLookBack,
                });
                return events.map((event) => ({
                    ...(0, svm_1.unwrapEventData)(event.data),
                    txnRef: event.signature,
                    blockNumber: Number(event.slot),
                    txnIndex: 0,
                    logIndex: 0,
                }));
            }),
        ]);
        this.log("debug", `Time to query new events from RPC for ${this.chainId}: ${Date.now() - timerStart} ms`);
        if (currentTime < this.currentTime) {
            const errMsg = `currentTime: ${currentTime} < ${this.currentTime}`;
            throw new Error(`SvmSpokePoolClient::update: ${errMsg}`);
        }
        eventsQueried.forEach((events) => (0, utils_1.sortEventsAscendingInPlace)(events));
        return {
            success: true,
            currentTime: Number(currentTime),
            searchEndBlock: searchConfig.to,
            events: eventsQueried,
        };
    }
    getMaxFillDeadlineInRange(_startSlot, _endSlot) {
        return (0, svm_1.getFillDeadline)(this.svmEventsClient.getRpc(), this.statePda);
    }
    async getTimestampForBlock(slot) {
        let _slot = BigInt(slot);
        const maxRetries = undefined;
        do {
            const timestamp = await (0, svm_1.getTimestampForSlot)(this.svmEventsClient.getRpc(), _slot, maxRetries, this.logger);
            if ((0, utils_1.isDefined)(timestamp)) {
                return timestamp;
            }
        } while (--_slot > 0);
        throw new Error(`Unable to resolve time at or before ${(0, utils_1.getNetworkName)(this.chainId)} slot ${slot}`);
    }
    getTimeAt(slot) {
        return this.getTimestampForBlock(slot);
    }
    async findDeposit(depositId) {
        const memoryDeposit = this.getDeposit(depositId);
        if (memoryDeposit) {
            return { found: true, deposit: memoryDeposit };
        }
        const deposit = await (0, svm_1.findDeposit)(this.svmEventsClient, depositId, this.logger);
        if (!deposit) {
            return {
                found: false,
                code: utils_1.InvalidFill.DepositIdNotFound,
                reason: `Deposit with ID ${depositId} not found`,
            };
        }
        const originChainId = this.chainId;
        return {
            found: true,
            deposit: {
                ...deposit,
                originChainId,
                quoteBlockNumber: await this.getBlockNumber(Number(deposit.quoteTimestamp)),
                fromLiteChain: this.isOriginLiteChain({ ...deposit, originChainId }),
                toLiteChain: this.isDestinationLiteChain(deposit),
            },
        };
    }
    relayFillStatus(relayData, atHeight) {
        return (0, svm_1.relayFillStatus)(this.programId, relayData, this.chainId, this.svmEventsClient, this.logger, atHeight);
    }
    fillStatusArray(relayData, atHeight, destinationChainId) {
        destinationChainId ??= this.chainId;
        return (0, svm_1.fillStatusArray)(this.programId, relayData, destinationChainId, this.svmEventsClient, this.logger, atHeight);
    }
}
exports.SVMSpokePoolClient = SVMSpokePoolClient;
//# sourceMappingURL=SVMSpokePoolClient.js.map