"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObjectNodeService = exports.ParentNodeType = void 0;
const tslib_1 = require("tslib");
const core_1 = require("@loopback/core");
const repository_1 = require("@loopback/repository");
const lodash_1 = require("lodash");
const helper_1 = require("../../helper");
const application_error_1 = require("../../helper/application-error");
const models_1 = require("../../models");
const repositories_1 = require("../../repositories");
const application_service_1 = require("../application.service");
const content_entity_service_1 = require("../content-entity/content-entity.service");
const object_type_service_1 = require("../object-type.service");
const application_service_2 = require("./../application.service");
var ParentNodeType;
(function (ParentNodeType) {
    ParentNodeType["node"] = "node";
    ParentNodeType["tree"] = "tree";
    ParentNodeType["owner"] = "owner";
    ParentNodeType["namespace"] = "namespace";
})(ParentNodeType = exports.ParentNodeType || (exports.ParentNodeType = {}));
let ObjectNodeService = class ObjectNodeService {
    constructor(objectNodeRepository, objectTypeService, contentEntityService, applicationService) {
        this.objectNodeRepository = objectNodeRepository;
        this.objectTypeService = objectTypeService;
        this.contentEntityService = contentEntityService;
        this.applicationService = applicationService;
        this.applicationService.entityActions['ObjectNodeController.prototype.create'] = {
            entityName: models_1.EntityName.objectNode,
            entityActionType: application_service_1.EntityActionType.create,
        };
        this.applicationService.entityActions['ObjectNodeController.prototype.updateById'] = {
            entityName: models_1.EntityName.objectNode,
            entityActionType: application_service_1.EntityActionType.update,
        };
        this.applicationService.entityActions['ObjectNodeController.prototype.deleteById'] = {
            entityName: models_1.EntityName.objectNode,
            entityActionType: application_service_1.EntityActionType.delete,
        };
        this.applicationService.entityActions['ObjectNodeController.prototype.findById'] = {
            entityName: models_1.EntityName.objectNode,
            entityActionType: application_service_1.EntityActionType.read,
        };
    }
    async migrateType(parentType, previousType, newType) {
        const whereParents = { objectTypeId: parentType };
        const whereChildren = {
            parentNodeId: {
                inq: (await this.objectNodeRepository.find({ where: whereParents })).map((node) => node.id),
            },
            objectTypeId: previousType,
        };
        await this.objectNodeRepository.updateAll({ objectTypeId: newType }, whereChildren);
    }
    async migrateName(previousName, newName, type, parentType) {
        const whereParents = { objectTypeId: parentType };
        const whereChildren = {
            name: previousName,
        };
        if (parentType) {
            whereChildren.parentNodeId = {
                inq: (await this.objectNodeRepository.find({ where: whereParents })).map((node) => node.id),
            };
        }
        if (type) {
            whereParents.objectTypeId = type;
        }
        await this.objectNodeRepository.updateAll({ name: newName }, whereChildren);
    }
    findOrderedNodes(filter) {
        const orderedFilter = lodash_1.merge({ order: ['index ASC'] }, filter);
        return this.objectNodeRepository.find(orderedFilter);
    }
    searchByParentTreeId(treeId, options = {}) {
        return this.searchByOneParentId('Tree', treeId, options);
    }
    searchByParentNamespaceId(treeId, options = {}) {
        return this.searchByOneParentId('Namespace', treeId, options);
    }
    searchByOneParentId(parentType, parentId, options = {}) {
        const where = { ['parent' + parentType + 'Id']: parentId };
        if (options.objectTypeId) {
            where.objectTypeId = options.objectTypeId;
        }
        if (options.name) {
            where.name = options.name;
        }
        if (options.objectTypeIds) {
            where.objectTypeId = { inq: options.objectTypeIds };
        }
        return this.findOrderedNodes({ where });
    }
    searchByTreeIds(treeIds) {
        return this.findOrderedNodes({
            where: { parentTreeId: { inq: treeIds } },
        });
    }
    searchByParentNodeId(parentNodeId, options = {}) {
        return this.searchByOneParentId('Node', parentNodeId, options);
    }
    searchByParentIdsAndObjectTypeId(parentNodeIds, objectTypeId) {
        return this.findOrderedNodes({
            where: { parentNodeId: { inq: parentNodeIds }, objectTypeId },
        });
    }
    async searchOwner(ownerType, ownerName) {
        const types = await this.objectTypeService.getImplementingTypes(ownerType);
        const nodes = await this.findOrderedNodes({
            where: { objectTypeId: { inq: types }, name: ownerName, owner: true },
        });
        if (1 < nodes.length) {
            throw application_error_1.ApplicationError.tooMany({ owner: ownerType, ownerName: ownerName });
        }
        return 1 === nodes.length ? nodes[0] : null;
    }
    async searchNamespace(ownerType, ownerName, namespaceType, namespaceName) {
        const owner = await this.searchOwner(ownerType, ownerName);
        if (!owner) {
            throw application_error_1.ApplicationError.notFound({ owner: ownerType, ownerName: ownerName });
        }
        const namespace = await this.searchNamespaceOfOwnerId(owner.id, namespaceType, namespaceName, {
            owner: ownerType,
            ownerName: ownerName,
        });
        if (!namespace &&
            ownerType === namespaceType &&
            ownerName === namespaceName) {
            return owner;
        }
        return namespace;
    }
    async searchNamespaceOfOwnerId(ownerId, namespaceType, namespaceName, errorContext = {}) {
        const types = await this.objectTypeService.getImplementingTypes(namespaceType);
        const nodes = await this.findOrderedNodes({
            where: {
                objectTypeId: { inq: types },
                name: namespaceName,
                parentOwnerId: ownerId,
                namespace: true,
            },
        });
        if (1 < nodes.length) {
            throw application_error_1.ApplicationError.tooMany(lodash_1.merge({ ownerId: ownerId }, errorContext, {
                namespace: namespaceType,
                namespaceName: namespaceName,
            }));
        }
        return 1 === nodes.length ? nodes[0] : null;
    }
    async searchTreeOfNamespaceId(namespaceId, treeType, treeName, errorContext = {}) {
        const nodes = await this.findOrderedNodes({
            where: {
                objectTypeId: treeType,
                name: treeName,
                parentNamespaceId: namespaceId,
                tree: true,
            },
        });
        if (1 < nodes.length) {
            throw application_error_1.ApplicationError.tooMany(lodash_1.merge({ namespaceId }, errorContext, {
                tree: treeType,
                treeName: treeName,
            }));
        }
        return 1 === nodes.length ? nodes[0] : null;
    }
    async getOrCreateChildren(parentId, objectTypeId, defaultValue, min = 1, ctx = new application_service_1.CurrentContext()) {
        const children = await this.searchByParentNodeId(parentId, { objectTypeId });
        if (defaultValue && children.length < min) {
            while (children.length < min) {
                children.push(await this.add(lodash_1.merge({}, defaultValue, {
                    parentNodeId: parentId,
                    objectTypeId: objectTypeId,
                }), application_service_1.CurrentContext.get(ctx, {}), false, true, true));
            }
        }
        return children;
    }
    async searchTreesOfNamespaceId(namespaceId, treeType, errorContext = {}) {
        const types = await this.objectTypeService.getImplementingTypes(treeType);
        return this.findOrderedNodes({
            where: {
                objectTypeId: { inq: types },
                parentNamespaceId: namespaceId,
                tree: true,
            },
        });
    }
    async searchTree(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName) {
        const namespace = await this.searchNamespace(ownerType, ownerName, namespaceType, namespaceName);
        if (!namespace) {
            throw application_error_1.ApplicationError.notFound({
                namespace: namespaceType,
                namespaceType,
            });
        }
        const types = await this.objectTypeService.getImplementingTypes(treeType);
        const nodes = await this.findOrderedNodes({
            where: {
                objectTypeId: { inq: types },
                name: treeName,
                parentNamespaceId: namespace.id,
                tree: true,
            },
        });
        if (1 < nodes.length) {
            throw application_error_1.ApplicationError.tooMany({
                namespace: namespaceType,
                namespaceName: namespaceName,
                owner: ownerType,
                ownerName: ownerName,
            });
        }
        if (0 === nodes.length &&
            treeType === namespaceType &&
            treeName === namespaceName) {
            return namespace;
        }
        return 1 === nodes.length ? nodes[0] : null;
    }
    async getNode(id, ctx) {
        return ctx.nodeContext.node.getOrSetValue(async () => {
            return this.searchById(id);
        });
    }
    async getOwner(ownerType, ownerName, ctx) {
        return ctx.nodeContext.node.getOrSetValue(async () => {
            return this.searchOwner(ownerType, ownerName);
        });
    }
    async getNamespace(ownerType, ownerName, namespaceType, namespaceName, ctx) {
        return ctx.nodeContext.node.getOrSetValue(async () => {
            return this.searchNamespace(ownerType, ownerName, namespaceType, namespaceName);
        });
    }
    async getTree(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, ctx) {
        return ctx.nodeContext.node.getOrSetValue(async () => {
            return this.searchTree(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName);
        });
    }
    async getANodeOfTree(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, nodeType, nodeName, ctx) {
        return ctx.nodeContext.node.getOrSetValue(async () => {
            const objectNodes = await this.getTreeNodes(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, ctx);
            const types = await this.objectTypeService.getImplementingTypes(nodeType);
            const treeNode = lodash_1.find(objectNodes, (node) => node.name === nodeName && -1 < lodash_1.indexOf(types, node.objectTypeId));
            if (!treeNode) {
                throw application_error_1.ApplicationError.notFound({
                    node: nodeType,
                    nodeName: nodeName,
                    tree: treeType,
                    treeName: treeName,
                    namespace: namespaceType,
                    namespaceName: namespaceName,
                    owner: ownerType,
                    ownerName: ownerName,
                });
            }
            return treeNode;
        });
    }
    async getTreeNodes(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName, ctx) {
        const tree = await ctx.treeContext.treeNode.getOrSetValue(async () => {
            return this.searchTree(ownerType, ownerName, namespaceType, namespaceName, treeType, treeName);
        });
        if (!tree) {
            throw application_error_1.ApplicationError.notFound({
                tree: treeType,
                treeName: treeName,
                namespace: namespaceType,
                namespaceName: namespaceName,
                owner: ownerType,
                ownerName: ownerName,
            });
        }
        return lodash_1.concat(tree, await this.searchByParentTreeId(tree.id));
    }
    async getTreeNode(objectNode, ctx) {
        return ctx.nodeContext.tree.getOrSetValue(async () => {
            return this.searchById(objectNode.parentTreeId);
        });
    }
    searchById(id, filter, options) {
        return this.objectNodeRepository.findById(id, filter, options);
    }
    async checkNameAvailability(objectNode, name, generate = false) {
        const authorizedChars = `A-Z|a-z|0-9|:$\\.\\-_`;
        const regexpName = new RegExp('^[' + authorizedChars + ']+$', 'g');
        if (!name) {
            throw application_error_1.ApplicationError.format('alphanumeric or - or _ or . or $', {
                name: name,
            });
        }
        if (!name.match(regexpName)) {
            if (generate) {
                name = helper_1.toKebabCase(name.normalize('NFD').replace(/[\u0300-\u036f]/g, ''));
                name = name
                    .replace(new RegExp('[^' + authorizedChars + ']', 'g'), '_')
                    .replace(/__+/g, '_');
            }
            if (!name.match(regexpName)) {
                throw application_error_1.ApplicationError.format('alphanumeric or - or _ or . or $', {
                    name: name,
                });
            }
            objectNode.name = name;
        }
        const types = await this.objectTypeService.getImplementingCommonTypes(objectNode.objectTypeId);
        if (!lodash_1.isArray(types)) {
            throw application_error_1.ApplicationError.notFound({ type: objectNode.objectTypeId });
        }
        const otherNodes = await this.findOrderedNodes({
            where: {
                parentTreeId: objectNode.parentTreeId,
                objectTypeId: { inq: types },
                name: name,
            },
        });
        if (otherNodes && 0 < otherNodes.length) {
            return this.resolveConflictName(objectNode, name, generate);
        }
        if (objectNode.tree) {
            const otherTrees = await this.findOrderedNodes({
                where: {
                    parentNamespaceId: objectNode.parentNamespaceId,
                    objectTypeId: { inq: types },
                    name: name,
                    tree: true,
                },
            });
            if (otherTrees && 0 < otherTrees.length) {
                return this.resolveConflictName(objectNode, name, generate);
            }
        }
        if (objectNode.namespace) {
            const otherNamespaces = await this.findOrderedNodes({
                where: {
                    parentOwnerId: objectNode.parentOwnerId,
                    objectTypeId: { inq: types },
                    name: name,
                    namespace: true,
                },
            });
            if (otherNamespaces && 0 < otherNamespaces.length) {
                return this.resolveConflictName(objectNode, name, generate);
            }
        }
        if (objectNode.owner) {
            const otherOwners = await this.findOrderedNodes({
                where: {
                    objectTypeId: { inq: types },
                    name: name,
                    owner: true,
                },
            });
            if (otherOwners && 0 < otherOwners.length) {
                return this.resolveConflictName(objectNode, name, generate);
            }
        }
    }
    async resolveConflictName(objectNode, name, generate = false) {
        if (!generate) {
            throw application_error_1.ApplicationError.conflict({ name: name });
        }
        const nameParts = name.split('_');
        if (1 < nameParts.length &&
            Number.isInteger(Number.parseInt(nameParts[nameParts.length - 1]))) {
            nameParts[nameParts.length - 1] =
                '' + (Number.parseInt(nameParts[nameParts.length - 1]) + 1);
            objectNode.name = nameParts.join('_');
        }
        else {
            objectNode.name = name + '_2';
        }
        return this.checkNameAvailability(objectNode, objectNode.name, generate);
    }
    async checkBrothersCondition(parentNodeId, nodeContext) {
        const objectSubType = nodeContext.objectSubType.value;
        const brothers = await nodeContext.brothers.getOrSetValue(async () => this.searchByParentNodeId(parentNodeId));
        if (objectSubType.mandatories) {
            for (const brotherTypeId of objectSubType.mandatories) {
                if (!lodash_1.some(brothers, (brother) => brother.objectTypeId === brotherTypeId)) {
                    throw application_error_1.ApplicationError.missing({ objectType: brotherTypeId });
                }
            }
        }
        if (objectSubType.exclusions) {
            for (const brotherTypeId of objectSubType.exclusions) {
                if (lodash_1.some(brothers, (brother) => brother.objectTypeId === brotherTypeId)) {
                    throw application_error_1.ApplicationError.incompatible({ objectType: brotherTypeId });
                }
            }
        }
        if (Number.isInteger(objectSubType.max) &&
            brothers.filter((brother) => brother.objectTypeId === objectSubType.subObjectTypeId).length >= objectSubType.max) {
            throw application_error_1.ApplicationError.tooMany({
                objectType: objectSubType.subObjectTypeId,
            });
        }
    }
    async checkSubTypesCondition(objectNode, nodeContext, ctx) {
        const objectType = nodeContext.objectType.value;
        if (objectType === null || objectType === void 0 ? void 0 : objectType.objectSubTypes) {
            for (const objectSubType of objectType.objectSubTypes) {
                if (objectSubType.min && 0 < objectSubType.min) {
                    for (let i = 0; i < objectSubType.min; i++) {
                        try {
                            await this.add({
                                title: objectSubType.title
                                    ? objectSubType.title
                                    : objectSubType.name,
                                objectTypeId: objectSubType.subObjectTypeId,
                                parentNodeId: objectNode.id,
                            }, application_service_1.CurrentContext.get(ctx, {
                                nodeContext: {
                                    parentType: new application_service_1.ExpectedValue(objectType),
                                    parent: new application_service_1.ExpectedValue(objectNode),
                                    objectSubType: new application_service_1.ExpectedValue(objectSubType),
                                },
                            }), false, true, true);
                        }
                        catch (error) {
                            console.trace(error);
                        }
                    }
                }
            }
        }
    }
    async add(objectNode, ctx, byPassCheck = false, autoGenerateChildren = true, resolveNameConflict = false) {
        const nodeContext = ctx.nodeContext;
        //let objectNode = clone(objectNodePosted);
        let objectNodeForUpdate = objectNode;
        let objectType = null;
        if (!objectNode.name && !objectNode.title && objectNode.objectTypeId) {
            objectType = await nodeContext.objectType.getOrSetValue(async () => {
                return this.objectTypeService.searchById(objectNode.objectTypeId);
            });
            if (objectType === null || objectType === void 0 ? void 0 : objectType.title) {
                objectNode.title = objectType.title;
            }
        }
        if (!objectNode.name && objectNode.title) {
            objectNode.name = helper_1.toKebabCase(objectNode.title);
        }
        if (!objectNode.title && objectNode.name) {
            objectNode.title = helper_1.toStartCase(objectNode.name);
        }
        if (!byPassCheck) {
            if (!objectNode.name) {
                throw application_error_1.ApplicationError.missingParameter('name');
            }
            if (!objectNode.objectTypeId) {
                throw application_error_1.ApplicationError.missingParameter('objectTypeId');
            }
            if (!objectNode.parentNodeId) {
                throw application_error_1.ApplicationError.missingParameter('parentNodeId');
            }
            const parent = await nodeContext.parent.getOrSetValue(async () => {
                return this.searchById(objectNode.parentNodeId);
            });
            if (!parent) {
                throw application_error_1.ApplicationError.notFound({ parent: objectNode.parentNodeId });
            }
            const parentType = await nodeContext.parentType.getOrSetValue(async () => {
                return this.objectTypeService.searchById(parent.objectTypeId);
            });
            if (!parentType) {
                throw application_error_1.ApplicationError.corruptedData({
                    parent: objectNode.parentNodeId,
                    parentType: parent.objectTypeId,
                });
            }
            const objectSubType = await nodeContext.objectSubType.getOrSetValue(async () => {
                return lodash_1.find(parentType.objectSubTypes, (subType) => {
                    return subType.subObjectTypeId === objectNode.objectTypeId;
                });
            });
            if (!objectSubType) {
                throw application_error_1.ApplicationError.unauthorizedValue({
                    objectType: objectNode.objectTypeId,
                });
            }
            await this.checkBrothersCondition(objectNode.parentNodeId, nodeContext);
            objectType = await nodeContext.objectType.getOrSetValue(async () => {
                return this.objectTypeService.searchById(objectNode.objectTypeId);
            });
            if (!objectType) {
                throw application_error_1.ApplicationError.notFound({ objectType: objectNode.objectTypeId });
            }
            objectNode.parentACLId =
                !parent.acl && parent.parentACLId ? parent.parentACLId : parent.id;
            objectNode.aclList = !parent.aclList
                ? [parent.id]
                : objectNode.parentACLId === parent.id
                    ? lodash_1.concat(parent.aclList, [objectNode.parentACLId])
                    : parent.aclList;
            objectNode.parentOwnerId =
                !parent.owner && parent.parentOwnerId
                    ? parent.parentOwnerId
                    : parent.id;
            objectNode.parentTreeId =
                !parent.tree && parent.parentTreeId ? parent.parentTreeId : parent.id;
            objectNode.parentNamespaceId =
                !parent.namespace && parent.parentNamespaceId
                    ? parent.parentNamespaceId
                    : parent.id;
            objectNode.owner = objectSubType.owner;
            objectNode.acl = !!objectNode.owner || !!objectSubType.acl;
            objectNode.namespace = !!objectNode.owner || !!objectSubType.namespace;
            objectNode.tree = !!objectNode.namespace || !!objectSubType.tree;
            await this.checkNameAvailability(objectNode, objectNode.name, resolveNameConflict);
            objectNodeForUpdate = lodash_1.pick(objectNode, this.getPropertiesKeys(objectType, [
                'name',
                'title',
                'objectTypeId',
                'parentNodeId',
                'parentACLId',
                'aclList',
                'parentOwnerId',
                'parentTreeId',
                'parentNamespaceId',
                'owner',
                'acl',
                'namespace',
                'tree',
            ]));
        }
        const brothers = await nodeContext.brothers.getOrSetValue(async () => this.searchByParentNodeId(objectNode.parentNodeId));
        objectNodeForUpdate.index = 10;
        for (const brother of brothers) {
            if (brother.index && brother.index + 10 > objectNodeForUpdate.index) {
                objectNodeForUpdate.index = brother.index + 10;
            }
        }
        const result = await this.objectNodeRepository.create(objectNodeForUpdate);
        const changes = await this.contentEntityService.manageContent(objectType === null || objectType === void 0 ? void 0 : objectType.contentType, result, objectNode);
        if (changes) {
            await this.objectNodeRepository.updateById(result.id, result);
        }
        if (autoGenerateChildren) {
            await this.checkSubTypesCondition(result, nodeContext, ctx);
        }
        return result;
    }
    getPropertiesKeys(objectType, objectKeys = ['name', 'title']) {
        var _a;
        let finalObjectKeys = objectKeys;
        if ((_a = objectType.definition) === null || _a === void 0 ? void 0 : _a.properties) {
            finalObjectKeys = objectKeys.concat(Object.keys(objectType.definition.properties));
        }
        return finalObjectKeys;
    }
    async modifyById(id, objectNode, ctx, byPassCheck = false) {
        let objectNodeForUpdate = objectNode;
        if (!byPassCheck) {
            const node = await this.getNode(id, ctx);
            if (!node) {
                throw application_error_1.ApplicationError.notFound({ object: id });
            }
            if ('name' in objectNode && node.name !== objectNode.name) {
                if (!objectNode.name) {
                    throw application_error_1.ApplicationError.missingParameter('name');
                }
                await this.checkNameAvailability(node, objectNode.name);
            }
            const objectType = await this.objectTypeService.searchById(node.objectTypeId);
            if (!objectType) {
                throw application_error_1.ApplicationError.corruptedData({
                    object: id,
                    objectType: node.objectTypeId,
                });
            }
            objectNodeForUpdate = lodash_1.pick(objectNode, this.getPropertiesKeys(objectType));
        }
        await this.objectNodeRepository.updateById(id, objectNodeForUpdate);
        const result = await this.objectNodeRepository.findById(id);
        const changes = await this.contentEntityService.manageContent((await this.objectTypeService.searchById(result.objectTypeId))
            .contentType, result, objectNode);
        if (changes) {
            await this.objectNodeRepository.updateById(result.id, result);
        }
        return result;
    }
    getParentIdKey(parentType) {
        return ('parent' +
            parentType.charAt(0).toUpperCase() +
            parentType.substr(1) +
            'Id');
    }
    async deleteContentNodes(where, ctx) {
        const contentTypes = await this.objectTypeService.getTypeWithContent(ctx);
        if (0 < Object.keys(contentTypes).length) {
            const deletedContentObjects = await this.findOrderedNodes({
                where: lodash_1.merge(where, {
                    objectTypeId: { inq: Object.keys(contentTypes) },
                }),
            });
            if (0 < deletedContentObjects.length) {
                for (const contentType of this.contentEntityService.contentTypes) {
                    const deletedObjects = deletedContentObjects.filter((deletedObject) => {
                        return (contentType ===
                            contentTypes[deletedObject.objectTypeId].contentType);
                    });
                    if (0 < deletedObjects.length) {
                        await this.contentEntityService.deleteContents(contentType, deletedObjects);
                    }
                }
            }
        }
    }
    async removeByParent(parentType, id, ctx) {
        const parentIdKey = this.getParentIdKey(parentType);
        const whereClause = lodash_1.merge({
            [parentIdKey]: id,
        }, 'node' === parentType ? {} : { [parentType]: true });
        const subChildrenNodes = await this.findOrderedNodes({
            where: whereClause,
        });
        for (const subChildNode of subChildrenNodes) {
            await this.removeByParent(parentType, subChildNode.id, ctx);
        }
        await this.deleteContentNodes({
            [parentIdKey]: id,
        }, ctx);
        await this.objectNodeRepository.deleteAll({
            [parentIdKey]: id,
        });
    }
    async removeById(id, ctx) {
        const node = await this.getNode(id, ctx);
        if (!node) {
            throw application_error_1.ApplicationError.notFound({ object: id });
        }
        if (node.owner) {
            await this.removeByParent(ParentNodeType.owner, node.id, ctx);
        }
        else if (node.namespace) {
            await this.removeByParent(ParentNodeType.namespace, node.id, ctx);
        }
        else if (node.tree) {
            await this.removeByParent(ParentNodeType.tree, node.id, ctx);
        }
        else {
            await this.removeByParent(ParentNodeType.node, node.id, ctx);
        }
        await this.deleteContentNodes({
            id: id,
        }, ctx);
        await this.objectNodeRepository.deleteById(id);
        return;
    }
    async moveTo(child, parent) {
        const newTreeId = (parent.tree
            ? parent.id
            : parent.parentTreeId);
        const newNamespaceId = (parent.namespace
            ? parent.id
            : parent.parentNamespaceId);
        const newACLId = (parent.acl
            ? parent.id
            : parent.parentACLId);
        const newOwnerId = (parent.owner
            ? parent.id
            : parent.parentOwnerId);
        if (child.parentNamespaceId !== newNamespaceId) {
            // can't move from namespace to another
            throw application_error_1.ApplicationError.notImplemented({
                method: 'moveTo',
                context: 'change namespace',
            });
        }
        if (child.parentOwnerId !== newOwnerId) {
            // can't move from owner to another
            throw application_error_1.ApplicationError.notImplemented({
                method: 'moveTo',
                context: 'change owner',
            });
        }
        if (child.parentACLId !== newACLId) {
            // can't move from ACL to another
            throw application_error_1.ApplicationError.notImplemented({
                method: 'moveTo',
                context: 'change ACL',
            });
        }
        if (child.parentTreeId !== newTreeId) {
            const children = await this.loadChildrenNodes(child);
            for (const node of children) {
                node.parentACLId = newACLId;
                node.parentNamespaceId = newNamespaceId;
                node.parentTreeId = newTreeId;
                node.parentOwnerId = newOwnerId;
                await this.checkNameAvailability(node, node.name, true);
                const objectNodeForUpdate = {
                    name: node.name,
                    parentACLId: newACLId,
                    parentOwnerId: newOwnerId,
                    parentNamespaceId: newNamespaceId,
                    parentTreeId: newTreeId,
                };
                if (node.id === child.id) {
                    objectNodeForUpdate.parentNodeId = parent.id;
                }
                await this.objectNodeRepository.updateById(node.id, objectNodeForUpdate);
            }
            return;
        }
        await this.objectNodeRepository.updateById(child.id, {
            parentNodeId: parent.id,
            parentACLId: newACLId,
            parentOwnerId: newOwnerId,
            parentNamespaceId: newNamespaceId,
            parentTreeId: newTreeId,
        });
    }
    async loadChildrenNodes(rootTreeNode) {
        let children;
        if (rootTreeNode.tree) {
            children = await this.searchByParentTreeId(rootTreeNode.id);
        }
        else {
            const candidateChildren = await this.searchByParentTreeId(rootTreeNode.parentTreeId);
            children = this.selectChildrenFromCandidatesList(rootTreeNode, candidateChildren);
        }
        return lodash_1.concat(rootTreeNode, children);
    }
    selectChildrenFromCandidatesList(parent, candidates, children = []) {
        for (const child of candidates) {
            if (child.parentNodeId === parent.id) {
                children.push(child);
                this.selectChildrenFromCandidatesList(child, candidates, children);
            }
        }
        return children;
    }
};
ObjectNodeService = tslib_1.__decorate([
    tslib_1.__param(0, repository_1.repository(repositories_1.ObjectNodeRepository)),
    tslib_1.__param(1, core_1.service(object_type_service_1.ObjectTypeService)),
    tslib_1.__param(2, core_1.service(content_entity_service_1.ContentEntityService)),
    tslib_1.__param(3, core_1.service(application_service_2.ApplicationService)),
    tslib_1.__metadata("design:paramtypes", [repositories_1.ObjectNodeRepository,
        object_type_service_1.ObjectTypeService,
        content_entity_service_1.ContentEntityService,
        application_service_2.ApplicationService])
], ObjectNodeService);
exports.ObjectNodeService = ObjectNodeService;
//# sourceMappingURL=object-node.service.js.map