import logger_default from "../logger.js";
import differenceInDays from "../../../../../date-fns/esm/differenceInDays/index.js";
import getUnixTime from "../../../../../date-fns/esm/getUnixTime/index.js";
import parseISO from "../../../../../date-fns/esm/parseISO/index.js";
import sub from "../../../../../date-fns/esm/sub/index.js";
import PackedBloomFilter from "./utils/bloom-filter-packed.js";
//#region node_modules/@whotracksme/reporting/reporting/src/request/qs-whitelist2.js
/**
* WhoTracks.Me
* https://whotracks.me/
*
* Copyright 2017-present Ghostery GmbH. All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
var STORAGE_CONFIG_KEY = "qs_config";
var STORAGE_BLOOM_FILTER_KEY = "qs_bloom_filter";
async function fetchPackedBloomFilter(url) {
	const response = await fetch(url);
	if (!response.ok) throw new Error(response.error);
	return await response.arrayBuffer();
}
var QSWhitelist2 = class {
	constructor({ storage, CDN_BASE_URL, LOCAL_BASE_URL, networkFetchEnabled }) {
		this.bloomFilter = null;
		this.localSafeKey = {};
		this.storage = storage;
		this.CDN_BASE_URL = CDN_BASE_URL;
		this.LOCAL_BASE_URL = LOCAL_BASE_URL;
		this.networkFetchEnabled = networkFetchEnabled !== false;
		this.initPromise = new Promise((resolve) => {
			this.initPromiseResolver = resolve;
		});
	}
	async init() {
		try {
			const { version, localSafeKey } = await this.storage.get(STORAGE_CONFIG_KEY);
			const buffer = await this.storage.get(STORAGE_BLOOM_FILTER_KEY);
			this.bloomFilter = new PackedBloomFilter(buffer);
			this.version = version;
			this.localSafeKey = localSafeKey || {};
			logger_default.debug(`[QSWhitelist2] Bloom filter loaded version ${version}`);
		} catch (e) {
			logger_default.info("[QSWhitelist2] Failed loading filter from local");
		}
		if (this.bloomFilter === null) try {
			const update = await this._fetchUpdateURL();
			await this._fullUpdate(update.version);
		} catch (e) {
			logger_default.error("[QSWhitelist2] Error fetching bloom filter from remote", e);
			this.networkFetchEnabled = false;
			try {
				await this._fullUpdate((await this._fetchUpdateURL()).version);
			} catch (e2) {
				logger_default.info("[QSWhitelist2] failed to load bloom filter");
				const n = 1e3;
				const k = 10;
				const buffer = /* @__PURE__ */ new ArrayBuffer(4005);
				const view = new DataView(buffer);
				view.setUint32(0, n, false);
				view.setUint8(4, k, false);
				this.bloomFilter = new PackedBloomFilter(buffer);
			}
		}
		else try {
			await this._checkForUpdates();
		} catch (e) {
			logger_default.error("[QSWhitelist2] Error fetching bloom filter updates from remote", e);
		}
		this.initPromiseResolver();
	}
	async _fetchUpdateURL() {
		const url = this.networkFetchEnabled ? `${this.CDN_BASE_URL}/update.json.gz` : `${this.LOCAL_BASE_URL}/update.json`;
		const request = await fetch(url);
		if (!request.ok) throw new Error(request.error);
		return request.json();
	}
	async _fullUpdate(version) {
		const buffer = await fetchPackedBloomFilter(this.networkFetchEnabled ? `${this.CDN_BASE_URL}/${version}/bloom_filter.gz` : `${this.LOCAL_BASE_URL}/bloom_filter.dat`);
		this.bloomFilter = new PackedBloomFilter(buffer);
		this.version = version;
		logger_default.debug(`[QSWhitelist2] Bloom filter fetched version ${version}`);
		await this._persistBloomFilter();
	}
	async _checkForUpdates() {
		const { version, useDiff } = await this._fetchUpdateURL();
		if (version === this.version) {
			logger_default.debug("[QSWhitelist2] Bloom filter is up-to-date");
			return;
		}
		this._cleanLocalSafekey();
		if (useDiff === true && differenceInDays(parseISO(this.version), parseISO(version)) === -1) {
			logger_default.debug(`[QSWhitelist2] Updating bloom filter to version ${version} from diff file`);
			const buffer = await fetchPackedBloomFilter(`${this.CDN_BASE_URL}/${version}/bf_diff_1.gz`);
			this.bloomFilter.update(buffer);
			this.version = version;
			await this._persistBloomFilter();
			return;
		}
		logger_default.debug(`[QSWhitelist2] Updating bloom filter to version ${version}`);
		await this._fullUpdate(version);
	}
	async _persistBloomFilter() {
		if (this.bloomFilter !== null) {
			await this.storage.set(STORAGE_CONFIG_KEY, {
				version: this.version,
				localSafeKey: this.localSafeKey
			});
			await this.storage.set(STORAGE_BLOOM_FILTER_KEY, this.bloomFilter.data.buffer);
		}
	}
	_cleanLocalSafekey() {
		const cutoff = getUnixTime(sub(/* @__PURE__ */ new Date(), { days: 7 }));
		Object.keys(this.localSafeKey).forEach((domain) => {
			Object.keys(this.localSafeKey[domain]).forEach((key) => {
				if (this.localSafeKey[domain][key] < cutoff) delete this.localSafeKey[domain][key];
			});
			if (Object.keys(this.localSafeKey[domain]).length === 0) delete this.localSafeKey[domain];
		});
	}
	async destroy() {
		await this._persistBloomFilter();
	}
	isUpToDate() {
		return this.isReady();
	}
	isReady() {
		return this.bloomFilter !== null;
	}
	isTrackerDomain(domain) {
		if (!this.isReady()) return false;
		return this.bloomFilter.testSingle(`d${domain}`);
	}
	shouldCheckDomainTokens(domain) {
		if (!this.isReady()) return false;
		return this.bloomFilter.testSingle(`c${domain}`);
	}
	isSafeKey(domain, key) {
		if (!this.isReady()) return true;
		if (this.bloomFilter.testSingle(`k${domain}${key}`)) return true;
		if (this.localSafeKey[domain] && this.localSafeKey[domain][key]) return true;
		return false;
	}
	isSafeToken(domain, token) {
		if (!this.isReady()) return true;
		return this.bloomFilter.testSingle(`t${token}`);
	}
	isUnsafeKey() {
		return false;
	}
	addSafeKey(domain, key) {
		if (!this.localSafeKey[domain]) this.localSafeKey[domain] = {};
		this.localSafeKey[domain][key] = getUnixTime(/* @__PURE__ */ new Date());
	}
	addSafeToken(tracker, token) {
		if (!this.isTrackerDomain(tracker)) this.bloomFilter.addSingle(`d${tracker}`);
		if (!this.shouldCheckDomainTokens(tracker)) this.bloomFilter.addSingle(`c${tracker}`);
		this.bloomFilter.addSingle(`t${token}`);
	}
	getVersion() {
		return { day: this.version };
	}
};
//#endregion
export { QSWhitelist2 as default };
