import { zeroAddress as e } from "../../constants/protocol.constants.mjs";
import { StorageKeys as t } from "../../types/cache.types.mjs";
import { lowerCaseIncludes as n } from "../../functions/utils/caseInsensitive.utils.mjs";
import { API as r } from "../../API/API.mjs";
import { getERC20Token as i, isNFTToken as a, overlayTokenWithRegistryData as o } from "../../functions/utils/erc20tokenFunctions.mjs";
import { EVENTS_INITIAL_BLOCKS as s } from "../../constants/events.constants.mjs";
import "../../constants/index.mjs";
import { Logger as c } from "../../error-handling/logger.mjs";
import { getStateKey as l } from "../../functions/utils/string.utils.mjs";
import { tokenVisibilityStorage as u } from "./token-visibility-db.mjs";
import { createJsonRpcProvider as d } from "../../functions/utils/create-provider.mjs";
import "../../functions/index.mjs";
import { Mutex as f } from "async-mutex";
import { ethers as p } from "ethers";
import { get as m, update as h } from "idb-keyval";
var g = new class {
	fetchedTokens = {};
	updateMutexes = {};
	transferEventTopic = p.id("Transfer(address,address,uint256)");
	chunkSize = 2e3;
	getMutex(e) {
		return this.updateMutexes[e] || (this.updateMutexes[e] = new f()), this.updateMutexes[e];
	}
	withRegistryOverlay(e, t) {
		let n = i(e.erc20TokenAddress, t);
		return n ? {
			...e,
			name: n.name,
			symbol: n.symbol,
			logoURI: n.logoURI,
			decimals: n.decimals
		} : e;
	}
	async getPublicTokens(e, n) {
		let r = l(n, e);
		return ((await m(t.EVENTS_PUBLIC_TOKENS))?.[r] || []).map((t) => o(t, e));
	}
	getFetchedTokens(e, t) {
		let n = l(t, e);
		return (this.fetchedTokens[n] || []).map((t) => o(t, e));
	}
	async fetchAndUpdatePublicTokens(e, r) {
		let i = l(e, r);
		await this.getMutex(i).runExclusive(async () => {
			try {
				let a = (await this.getPublicTokens(r, e)).map((e) => e.erc20TokenAddress), { toEvents: o, fromEvents: s, latestBlock: c } = await this.getEvents(e, r), l = [...o, ...s], { erc20Tokens: d } = await this.fetchPublicTokens(l, r, a), f = await u.hideSpamsAndFetchHiddenTokensAddresses(r, d), p = d.filter((e) => !n(f, e.erc20TokenAddress));
				await h(t.EVENTS_PUBLIC_TOKENS, (e = {}) => {
					let t = e[i] || [], r = t.map((e) => e.erc20TokenAddress), a = p.filter((e) => !n(r, e.erc20TokenAddress));
					t.push(...a);
					let c = this.updateTokenIds(s, o, t), l = {
						...e,
						[i]: c
					};
					return this.fetchedTokens = l, l;
				}), c && await this.updateLastProcessedBlock(r, e, c);
			} catch (e) {
				c.error("public tokens db error", e);
			}
		});
	}
	async getLastProcessedBlock(e, n) {
		return (await m(t.PUBLIC_TOKENS_LAST_PROCESSED_BLOCKS) || {})[l(n, e)] || s[e];
	}
	async updateLastProcessedBlock(e, n, r) {
		await h(t.PUBLIC_TOKENS_LAST_PROCESSED_BLOCKS, (t = {}) => {
			let i = l(n, e);
			return {
				...t,
				[i]: r
			};
		});
	}
	async getEvents(e, t) {
		try {
			let n = d(t), r = await this.getLastProcessedBlock(t, e) + 1, i = await n.getBlockNumber(), a = p.zeroPadValue(e.toLowerCase(), 32), o = [], s = [];
			for (let e = r; e <= i; e += this.chunkSize) {
				let t = Math.min(e + this.chunkSize - 1, i), r = {
					topics: [
						this.transferEventTopic,
						null,
						a
					],
					fromBlock: e,
					toBlock: t
				}, c = {
					topics: [
						this.transferEventTopic,
						a,
						null
					],
					fromBlock: e,
					toBlock: t
				}, [l, u] = await Promise.all([n.getLogs(r), n.getLogs(c)]);
				o.push(...l), s.push(...u);
			}
			return {
				toEvents: o,
				fromEvents: s,
				latestBlock: i
			};
		} catch (n) {
			return c.error("Error fetching events:", n, "chainId", t, "walletAddress", e), {
				toEvents: [],
				fromEvents: [],
				latestBlock: void 0
			};
		}
	}
	updateTokenIds(e, t, n) {
		let r = /* @__PURE__ */ new Map();
		return n.forEach((e) => {
			if (a(e) && e.tokenIds) {
				let t = e.erc20TokenAddress.toLowerCase();
				r.set(t, [...e.tokenIds]);
			}
		}), t.forEach((e) => {
			if (e.topics.length <= 3) return;
			let t = e.address.toLowerCase(), n = BigInt(e.topics[3]).toString();
			r.has(t) || r.set(t, []), r.get(t)?.push(n);
		}), e.forEach((e) => {
			if (e.topics.length <= 3) return;
			let t = e.address.toLowerCase(), n = r.get(t);
			if (n?.length) {
				let t = BigInt(e.topics[3]).toString(), r = n.indexOf(t);
				r !== -1 && n.splice(r, 1);
			}
		}), n.forEach((e) => {
			if (a(e)) {
				let t = e.erc20TokenAddress.toLowerCase(), n = r.get(t);
				e.tokenIds = n?.length && n.length > 0 ? n : void 0;
			}
		}), n;
	}
	async fetchPublicTokens(t, i, a) {
		try {
			let o = [e, ...new Set(t.map((e) => e.address))].filter((e) => e && !n(a, e));
			return o.length ? { erc20Tokens: (await r.tokensInfoCall(i, o)).filter((e) => !!e) } : { erc20Tokens: [] };
		} catch (e) {
			return c.error("Error fetching public tokens:", e), { erc20Tokens: [] };
		}
	}
}();
//#endregion
export { g as eventsPublicTokensDB };
