"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObjectTypeService = void 0;
const tslib_1 = require("tslib");
const core_1 = require("@loopback/core");
const repository_1 = require("@loopback/repository");
const lodash_1 = tslib_1.__importStar(require("lodash"));
const helper_1 = require("../helper");
const repositories_1 = require("../repositories");
const application_error_1 = require("./../helper/application-error");
const entity_name_1 = require("./../models/entity-name");
const object_sub_type_repository_1 = require("./../repositories/object-sub-type.repository");
const application_service_1 = require("./application.service");
const content_entity_service_1 = require("./content-entity/content-entity.service");
const defaultObjectTypeFilter = {
    order: ['name'],
    fields: {
        definition: true,
        inheritedTypesIds: true,
        id: true,
        name: true,
        title: true,
        contentType: true,
        uri: true,
        iconView: true,
        templateView: true,
    },
    include: [
        {
            relation: 'objectSubTypes',
            scope: {
                order: ['index'],
            },
        },
    ],
};
let ObjectTypeService = class ObjectTypeService {
    constructor(objectTypeRepository, objectSubTypeRepository, contentEntityService, appCtx) {
        this.objectTypeRepository = objectTypeRepository;
        this.objectSubTypeRepository = objectSubTypeRepository;
        this.contentEntityService = contentEntityService;
        this.appCtx = appCtx;
    }
    async add(objectType, ctx) {
        delete objectType.contentDefinition;
        objectType.id = objectType.name;
        objectType.applicationType = !!objectType.applicationType;
        const newType = await this.objectTypeRepository.create(objectType);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.resetCache();
        if (!objectType.applicationType) {
            return this.searchByName(objectType.name);
        }
        return newType;
    }
    async getTypeWithContent(ctx) {
        const types = await this.getAll(ctx);
        return lodash_1.omitBy(types, (type) => {
            return !type.contentType || '' === type.contentType;
        });
    }
    async cleanWhenReboot() {
        const types = await this.objectTypeRepository.find({
            where: { applicationType: true },
        });
        for (const type of types) {
            await this.objectSubTypeRepository.deleteAll({
                or: [{ objectTypeId: type.id }, { subObjectTypeId: type.id }],
            });
            await this.objectTypeRepository.delete(type);
        }
    }
    async registerApplicationType(objectType) {
        objectType.applicationType = true;
        return this.add(objectType, new application_service_1.CurrentContext());
    }
    async searchByName(name) {
        return this.searchById(name);
    }
    get filterOrder() {
        return lodash_1.default.cloneDeep(defaultObjectTypeFilter.order);
    }
    get filterFields() {
        return lodash_1.default.cloneDeep(defaultObjectTypeFilter.fields);
    }
    get filterInclude() {
        return lodash_1.default.cloneDeep(defaultObjectTypeFilter.include);
    }
    async searchById(id) {
        return (await this.searchAll())[id];
    }
    async getImplementedTypes(type) {
        const implementedTypes = await this.appCtx.implementedTypes.getOrSetValue(async () => {
            var _a;
            const result = {};
            const allTypes = await this.searchAll();
            for (const knownType in allTypes) {
                result[knownType] = [];
                const objectType = allTypes[knownType];
                if ((_a = objectType === null || objectType === void 0 ? void 0 : objectType.entityCtx) === null || _a === void 0 ? void 0 : _a.implementedTypes) {
                    result[knownType].push(...objectType.entityCtx.implementedTypes);
                }
            }
            return result;
        });
        return type in implementedTypes ? implementedTypes[type] : [];
    }
    async getImplementingCommonTypes(type) {
        const implementingCommonTypes = await this.appCtx.implementingCommonTypes.getOrSetValue(async () => {
            const types = {};
            const allTypes = await this.searchAll();
            for (const knownType in allTypes) {
                types[knownType] = [];
                for (const implementedType of await this.getImplementedTypes(knownType)) {
                    for (const implementingType of await this.getImplementingTypes(implementedType)) {
                        if (-1 === lodash_1.indexOf(types[knownType], implementingType)) {
                            types[knownType].push(implementingType);
                        }
                    }
                }
            }
            return types;
        });
        return type in implementingCommonTypes ? implementingCommonTypes[type] : [];
    }
    async getImplementingTypes(type) {
        const implementingTypes = await this.appCtx.implementingTypes.getOrSetValue(async () => {
            var _a, _b;
            const result = {};
            const allTypes = await this.searchAll();
            for (const knownType in allTypes) {
                result[knownType] = [];
                for (const subType in allTypes) {
                    if (-1 <
                        lodash_1.indexOf((_b = (_a = allTypes[subType]) === null || _a === void 0 ? void 0 : _a.entityCtx) === null || _b === void 0 ? void 0 : _b.implementedTypes, knownType)) {
                        result[knownType].push(subType);
                    }
                }
            }
            return result;
        });
        return type in implementingTypes ? implementingTypes[type] : [];
    }
    async getParentTypesOfType(type) {
        const parentTypes = await this.appCtx.parentTypes.getOrSetValue(async () => {
            const result = {};
            const allTypes = await this.searchAll();
            for (const knownType in allTypes) {
                result[knownType] = [];
                for (const parentType in allTypes) {
                    if (parentType in allTypes &&
                        allTypes[parentType].objectSubTypes &&
                        -1 <
                            lodash_1.indexOf(allTypes[parentType].objectSubTypes.map((subType) => subType.subObjectTypeId), knownType)) {
                        result[knownType].push(parentType);
                    }
                }
            }
            return result;
        });
        return type in parentTypes ? parentTypes[type] : [];
    }
    async getParentTypesOfImplementingType(type) {
        const parentImplementingTypes = await this.appCtx.parentImplementingTypes.getOrSetValue(async () => {
            const result = {};
            const allTypes = await this.searchAll();
            for (const knownType in allTypes) {
                result[knownType] = [];
                for (const implementingType of await this.getImplementingTypes(knownType)) {
                    result[knownType].push(...(await this.getParentTypesOfType(implementingType)).filter((parentType) => -1 === result[knownType].indexOf(parentType)));
                }
            }
            return result;
        });
        return type in parentImplementingTypes ? parentImplementingTypes[type] : [];
    }
    async search(ctx) {
        return lodash_1.default.values(await this.searchAll());
    }
    extendsType(type, allTypes) {
        var _a;
        if (type.extended) {
            return;
        }
        const implementedTypes = [type.name];
        type.extended = true;
        if (lodash_1.isArray(type.inheritedTypesIds)) {
            let parentTypeDefinition = { properties: {} };
            for (const inheritedTypeName of type.inheritedTypesIds) {
                const parentType = allTypes[inheritedTypeName];
                if (!parentType) {
                    console.error(type.name + ': missing ' + inheritedTypeName + ' inherited type');
                    throw application_error_1.ApplicationError.missing({
                        type: type.name,
                        inheritedType: inheritedTypeName,
                    });
                }
                this.extendsType(parentType, allTypes);
                if ((_a = parentType.entityCtx) === null || _a === void 0 ? void 0 : _a.implementedTypes) {
                    implementedTypes.push(...parentType.entityCtx.implementedTypes);
                }
                if (!type.title || '' === type.title) {
                    type.title = parentType.title;
                }
                if (!type.contentType || '' === type.contentType) {
                    type.contentType = parentType.contentType;
                }
                parentTypeDefinition = lodash_1.default.merge({}, parentTypeDefinition, parentType.definition);
                if (!type.iconView || '' === type.iconView) {
                    type.iconView = parentType.iconView;
                }
                if (!type.templateView || '' === type.templateView) {
                    type.templateView = parentType.templateView;
                }
                if (parentType.objectSubTypes) {
                    for (const parentSubType of parentType.objectSubTypes) {
                        if (!lodash_1.default.some(type.objectSubTypes, (subType) => subType.name === parentSubType.name)) {
                            if (!type.objectSubTypes) {
                                type.objectSubTypes = [];
                            }
                            type.objectSubTypes.push(parentSubType);
                        }
                    }
                }
            }
            if (!type.definition) {
                type.definition = parentTypeDefinition;
            }
            else {
                type.definition = lodash_1.default.merge({}, parentTypeDefinition, type.definition);
            }
        }
        if (!type.entityCtx) {
            type.entityCtx = { entityType: entity_name_1.EntityName.objectType };
        }
        type.entityCtx.implementedTypes = lodash_1.uniq(implementedTypes);
    }
    async searchAll() {
        return this.appCtx.allTypes.getOrSetValue(async () => {
            const filter = {
                order: this.filterOrder,
                fields: this.filterFields,
                include: this.filterInclude,
            };
            const allTypes = lodash_1.default.mapKeys(await this.objectTypeRepository.find(filter), (objectType, index) => {
                return objectType.id;
            });
            for (const objectTypeName in allTypes) {
                const objectType = allTypes[objectTypeName];
                this.extendsType(objectType, allTypes);
                objectType.contentDefinition = await this.contentEntityService.getContentDefinition(objectType.contentType);
            }
            for (const objectTypeName in allTypes) {
                if (!allTypes[objectTypeName].title ||
                    '' === allTypes[objectTypeName].title) {
                    allTypes[objectTypeName].title = helper_1.toStartCase(allTypes[objectTypeName].name);
                }
            }
            for (const objectTypeName in allTypes) {
                if (allTypes[objectTypeName].objectSubTypes) {
                    for (const objectSubType of allTypes[objectTypeName].objectSubTypes) {
                        if (!objectSubType.title || '' === objectSubType.title) {
                            objectSubType.title =
                                allTypes[objectSubType.subObjectTypeId].title;
                        }
                    }
                }
            }
            return allTypes;
        });
    }
    async getAll(ctx) {
        return ctx.typeContext.types.getOrSetValue(async () => {
            return this.searchAll();
        });
    }
    resetCache() {
        this.appCtx.allTypes.value = undefined;
        this.appCtx.implementedTypes.value = undefined;
        this.appCtx.implementingTypes.value = undefined;
    }
    removeById(id, ctx) {
        this.resetCache();
        return this.objectTypeRepository.deleteById(id);
    }
    async modifyById(id, objectType, ctx) {
        this.resetCache();
        delete objectType.contentDefinition;
        await this.objectTypeRepository.updateById(id, objectType);
        return this.searchById(id);
    }
    async createSubType(id, objectSubType) {
        this.resetCache();
        return this.objectTypeRepository.objectSubTypes(id).create(objectSubType);
    }
    async modifySubTypeById(objectTypeId, id, objectSubType) {
        this.resetCache();
        const where = {
            id: id,
        };
        await this.objectTypeRepository
            .objectSubTypes(objectTypeId)
            .patch(objectSubType, where);
        return this.objectSubTypeRepository.findById(id);
    }
    async removeSubTypeById(objectTypeId, id) {
        this.resetCache();
        const where = {
            id: id,
        };
        return this.objectTypeRepository.objectSubTypes(objectTypeId).delete(where);
    }
    async getOrCreateObjectSubType(objectTypeId, subObjectTypeId, defaultValue) {
        this.resetCache();
        const where = {
            subObjectTypeId: subObjectTypeId,
        };
        const subTypes = await this.objectTypeRepository
            .objectSubTypes(objectTypeId)
            .find({ where });
        if (!subTypes || 0 === subTypes.length) {
            if (!defaultValue) {
                return null;
            }
            return this.createSubType(objectTypeId, lodash_1.default.merge({}, defaultValue, { subObjectTypeId }));
        }
        if (1 < subTypes.length) {
            throw application_error_1.ApplicationError.tooMany({
                objectType: objectTypeId,
                subObjectType: subObjectTypeId,
            });
        }
        return subTypes[0];
    }
};
ObjectTypeService = tslib_1.__decorate([
    tslib_1.__param(0, repository_1.repository(repositories_1.ObjectTypeRepository)),
    tslib_1.__param(1, repository_1.repository(object_sub_type_repository_1.ObjectSubTypeRepository)),
    tslib_1.__param(2, core_1.service(content_entity_service_1.ContentEntityService)),
    tslib_1.__param(3, core_1.service(application_service_1.ApplicationService)),
    tslib_1.__metadata("design:paramtypes", [repositories_1.ObjectTypeRepository,
        object_sub_type_repository_1.ObjectSubTypeRepository,
        content_entity_service_1.ContentEntityService,
        application_service_1.ApplicationService])
], ObjectTypeService);
exports.ObjectTypeService = ObjectTypeService;
//# sourceMappingURL=object-type.service.js.map