"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2022 Jonas Lochmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cache = void 0;
const lodash_1 = require("lodash");
const Sequelize = require("sequelize");
const config_1 = require("../../../config");
const list_1 = require("../../../util/list");
const token_1 = require("../../../util/token");
const illegal_state_1 = require("./exception/illegal-state");
const integrity_1 = require("./exception/integrity");
class Cache {
    constructor({ familyId, deviceId, hasFullVersion, database, transaction, connectedDevicesManager }) {
        this.requireSenderDoFullSync = false;
        this.categoriesWithModifiedApps = new Set();
        this.categoriesWithModifiedBaseData = new Set();
        this.categoriesWithModifiedTimeLimitRules = new Set();
        this.categoriesWithModifiedUsedTimes = new Set();
        this.categoriesWithModifiedTasks = new Set();
        this.devicesWithModifiedShowDeviceConnected = new Map();
        this.invalidiateUserList = false;
        this.invalidiateDeviceList = false;
        this.invalidateU2fList = false;
        this.triggeredSyncLevel = 0; // 0 = no, 1 = unimportant, 2 = important
        this.targetedTriggeredSyncLevels = new Map();
        this.getSecondPasswordHashOfParent = (0, lodash_1.memoize)(async (parentId) => {
            const userEntryUnsafe = await this.database.user.findOne({
                where: {
                    familyId: this.familyId,
                    userId: parentId,
                    type: 'parent'
                },
                attributes: ['secondPasswordHash'],
                transaction: this.transaction
            });
            if (!userEntryUnsafe) {
                throw new illegal_state_1.SourceUserNotFoundException();
            }
            return userEntryUnsafe.secondPasswordHash;
        });
        this.getSecondPasswordHashOfChild = (0, lodash_1.memoize)(async (childId) => {
            const userEntryUnsafe = await this.database.user.findOne({
                where: {
                    familyId: this.familyId,
                    userId: childId,
                    type: 'child'
                },
                attributes: ['secondPasswordHash'],
                transaction: this.transaction
            });
            if (!userEntryUnsafe) {
                throw new illegal_state_1.SourceUserNotFoundException();
            }
            if (!userEntryUnsafe.secondPasswordHash) {
                throw new integrity_1.InvalidChildActionIntegrityValue();
            }
            return userEntryUnsafe.secondPasswordHash;
        });
        this.doesCategoryExist = (0, lodash_1.memoize)(async (categoryId) => {
            const categoryEntry = await this.database.category.findOne({
                where: {
                    familyId: this.familyId,
                    categoryId
                },
                transaction: this.transaction
            });
            return !!categoryEntry;
        });
        this.doesUserExist = (0, lodash_1.memoize)(async (userId) => {
            const userEntry = await this.database.user.findOne({
                where: {
                    familyId: this.familyId,
                    userId
                },
                transaction: this.transaction
            });
            return !!userEntry;
        });
        this.isSenderDoFullSyncTrue = () => this.requireSenderDoFullSync;
        this.requireSenderFullSync = () => this.requireSenderDoFullSync = true;
        this.familyId = familyId;
        this.deviceId = deviceId;
        this.hasFullVersion = hasFullVersion || config_1.config.alwaysPro;
        this.database = database;
        this.transaction = transaction;
        this.connectedDevicesManager = connectedDevicesManager;
    }
    incrementTriggeredSyncLevel(newLevel) {
        if (newLevel > this.triggeredSyncLevel)
            this.triggeredSyncLevel = newLevel;
    }
    incrementTargetedTriggeredSyncLevel(deviceId, newLevel) {
        const oldLevel = this.targetedTriggeredSyncLevels.get(deviceId) || 0;
        if (newLevel > oldLevel)
            this.targetedTriggeredSyncLevels.set(deviceId, newLevel);
    }
    async subtransaction(callback) {
        const oldTransaction = this.transaction;
        return this.database.transaction(async (newTransaction) => {
            try {
                this.transaction = newTransaction;
                const result = await callback();
                return result;
            }
            finally {
                this.transaction = oldTransaction;
            }
        }, { transaction: oldTransaction });
    }
    async saveModifiedVersionNumbers() {
        const { database, transaction, familyId } = this;
        if (this.categoriesWithModifiedApps.size > 0) {
            await database.category.update({
                assignedAppsVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId,
                    categoryId: {
                        [Sequelize.Op.in]: (0, list_1.setToList)(this.categoriesWithModifiedApps)
                    }
                },
                transaction
            });
            this.categoriesWithModifiedApps.clear();
        }
        if (this.categoriesWithModifiedBaseData.size > 0) {
            await database.category.update({
                baseVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId,
                    categoryId: {
                        [Sequelize.Op.in]: (0, list_1.setToList)(this.categoriesWithModifiedBaseData)
                    }
                },
                transaction
            });
            this.categoriesWithModifiedBaseData.clear();
        }
        if (this.categoriesWithModifiedTimeLimitRules.size > 0) {
            await database.category.update({
                timeLimitRulesVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId,
                    categoryId: {
                        [Sequelize.Op.in]: (0, list_1.setToList)(this.categoriesWithModifiedTimeLimitRules)
                    }
                },
                transaction
            });
            this.categoriesWithModifiedTimeLimitRules.clear();
        }
        if (this.categoriesWithModifiedUsedTimes.size > 0) {
            await database.category.update({
                usedTimesVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId,
                    categoryId: {
                        [Sequelize.Op.in]: (0, list_1.setToList)(this.categoriesWithModifiedUsedTimes)
                    }
                },
                transaction
            });
            this.categoriesWithModifiedUsedTimes.clear();
        }
        if (this.categoriesWithModifiedTasks.size > 0) {
            await database.category.update({
                taskListVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId,
                    categoryId: {
                        [Sequelize.Op.in]: (0, list_1.setToList)(this.categoriesWithModifiedTasks)
                    }
                },
                transaction
            });
            this.categoriesWithModifiedUsedTimes.clear();
        }
        if (this.invalidiateUserList) {
            await database.family.update({
                userListVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId: this.familyId
                },
                transaction
            });
            this.invalidiateUserList = false;
        }
        if (this.invalidiateDeviceList) {
            await database.family.update({
                deviceListVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId: this.familyId
                },
                transaction
            });
            this.invalidiateDeviceList = false;
        }
        if (this.invalidateU2fList) {
            await database.family.update({
                u2fKeysVersion: (0, token_1.generateVersionId)()
            }, {
                where: {
                    familyId: this.familyId
                },
                transaction
            });
            this.invalidateU2fList = false;
        }
        this.devicesWithModifiedShowDeviceConnected.forEach((showDeviceConnected, deviceId) => {
            this.connectedDevicesManager.notifyShareConnectedChanged({
                familyId: this.familyId,
                deviceId,
                showDeviceConnected
            });
        });
    }
}
exports.Cache = Cache;
//# sourceMappingURL=cache.js.map