import { __assign, __awaiter, __generator, __spreadArray } from "tslib";
import { getDeployedAddress, SvmSpokeIdl } from "@across-protocol/contracts";
import { getSolanaChainId } from "@across-protocol/contracts/dist/src/svm/web3-v1";
import { address, getProgramDerivedAddress, } from "@solana/kit";
import { bs58, chainIsSvm, getMessageHash, toAddressType } from "../../utils";
import { decodeEvent, isDevnet } from "./utils";
import { unwrapEventData } from "./";
import assert from "assert";
var SvmCpiEventsClient = /** @class */ (function () {
    /**
     * Note: Strongly prefer to use the async create() method to instantiate.
     */
    function SvmCpiEventsClient(rpc, address, eventAuthority, idl) {
        this.rpc = rpc;
        this.programAddress = address;
        this.programEventAuthority = eventAuthority;
        this.idl = idl;
    }
    /**
     * Factory method to asynchronously create an instance of SvmSpokeEventsClient.
     */
    SvmCpiEventsClient.create = function (rpc) {
        return __awaiter(this, void 0, void 0, function () {
            var isTestnet, programId;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, isDevnet(rpc)];
                    case 1:
                        isTestnet = _a.sent();
                        programId = getDeployedAddress("SvmSpoke", getSolanaChainId(isTestnet ? "devnet" : "mainnet").toString());
                        if (!programId)
                            throw new Error("Program not found");
                        return [2 /*return*/, this.createFor(rpc, programId, SvmSpokeIdl)];
                }
            });
        });
    };
    SvmCpiEventsClient.createFor = function (rpc, programId, idl) {
        return __awaiter(this, void 0, void 0, function () {
            var programAddress, eventAuthority;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        programAddress = address(programId);
                        return [4 /*yield*/, getProgramDerivedAddress({
                                programAddress: programAddress,
                                seeds: ["__event_authority"],
                            })];
                    case 1:
                        eventAuthority = (_a.sent())[0];
                        return [2 /*return*/, new SvmCpiEventsClient(rpc, programAddress, eventAuthority, idl)];
                }
            });
        });
    };
    /**
     * Queries events for the SvmSpoke program filtered by event name.
     *
     * @param eventName - The name of the event to filter by.
     * @param fromSlot - Optional starting slot.
     * @param toSlot - Optional ending slot.
     * @param options - Options for fetching signatures.
     * @returns A promise that resolves to an array of events matching the eventName.
     */
    SvmCpiEventsClient.prototype.queryEvents = function (eventName, fromSlot, toSlot, options) {
        if (options === void 0) { options = { limit: 1000, commitment: "confirmed" }; }
        return __awaiter(this, void 0, void 0, function () {
            var events;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.queryAllEvents(fromSlot, toSlot, options)];
                    case 1:
                        events = _a.sent();
                        return [2 /*return*/, events.filter(function (event) { return event.name === eventName; })];
                }
            });
        });
    };
    /**
     * Queries events for the provided derived address at instantiation filtered by event name.
     *
     * @param eventName - The name of the event to filter by.
     * @param fromSlot - Optional starting slot.
     * @param toSlot - Optional ending slot.
     * @param options - Options for fetching signatures.
     * @returns A promise that resolves to an array of events matching the eventName.
     */
    SvmCpiEventsClient.prototype.queryDerivedAddressEvents = function (eventName, derivedAddress, fromSlot, toSlot, options) {
        if (options === void 0) { options = { limit: 1000, commitment: "confirmed" }; }
        return __awaiter(this, void 0, void 0, function () {
            var events;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.queryAllEvents(fromSlot, toSlot, options, derivedAddress)];
                    case 1:
                        events = _a.sent();
                        return [2 /*return*/, events.filter(function (event) { return event.name === eventName; })];
                }
            });
        });
    };
    /**
     * Queries all events for a specific program.
     *
     * @param fromSlot - Optional starting slot.
     * @param toSlot - Optional ending slot.
     * @param options - Options for fetching signatures.
     * @param forDerivedAddress - Whether to query events for the program or the derived address.
     * @returns A promise that resolves to an array of all events with additional metadata.
     */
    SvmCpiEventsClient.prototype.queryAllEvents = function (fromSlot, toSlot, options, derivedAddress) {
        if (options === void 0) { options = { limit: 1000, commitment: "confirmed" }; }
        return __awaiter(this, void 0, void 0, function () {
            var addressToQuery, allSignatures, hasMoreSignatures, currentOptions, signatures, filteredSignatures, eventsWithSlots;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        addressToQuery = derivedAddress || this.programAddress;
                        allSignatures = [];
                        hasMoreSignatures = true;
                        currentOptions = options;
                        _a.label = 1;
                    case 1:
                        if (!hasMoreSignatures) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.rpc
                                .getSignaturesForAddress(addressToQuery, currentOptions)
                                .send()];
                    case 2:
                        signatures = _a.sent();
                        // Signatures are sorted by slot in descending order.
                        allSignatures.push.apply(allSignatures, signatures);
                        // Update options for the next batch. Set "before" to the last fetched signature.
                        if (signatures.length > 0) {
                            currentOptions = __assign(__assign({}, currentOptions), { before: signatures[signatures.length - 1].signature });
                        }
                        if (fromSlot && allSignatures.length > 0 && allSignatures[allSignatures.length - 1].slot < fromSlot) {
                            hasMoreSignatures = false;
                        }
                        hasMoreSignatures = Boolean(hasMoreSignatures && currentOptions.limit && signatures.length === currentOptions.limit);
                        return [3 /*break*/, 1];
                    case 3:
                        filteredSignatures = allSignatures.filter(function (signatureTransaction) {
                            if (fromSlot && signatureTransaction.slot < fromSlot)
                                return false;
                            if (toSlot && signatureTransaction.slot > toSlot)
                                return false;
                            return true;
                        });
                        return [4 /*yield*/, Promise.all(filteredSignatures.map(function (signatureTransaction) { return __awaiter(_this, void 0, void 0, function () {
                                var events;
                                return __generator(this, function (_a) {
                                    switch (_a.label) {
                                        case 0: return [4 /*yield*/, this.readEventsFromSignature(signatureTransaction.signature, options.commitment)];
                                        case 1:
                                            events = _a.sent();
                                            return [2 /*return*/, events.map(function (event) { return (__assign(__assign({}, event), { confirmationStatus: signatureTransaction.confirmationStatus, blockTime: signatureTransaction.blockTime, signature: signatureTransaction.signature, slot: signatureTransaction.slot })); })];
                                    }
                                });
                            }); }))];
                    case 4:
                        eventsWithSlots = _a.sent();
                        return [2 /*return*/, eventsWithSlots.flat()];
                }
            });
        });
    };
    /**
     * Reads events from a transaction signature.
     *
     * @param txSignature - The transaction signature.
     * @param commitment - Commitment level.
     * @returns A promise that resolves to an array of events.
     */
    SvmCpiEventsClient.prototype.readEventsFromSignature = function (txSignature, commitment) {
        if (commitment === void 0) { commitment = "confirmed"; }
        return __awaiter(this, void 0, void 0, function () {
            var txResult;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.rpc
                            .getTransaction(txSignature, { commitment: commitment, maxSupportedTransactionVersion: 0 })
                            .send()];
                    case 1:
                        txResult = _a.sent();
                        if (txResult === null)
                            return [2 /*return*/, []];
                        return [2 /*return*/, this.processEventFromTx(txResult)];
                }
            });
        });
    };
    /**
     * Processes events from a transaction.
     *
     * @param txResult - The transaction result.
     * @returns A promise that resolves to an array of events with their data and name.
     */
    SvmCpiEventsClient.prototype.processEventFromTx = function (txResult) {
        var _a, _b, _c, _d, _e, _f, _g, _h;
        if (!txResult)
            return [];
        var events = [];
        var accountKeys = txResult.transaction.message.accountKeys;
        var messageAccountKeys = __spreadArray([], accountKeys, true);
        // Writable accounts come first, then readonly.
        // See https://docs.anza.xyz/proposals/versioned-transactions#new-transaction-format
        messageAccountKeys.push.apply(messageAccountKeys, ((_c = (_b = (_a = txResult === null || txResult === void 0 ? void 0 : txResult.meta) === null || _a === void 0 ? void 0 : _a.loadedAddresses) === null || _b === void 0 ? void 0 : _b.writable) !== null && _c !== void 0 ? _c : []));
        messageAccountKeys.push.apply(messageAccountKeys, ((_f = (_e = (_d = txResult === null || txResult === void 0 ? void 0 : txResult.meta) === null || _d === void 0 ? void 0 : _d.loadedAddresses) === null || _e === void 0 ? void 0 : _e.readonly) !== null && _f !== void 0 ? _f : []));
        for (var _i = 0, _j = (_h = (_g = txResult.meta) === null || _g === void 0 ? void 0 : _g.innerInstructions) !== null && _h !== void 0 ? _h : []; _i < _j.length; _i++) {
            var ixBlock = _j[_i];
            for (var _k = 0, _l = ixBlock.instructions; _k < _l.length; _k++) {
                var ix = _l[_k];
                var ixProgramId = messageAccountKeys[ix.programIdIndex];
                var singleIxAccount = ix.accounts.length === 1 ? messageAccountKeys[ix.accounts[0]] : undefined;
                if (ixProgramId !== undefined &&
                    singleIxAccount !== undefined &&
                    this.programAddress === ixProgramId &&
                    this.programEventAuthority === singleIxAccount) {
                    var ixData = bs58.decode(ix.data);
                    // Skip the first 8 bytes (assumed header) and encode the rest.
                    var eventData = Buffer.from(ixData.slice(8)).toString("base64");
                    var _m = decodeEvent(this.idl, eventData), name_1 = _m.name, data = _m.data;
                    events.push({ program: this.programAddress, name: name_1, data: data });
                }
            }
        }
        return events;
    };
    /**
     * Finds all FundsDeposited events for a given transaction signature.
     *
     * @param originChainId - The chain ID where the deposit originated.
     * @param txSignature - The transaction signature to search for events.
     * @param commitment - Optional commitment level for the transaction query.
     * @returns A promise that resolves to an array of deposit events for the transaction, or undefined if none found.
     */
    SvmCpiEventsClient.prototype.getDepositEventsFromSignature = function (originChainId, txSignature, commitment) {
        if (commitment === void 0) { commitment = "confirmed"; }
        return __awaiter(this, void 0, void 0, function () {
            var _a, events, txDetails, depositEvents;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        assert(chainIsSvm(originChainId), "Origin chain ".concat(originChainId, " is not an SVM chain"));
                        return [4 /*yield*/, Promise.all([
                                this.readEventsFromSignature(txSignature, commitment),
                                this.rpc
                                    .getTransaction(txSignature, {
                                    commitment: commitment,
                                    maxSupportedTransactionVersion: 0,
                                })
                                    .send(),
                            ])];
                    case 1:
                        _a = _b.sent(), events = _a[0], txDetails = _a[1];
                        depositEvents = events === null || events === void 0 ? void 0 : events.filter(function (event) { return (event === null || event === void 0 ? void 0 : event.name) === "FundsDeposited"; });
                        if (!txDetails || !(depositEvents === null || depositEvents === void 0 ? void 0 : depositEvents.length)) {
                            return [2 /*return*/];
                        }
                        return [2 /*return*/, events.map(function (event) {
                                var unwrappedEventArgs = unwrapEventData(event, [
                                    "depositId",
                                    "outputAmount",
                                ]);
                                var data = unwrappedEventArgs.data;
                                return __assign(__assign({}, data), { depositor: toAddressType(data.depositor, data.originChainId), recipient: toAddressType(data.recipient, data.destinationChainId), exclusiveRelayer: toAddressType(data.exclusiveRelayer, data.destinationChainId), inputToken: toAddressType(data.inputToken, data.originChainId), outputToken: toAddressType(data.outputToken, data.destinationChainId), depositTimestamp: Number(txDetails.blockTime), originChainId: originChainId, messageHash: getMessageHash(data.message), blockNumber: Number(txDetails.slot), txnIndex: 0, txnRef: txSignature, logIndex: 0 });
                            })];
                }
            });
        });
    };
    /**
     * Finds all FilledRelay events for a given transaction signature.
     *
     * @param destinationChainId - The destination chain ID (must be an SVM chain).
     * @param txSignature - The transaction signature to search for events.
     * @returns A promise that resolves to an array of fill events for the transaction, or undefined if none found.
     */
    SvmCpiEventsClient.prototype.getFillEventsFromSignature = function (destinationChainId, txSignature, commitment) {
        if (commitment === void 0) { commitment = "confirmed"; }
        return __awaiter(this, void 0, void 0, function () {
            var _a, events, txDetails, fillEvents;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        assert(chainIsSvm(destinationChainId), "Destination chain ".concat(destinationChainId, " is not an SVM chain"));
                        return [4 /*yield*/, Promise.all([
                                this.readEventsFromSignature(txSignature, commitment),
                                this.rpc
                                    .getTransaction(txSignature, {
                                    commitment: commitment,
                                    maxSupportedTransactionVersion: 0,
                                })
                                    .send(),
                            ])];
                    case 1:
                        _a = _b.sent(), events = _a[0], txDetails = _a[1];
                        fillEvents = events === null || events === void 0 ? void 0 : events.filter(function (event) { return (event === null || event === void 0 ? void 0 : event.name) === "FilledRelay"; });
                        if (!txDetails || !(fillEvents === null || fillEvents === void 0 ? void 0 : fillEvents.length)) {
                            return [2 /*return*/];
                        }
                        return [2 /*return*/, fillEvents.map(function (event) {
                                var unwrappedEventData = unwrapEventData(event, [
                                    "depositId",
                                    "inputAmount",
                                ]);
                                var data = unwrappedEventData.data;
                                return __assign(__assign({}, data), { depositor: toAddressType(data.depositor, data.originChainId), recipient: toAddressType(data.recipient, data.destinationChainId), exclusiveRelayer: toAddressType(data.exclusiveRelayer, data.destinationChainId), inputToken: toAddressType(data.inputToken, data.originChainId), outputToken: toAddressType(data.outputToken, data.destinationChainId), fillTimestamp: Number(txDetails.blockTime), blockNumber: Number(txDetails.slot), txnRef: txSignature, txnIndex: 0, logIndex: 0, destinationChainId: destinationChainId });
                            })];
                }
            });
        });
    };
    SvmCpiEventsClient.prototype.getProgramAddress = function () {
        return this.programAddress;
    };
    SvmCpiEventsClient.prototype.getRpc = function () {
        return this.rpc;
    };
    return SvmCpiEventsClient;
}());
export { SvmCpiEventsClient };
//# sourceMappingURL=eventsClient.js.map