var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
export class CacheService {
    constructor(app, storageConfigs) {
        this.app = app;
        // localstorage
        this.Localstorage = this.app.Localstorage
            .instance(!!storageConfigs ? storageConfigs : {
            name: 'SHEETBASE_CACHE',
        });
    }
    instance(storageConfigs) {
        return new CacheService(this.app, storageConfigs);
    }
    cacheTime(cacheTime) {
        // app caching time policy
        // cacheTime = -1 -> disable cache (0)
        // globalCacheTime = 0 and cacheTime = 0 -> disable cache (0)
        // globalCacheTime = 0 and cacheTime # 0 -> cacheTime
        // globalCacheTime # 0 and cacheTime = 0 -> globalCacheTime
        // globalCacheTime # 0 and cacheTime # 0 -> cacheTime
        const { cacheTime: globalCacheTime } = this.app.options;
        return (cacheTime === -1) ? 0 : Math.abs(cacheTime || globalCacheTime || 0);
    }
    set(key, data, cacheTime) {
        return __awaiter(this, void 0, void 0, function* () {
            cacheTime = !!cacheTime ? cacheTime : 1; // default to 1 minute
            yield this.Localstorage.set(key + '__expiration', new Date().getTime() + (cacheTime * 60000));
            return yield this.Localstorage.set(key, data);
        });
    }
    get(key, always = false) {
        return __awaiter(this, void 0, void 0, function* () {
            let expired = true;
            const cachedData = yield this.Localstorage.get(key);
            if (!!cachedData) {
                const cacheExpiration = yield this.Localstorage.get(key + '__expiration');
                if (!cacheExpiration || cacheExpiration > new Date().getTime()) {
                    expired = false;
                }
            }
            if (always) {
                return { data: cachedData, expired };
            }
            else {
                return expired ? null : cachedData;
            }
        });
    }
    getRefresh(key, cacheTime, refresher) {
        return __awaiter(this, void 0, void 0, function* () {
            let data = null;
            cacheTime = this.cacheTime(cacheTime); // get app caching time
            if (cacheTime === 0) {
                data = yield refresher(); // always fresh
            }
            else {
                // get cached
                const { data: cachedData, expired } = yield this.get(key, true);
                if (!expired) {
                    data = cachedData;
                }
                // no cached or expired
                if (!data && !!refresher) {
                    try {
                        data = yield refresher();
                    }
                    catch (error) {
                        // error
                    }
                    if (!!data) {
                        yield this.set(key, data, cacheTime);
                    }
                    else {
                        data = cachedData; // use expired value anyway
                    }
                }
            }
            // return data
            return data;
        });
    }
    iterate(handler) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Localstorage.iterate(handler);
        });
    }
    iterateKeys(handler) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Localstorage.iterateKeys(handler);
        });
    }
    remove(key) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.Localstorage.remove(key + '__expiration');
            return yield this.Localstorage.remove(key);
        });
    }
    removeBulk(keys) {
        return __awaiter(this, void 0, void 0, function* () {
            for (let i = 0; i < keys.length; i++) {
                this.remove(keys[i]);
            }
        });
    }
    removeByPrefix(prefix) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Localstorage.removeByPrefix(prefix);
        });
    }
    removeBySuffix(suffix) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Localstorage.removeBySuffix(suffix);
        });
    }
    flush() {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Localstorage.clear();
        });
    }
    flushExpired() {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.iterateKeys((key) => __awaiter(this, void 0, void 0, function* () {
                if (key.indexOf('__expiration') > -1) {
                    // retrieve expiration
                    const cacheExpiration = yield this.Localstorage.get(key);
                    // remove if expired
                    if (!!cacheExpiration && cacheExpiration <= new Date().getTime()) {
                        yield this.Localstorage.remove(key); // expiration
                        yield this.Localstorage.remove(key.replace('__expiration', '')); // value
                    }
                }
            }));
        });
    }
}
