import { safeJsonStringify as e } from "../../../functions/utils/serialize.utils.mjs";
import { Logger as t } from "../../../error-handling/logger.mjs";
import { getSequence as n, resolveSync as r } from "../../../functions/utils/resolve-sync.utils.mjs";
import { BlockchainEvent as i } from "../../BlockchainEvent/BlockchainEvent.mjs";
import { BorshCoder as a, EventParser as o, utils as s } from "@coral-xyz/anchor";
//#region libs/shared/common/src/data-structures/event-service/solana/SolanaBlockchainEventEmitter.ts
var c = 1e3, l = 20, u = class {
	_chainId;
	connection;
	programId;
	_latestSlot;
	_initialSlot;
	onEventsProcessed;
	eventProcessors = /* @__PURE__ */ new Set();
	maxPageSize;
	_isReady = !1;
	isServer;
	inProgress;
	anchorParser;
	anchorCoder;
	eventsFetchingMutex;
	constructor(e, t, n, r, i, a, o) {
		this._chainId = e, this.connection = t, this.programId = n, this._initialSlot = r, this.maxPageSize = o, this.isServer = i, this.inProgress = !1, this.eventsFetchingMutex = a;
	}
	get chainId() {
		return this._chainId;
	}
	get latestBlockNumber() {
		return this._latestSlot ?? this._initialSlot;
	}
	get isReady() {
		return this._isReady;
	}
	setIdl(e) {
		try {
			let t = new a(e);
			this.anchorCoder = t, this.anchorParser = new o(this.programId, t);
		} catch (e) {
			t.error("Failed to initialize Anchor EventParser:", e), this.anchorParser = void 0, this.anchorCoder = void 0;
		}
	}
	syncFromAtMost(e) {
		(this._latestSlot === void 0 || e < this._latestSlot) && (this._latestSlot = e);
	}
	advanceLatestBlockNumber(e) {
		e > (this._latestSlot ?? this._initialSlot) && (this._latestSlot = e);
	}
	intervalClear() {
		this._isReady = !1, this.eventProcessors = /* @__PURE__ */ new Set(), this.clear();
	}
	async init() {
		if (this._isReady) throw Error("Already initialized");
		this._isReady = !0, await this.retrieveEvents(this.latestBlockNumber + 1, !1, !0), this.startUpdateListener();
	}
	requireReady() {
		if (!this._isReady) throw Error("Not ready");
	}
	addEventProcessorFunction(e) {
		if (this._isReady) throw Error("Can't add event processor after event emitter initialized");
		this.eventProcessors.add(e);
	}
	async getEventsInRange(e, t) {
		return (await this.getProgramEvents(e, t)).map((e) => i.fromSolanaEvent(e));
	}
	async getLastBlockNumberForEventRequest() {
		let e = await this.connection.getSlot();
		return this.isServer ? Math.max(this.latestBlockNumber, e - 32 + 1) : e;
	}
	async retrieveEvents(e, a = !1, o = !1) {
		let s = e;
		return this.requireReady(), await this.eventsFetchingMutex.runExclusive(async () => {
			try {
				if (this.inProgress && !a) return !1;
				this.inProgress = !0;
				let e = await this.getLastBlockNumberForEventRequest();
				if (e < s) return this.inProgress = !1, !1;
				let t = n(s, e, this.maxPageSize), c = [];
				await r(t.map(({ from: e, to: t }) => async () => {
					let n = await this.getProgramEvents(e, t, o);
					c.push(...n);
				})), c.sort((e, t) => e.slot - t.slot);
				let l = c.map((e) => i.fromSolanaEvent(e));
				return await this.processEvents(l, e), this._latestSlot = e, this.inProgress = !1, !0;
			} catch (e) {
				return t.error("retrieveEvents error:", e), this.inProgress = !1, !1;
			}
		});
	}
	async getProgramEvents(e, n, r = !1) {
		let i = [], a = r ? c : l;
		try {
			let r = await this.connection.getSignaturesForAddress(this.programId, {
				limit: a,
				before: void 0
			}), o = r.length;
			for (; o >= a && r[r.length - 1].slot >= e;) {
				let e = r[r.length - 1], t = await this.connection.getSignaturesForAddress(this.programId, {
					limit: a,
					before: e.signature
				});
				if (t.length === 0) break;
				r.push(...t), o = t.length;
			}
			let s = r.filter((t) => t.slot >= e && t.slot <= n).map(async (e) => {
				try {
					let t = await this.connection.getTransaction(e.signature, {
						commitment: "confirmed",
						maxSupportedTransactionVersion: 0
					});
					return t && t.meta && !t.meta.err ? this.parseTransactionForEvents(t, e.signature, e.slot, t.blockTime || null) : [];
				} catch (n) {
					return t.error(`Failed to get transaction ${e.signature}:`, n), [];
				}
			}), c = await Promise.all(s);
			i.push(...c.flat());
		} catch (e) {
			t.error("Error getting program events:", e);
		}
		return i;
	}
	parseTransactionForEvents(t, n, r, i) {
		let a = this.parseLogsForEvents(t?.meta?.logMessages ?? [], n, r, i), o = this.parseCpiForEvents(t, n, r, i), s = [...a, ...o];
		if (s.length === 0) return [];
		let c = /* @__PURE__ */ new Set();
		return s.filter((t) => {
			let n = `${t.signature}:${t.eventName}:${e(t.data)}`;
			return c.has(n) ? !1 : (c.add(n), !0);
		});
	}
	parseLogsForEvents(e, n, r, i) {
		if (!this.anchorParser) return [];
		try {
			return Array.from(this.anchorParser.parseLogs(e)).map(({ name: e, data: t }) => ({
				signature: n,
				slot: r,
				blockTime: i || 0,
				programId: this.programId,
				data: t,
				eventName: e
			}));
		} catch (e) {
			return t.error("Anchor EventParser failed; falling back to generic parsing:", e), [];
		}
	}
	parseCpiForEvents(e, n, r, i) {
		if (!e || !this.anchorCoder) return [];
		let a = e.meta?.innerInstructions ?? [];
		if (a.length === 0) return [];
		try {
			return a.flatMap((e) => e.instructions ?? []).map((e) => {
				try {
					if (!e?.data) return null;
					let t = s.bytes.bs58.decode(e.data);
					if (t.length <= 8) return null;
					let a = s.bytes.base64.encode(t.subarray(8)), o = this.anchorCoder?.events.decode(a);
					return o ? {
						signature: n,
						slot: r,
						blockTime: i || 0,
						programId: this.programId,
						data: o.data,
						eventName: o.name
					} : null;
				} catch {
					return null;
				}
			}).filter((e) => e !== null);
		} catch (e) {
			return t.error("Failed to parse emit_cpi events:", e), [];
		}
	}
	async processEvents(e, t) {
		let n = (await Promise.all([...this.eventProcessors].map((n) => n(e, t)))).reduce((e, t) => e + t, 0);
		this.onEventsProcessed?.(n);
	}
};
//#endregion
export { u as SolanaBlockchainEventEmitter };
