"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.dispatchAddCategoryApps = dispatchAddCategoryApps;
const Sequelize = require("sequelize");
const category_1 = require("../../../../util/category");
const illegal_state_1 = require("../exception/illegal-state");
const missing_item_1 = require("../exception/missing-item");
const self_limit_1 = require("../exception/self-limit");
async function dispatchAddCategoryApps({ action, cache, fromChildSelfLimitAddChildUserId }) {
    var _a;
    const categoryEntryUnsafe = await cache.database.category.findOne({
        where: {
            familyId: cache.familyId,
            categoryId: action.categoryId
        },
        transaction: cache.transaction,
        attributes: ['childId']
    });
    if (!categoryEntryUnsafe) {
        throw new missing_item_1.MissingCategoryException();
    }
    const { childId } = categoryEntryUnsafe;
    if (fromChildSelfLimitAddChildUserId !== null) {
        if (childId !== fromChildSelfLimitAddChildUserId) {
            throw new self_limit_1.CanNotModifyOtherUsersBySelfLimitationException();
        }
    }
    const categoriesOfSameChild = (await cache.database.category.findAll({
        where: {
            familyId: cache.familyId,
            childId
        },
        attributes: ['categoryId', 'parentCategoryId'],
        transaction: cache.transaction
    })).map((item) => ({
        categoryId: item.categoryId,
        parentCategoryId: item.parentCategoryId
    }));
    const userCategoryIds = categoriesOfSameChild.map((item) => item.categoryId);
    const oldCategories = (await cache.database.categoryApp.findAll({
        attributes: ['categoryId'],
        group: ['categoryId'],
        where: {
            familyId: cache.familyId,
            categoryId: {
                [Sequelize.Op.in]: userCategoryIds
            },
            packageName: {
                [Sequelize.Op.in]: action.packageNames
            }
        },
        transaction: cache.transaction
    })).map((item) => item.categoryId);
    if (fromChildSelfLimitAddChildUserId !== null) {
        for (let i = 0; i < action.packageNames.length; i++) {
            const packageName = action.packageNames[i];
            if (packageName.indexOf('@') !== -1) {
                throw new self_limit_1.SelfLimitationException({
                    staticMessage: 'can not do device specific assignments as child'
                });
            }
        }
        try {
            const parentCategoriesOfTargetCategory = (0, category_1.getCategoryWithParentCategories)(categoriesOfSameChild, action.categoryId);
            const userEntryUnsafe = await cache.database.user.findOne({
                attributes: ['categoryForNotAssignedApps'],
                where: {
                    familyId: cache.familyId,
                    userId: fromChildSelfLimitAddChildUserId
                },
                transaction: cache.transaction
            });
            if (!userEntryUnsafe) {
                throw new illegal_state_1.SourceUserNotFoundException();
            }
            const userEntry = { categoryForNotAssignedApps: userEntryUnsafe.categoryForNotAssignedApps };
            const validatedDefaultCategoryId = (_a = categoriesOfSameChild.find((item) => item.categoryId === userEntry.categoryForNotAssignedApps)) === null || _a === void 0 ? void 0 : _a.categoryId;
            const allowUnassignedElements = validatedDefaultCategoryId !== undefined &&
                parentCategoriesOfTargetCategory.indexOf(validatedDefaultCategoryId) !== -1;
            const assertCanAddApp = async (packageName, isApp) => {
                const categoryAppEntryUnsafe = await cache.database.categoryApp.findOne({
                    attributes: ['categoryId'],
                    where: {
                        familyId: cache.familyId,
                        categoryId: {
                            [Sequelize.Op.in]: userCategoryIds
                        },
                        packageName: packageName
                    },
                    transaction: cache.transaction
                });
                const categoryAppEntry = categoryAppEntryUnsafe ? { categoryId: categoryAppEntryUnsafe.categoryId } : null;
                if (categoryAppEntry === null) {
                    if ((isApp && allowUnassignedElements) || (!isApp)) {
                        // allow
                    }
                    else {
                        throw new self_limit_1.SelfLimitationException({
                            staticMessage: 'can not assign apps without category as child'
                        });
                    }
                }
                else {
                    if (parentCategoriesOfTargetCategory.indexOf(categoryAppEntry.categoryId) !== -1) {
                        // allow
                    }
                    else {
                        throw new self_limit_1.SelfLimitationException({
                            staticMessage: 'can not add app which is not contained in the parent category as child'
                        });
                    }
                }
            };
            for (let i = 0; i < action.packageNames.length; i++) {
                const packageName = action.packageNames[i];
                if (packageName.indexOf(':') !== -1) {
                    await assertCanAddApp(packageName.substring(0, packageName.indexOf(':')), true);
                    await assertCanAddApp(packageName, false);
                }
                else {
                    await assertCanAddApp(packageName, true);
                }
            }
        }
        catch (ex) {
            if (ex instanceof category_1.GetParentCategoriesException) {
                throw new missing_item_1.MissingCategoryException();
            }
            else
                throw ex;
        }
    }
    if (oldCategories.length > 0) {
        await cache.database.categoryApp.destroy({
            where: {
                familyId: cache.familyId,
                categoryId: {
                    [Sequelize.Op.in]: categoriesOfSameChild.map((item) => item.categoryId)
                },
                packageName: {
                    [Sequelize.Op.in]: action.packageNames
                }
            },
            transaction: cache.transaction
        });
    }
    await cache.database.categoryApp.bulkCreate(action.packageNames.map((packageName) => ({
        familyId: cache.familyId,
        categoryId: action.categoryId,
        packageName
    })), {
        transaction: cache.transaction
    });
    oldCategories.forEach((categoryId) => cache.categoriesWithModifiedApps.add(categoryId));
    cache.categoriesWithModifiedApps.add(action.categoryId);
    cache.incrementTriggeredSyncLevel(2);
}
//# sourceMappingURL=addcategoryapps.js.map