"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActionEntityService = void 0;
const tslib_1 = require("tslib");
/* eslint-disable @typescript-eslint/no-explicit-any */
const core_1 = require("@loopback/core");
const lodash_1 = require("lodash");
const entity_name_1 = require("../../models/entity-name");
const object_node_service_1 = require("../object-node/object-node.service");
const application_error_1 = require("./../../helper/application-error");
const extension_description_1 = require("./../../integration/extension-description");
const object_tree_service_1 = require("./../object-tree/object-tree.service");
const object_type_service_1 = require("./../object-type.service");
let ActionEntityService = class ActionEntityService {
    constructor(objectNodeService, objectTreeService, objectTypeService) {
        this.objectNodeService = objectNodeService;
        this.objectTreeService = objectTreeService;
        this.objectTypeService = objectTypeService;
        this.methodsAndViews = {};
    }
    getTreatmentDescription() {
        const treatments = [];
        for (const entityType in this.methodsAndViews) {
            for (const methodOrViewId in this.methodsAndViews[entityType]) {
                for (const methodOrView of this.methodsAndViews[entityType][methodOrViewId]) {
                    if (lodash_1.isString(methodOrView.description)) {
                        treatments.push(new extension_description_1.TreatmentDescription(methodOrView.providerId, methodOrView.serviceId, entityType +
                            '.' +
                            methodOrViewId +
                            '(): ' +
                            methodOrView.description));
                    }
                    else if (lodash_1.isFunction(methodOrView.description)) {
                        treatments.push(methodOrView.description());
                    }
                    else {
                        treatments.push(methodOrView.description);
                    }
                }
            }
        }
        return treatments;
    }
    async getMethodAuthorizationContext(entityType, args, ctx) {
        const method = await this.getMethod(entityType, args[0], args[1], ctx);
        if (method) {
            return method.accessRightsScope;
        }
        throw application_error_1.ApplicationError.notImplemented({
            entityType,
            methodId: args[1],
            entityId: args[0],
        });
    }
    async getViewAuthorizationContext(entityType, args, ctx) {
        const view = await this.getView(entityType, args[0], args[1], ctx);
        if (view) {
            return view.accessRightsScope;
        }
        throw application_error_1.ApplicationError.notImplemented({
            entityType,
            viewId: args[1],
            entityId: args[0],
        });
    }
    registerNewMethodOrView(entityType, methodOrViewId, methodOrView) {
        if (!this.methodsAndViews[entityType]) {
            this.methodsAndViews[entityType] = {};
        }
        if (!this.methodsAndViews[entityType][methodOrViewId]) {
            this.methodsAndViews[entityType][methodOrViewId] = [];
        }
        this.methodsAndViews[entityType][methodOrViewId].push(methodOrView);
    }
    registerNewMethodFunction(functionProviderId, functionServiceId, functionDescription, entityType, methodId, objectType, actionsFunction, functionAccessRightsScope = 'read') {
        const objectTypeService = this.objectTypeService;
        this.registerNewMethodOrView(entityType, methodId, new (class {
            constructor() {
                this.accessRightsScope = 'read';
                this.providerId = functionProviderId;
                this.serviceId = functionServiceId;
                this.description = new extension_description_1.TreatmentDescription(this.providerId, this.serviceId, entityType +
                    '.' +
                    objectType +
                    '.' +
                    methodId +
                    '(): ' +
                    functionDescription);
                this.accessRightsScope = functionAccessRightsScope;
            }
            async runMethod(entity, args, ctx) {
                if (await this.hasMethod(entity, ctx)) {
                    return actionsFunction(entity, args, ctx);
                }
                throw application_error_1.ApplicationError.notImplemented({
                    entityType,
                    methodId,
                    entityId: entity.id,
                });
            }
            async generateView(entity, args, ctx) {
                throw application_error_1.ApplicationError.notImplemented({
                    entityType,
                    viewId: methodId,
                    entityId: entity.id,
                });
            }
            async hasMethod(entity, ctx) {
                var _a;
                const objectTypeId = entity_name_1.EntityName.objectTree === entityType
                    ? (_a = entity.treeNode) === null || _a === void 0 ? void 0 : _a.objectTypeId : entity.objectTypeId;
                const implementedTypes = await objectTypeService.getImplementedTypes(objectTypeId);
                return -1 < lodash_1.indexOf(implementedTypes, objectType);
            }
            async hasView(entity, ctx) {
                return false;
            }
        })());
    }
    registerNewViewFunction(functionProviderId, functionServiceId, functionDescription, entityType, methodId, objectType, viewFunction, functionAccessRightsScope = 'read') {
        const objectTypeService = this.objectTypeService;
        this.registerNewMethodOrView(entityType, methodId, new (class {
            constructor() {
                this.accessRightsScope = 'read';
                this.providerId = functionProviderId;
                this.serviceId = functionServiceId;
                this.description = new extension_description_1.TreatmentDescription(this.providerId, this.serviceId, entityType +
                    '.' +
                    objectType +
                    '.' +
                    methodId +
                    '(): ' +
                    functionDescription);
                this.accessRightsScope = functionAccessRightsScope;
            }
            async runMethod(entity, args, ctx) {
                throw application_error_1.ApplicationError.notImplemented({
                    entityType,
                    methodId,
                    entityId: entity.id,
                });
            }
            async generateView(entity, args, ctx) {
                if (await this.hasView(entity, ctx)) {
                    return viewFunction(entity, args, ctx);
                }
                throw application_error_1.ApplicationError.notImplemented({
                    entityType,
                    viewId: methodId,
                    entityId: entity.id,
                });
            }
            async hasMethod(entity, ctx) {
                return false;
            }
            async hasView(entity, ctx) {
                var _a;
                const objectTypeId = entity_name_1.EntityName.objectTree === entityType
                    ? (_a = entity.treeNode) === null || _a === void 0 ? void 0 : _a.objectTypeId : entity.objectTypeId;
                const implementedTypes = await objectTypeService.getImplementedTypes(objectTypeId);
                return -1 < lodash_1.indexOf(implementedTypes, objectType);
            }
        })());
    }
    async getEntity(entityType, id, ctx) {
        return (await ctx.methodContext.entity.getOrSetValue(async () => {
            let result = undefined;
            switch (entityType) {
                case entity_name_1.EntityName.objectNode:
                    {
                        result = await this.objectNodeService.getNode(id, ctx);
                    }
                    break;
                case entity_name_1.EntityName.objectTree: {
                    const nodeObject = await this.objectNodeService.getNode(id, ctx);
                    ctx.treeContext.treeNode.value = nodeObject;
                    const treeNodes = await this.objectTreeService.loadChildrenNodes(nodeObject.tree
                        ? nodeObject.id
                        : nodeObject.parentTreeId, ctx);
                    result = await this.objectTreeService.buildTreeFromNodes(nodeObject, treeNodes, ctx);
                    break;
                }
            }
            return result;
        }));
    }
    async getMethod(entityType, id, methodId, ctx) {
        if (entityType in this.methodsAndViews) {
            if (methodId in this.methodsAndViews[entityType]) {
                const entity = await this.getEntity(entityType, id, ctx);
                for (const method of this.methodsAndViews[entityType][methodId]) {
                    if (method.hasMethod && (await method.hasMethod(entity, ctx))) {
                        return method;
                    }
                }
            }
        }
        return undefined;
    }
    async getView(entityType, id, viewId, ctx) {
        if (entityType in this.methodsAndViews) {
            if (viewId in this.methodsAndViews[entityType]) {
                const entity = await this.getEntity(entityType, id, ctx);
                for (const view of this.methodsAndViews[entityType][viewId]) {
                    if (view.hasView && (await view.hasView(entity, ctx))) {
                        return view;
                    }
                }
            }
        }
        return undefined;
    }
    async runMethod(entityType, id, methodId, args, ctx) {
        const method = await this.getMethod(entityType, id, methodId, ctx);
        if (!method) {
            throw application_error_1.ApplicationError.notImplemented({
                entityType,
                methodId,
                entityId: id,
            });
        }
        const entity = await this.getEntity(entityType, id, ctx);
        if (!entity) {
            throw application_error_1.ApplicationError.notFound({ entityType, entityId: id });
        }
        let methodResult;
        if (method.runMethod) {
            methodResult = await method.runMethod(entity, args, ctx);
        }
        return methodResult ? methodResult : this.getEntity(entityType, id, ctx);
    }
    async generateView(entityType, id, viewId, args, ctx) {
        const view = await this.getView(entityType, id, viewId, ctx);
        if (!view) {
            throw application_error_1.ApplicationError.notImplemented({
                entityType,
                viewId: viewId,
                entityId: id,
            });
        }
        const entity = await this.getEntity(entityType, id, ctx);
        if (!entity) {
            throw application_error_1.ApplicationError.notFound({ entityType, entityId: id });
        }
        if (view.generateView) {
            return view.generateView(entity, args, ctx);
        }
        throw application_error_1.ApplicationError.notImplemented({
            entityType,
            viewId: viewId,
            entityId: id,
        });
    }
};
ActionEntityService = tslib_1.__decorate([
    core_1.injectable({ scope: core_1.BindingScope.SINGLETON }),
    tslib_1.__param(0, core_1.service(object_node_service_1.ObjectNodeService)),
    tslib_1.__param(1, core_1.service(object_tree_service_1.ObjectTreeService)),
    tslib_1.__param(2, core_1.service(object_type_service_1.ObjectTypeService)),
    tslib_1.__metadata("design:paramtypes", [object_node_service_1.ObjectNodeService,
        object_tree_service_1.ObjectTreeService,
        object_type_service_1.ObjectTypeService])
], ActionEntityService);
exports.ActionEntityService = ActionEntityService;
//# sourceMappingURL=action-entity.service.js.map