"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractGithubGraphqlCacheStrategy = void 0;
const dequal_1 = require("dequal");
const luxon_1 = require("luxon");
const util_1 = require("../util");
/**
 * Cache strategy handles the caching Github GraphQL items
 * and reconciling them with newly obtained ones from paginated queries.
 */
class AbstractGithubGraphqlCacheStrategy {
    cacheNs;
    cacheKey;
    /**
     * Time period after which a cache record is considered expired.
     */
    static cacheTTLDays = 30;
    /**
     * The time which is used during single cache access cycle.
     */
    now = luxon_1.DateTime.now().toUTC();
    /**
     * Set of all versions which were reconciled
     * during the current cache access cycle.
     */
    reconciledVersions = new Set();
    /**
     * These fields will be persisted.
     */
    items;
    createdAt = this.now;
    /**
     * This flag indicates whether there is any new or updated items
     */
    hasNovelty = false;
    constructor(cacheNs, cacheKey) {
        this.cacheNs = cacheNs;
        this.cacheKey = cacheKey;
    }
    /**
     * Load data previously persisted by this strategy
     * for given `cacheNs` and `cacheKey`.
     */
    async getItems() {
        if (this.items) {
            return this.items;
        }
        let result = {
            items: {},
            createdAt: this.createdAt.toISO(),
        };
        const storedData = await this.load();
        if (storedData) {
            const cacheTTLDuration = {
                hours: AbstractGithubGraphqlCacheStrategy.cacheTTLDays * 24,
            };
            if (!(0, util_1.isDateExpired)(this.now, storedData.createdAt, cacheTTLDuration)) {
                result = storedData;
            }
        }
        this.createdAt = luxon_1.DateTime.fromISO(result.createdAt).toUTC();
        this.items = result.items;
        return this.items;
    }
    /**
     * If package release exists longer than this cache can exist,
     * we assume it won't updated/removed on the Github side.
     */
    isStabilized(item) {
        const unstableDuration = {
            hours: AbstractGithubGraphqlCacheStrategy.cacheTTLDays * 24,
        };
        return (0, util_1.isDateExpired)(this.now, item.releaseTimestamp, unstableDuration);
    }
    /**
     * Process items received from GraphQL page
     * ordered by `releaseTimestamp` in descending order
     * (fresh versions go first).
     */
    async reconcile(items) {
        const cachedItems = await this.getItems();
        let isPaginationDone = false;
        for (const item of items) {
            const { version } = item;
            const oldItem = cachedItems[version];
            // If we reached previously stored item that is stabilized,
            // we assume the further pagination will not yield any new items.
            if (oldItem && this.isStabilized(oldItem)) {
                isPaginationDone = true;
                break;
            }
            // Check if item is new or updated
            if (!oldItem || !(0, dequal_1.dequal)(oldItem, item)) {
                this.hasNovelty = true;
            }
            cachedItems[version] = item;
            this.reconciledVersions.add(version);
        }
        this.items = cachedItems;
        return isPaginationDone;
    }
    /**
     * Handle removed items for packages that are not stabilized
     * and return the list of all items.
     */
    async finalize() {
        const cachedItems = await this.getItems();
        const resultItems = {};
        let hasDeletedItems = false;
        for (const [version, item] of Object.entries(cachedItems)) {
            if (this.isStabilized(item) || this.reconciledVersions.has(version)) {
                resultItems[version] = item;
            }
            else {
                hasDeletedItems = true;
            }
        }
        if (this.hasNovelty || hasDeletedItems) {
            await this.store(resultItems);
        }
        return Object.values(resultItems);
    }
    async store(cachedItems) {
        const cacheRecord = {
            items: cachedItems,
            createdAt: this.createdAt.toISO(),
        };
        await this.persist(cacheRecord);
    }
}
exports.AbstractGithubGraphqlCacheStrategy = AbstractGithubGraphqlCacheStrategy;
//# sourceMappingURL=abstract-cache-strategy.js.map