"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var ViewMetadataService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ViewMetadataService = void 0;
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const typeorm_1 = require("@nestjs/typeorm");
const crud_service_1 = require("./crud.service");
const typeorm_2 = require("typeorm");
const string_helper_1 = require("../helpers/string.helper");
const locale_entity_1 = require("../entities/locale.entity");
const model_metadata_helper_service_1 = require("../helpers/model-metadata-helper.service");
const solid_registry_1 = require("../helpers/solid-registry");
const model_metadata_repository_1 = require("../repository/model-metadata.repository");
const view_metadata_repository_1 = require("../repository/view-metadata.repository");
const action_metadata_service_1 = require("./action-metadata.service");
const menu_item_metadata_service_1 = require("./menu-item-metadata.service");
const solid_introspect_service_1 = require("./solid-introspect.service");
const user_view_metadata_service_1 = require("./user-view-metadata.service");
let ViewMetadataService = ViewMetadataService_1 = class ViewMetadataService extends crud_service_1.CRUDService {
    constructor(actionMetadataService, menuItemMetadataService, introspectService, userViewMetadataService, entityManager, repo, modelMetadataRepo, modelMetadataHelperService, moduleRef) {
        super(entityManager, repo, 'viewMetadata', 'solid-core', moduleRef);
        this.actionMetadataService = actionMetadataService;
        this.menuItemMetadataService = menuItemMetadataService;
        this.introspectService = introspectService;
        this.userViewMetadataService = userViewMetadataService;
        this.entityManager = entityManager;
        this.repo = repo;
        this.modelMetadataRepo = modelMetadataRepo;
        this.modelMetadataHelperService = modelMetadataHelperService;
        this.moduleRef = moduleRef;
        this.logger = new common_1.Logger(ViewMetadataService_1.name);
    }
    async getEntityRecordsInAllLocales(modelName, id, defaultEntityLocaleIdFromQuery) {
        const solidRegistry = await this.moduleRef.get(solid_registry_1.SolidRegistry, { strict: false });
        const currentEntityRepository = this.entityManager.getRepository((0, string_helper_1.classify)(modelName));
        if (id === 'new' && !defaultEntityLocaleIdFromQuery) {
            this.logger.debug(`Creating new record without cloning from any defaultEntityLocaleId.`);
            return { records: [], defaultEntityLocaleId: null };
        }
        if (id === 'new' && defaultEntityLocaleIdFromQuery) {
            this.logger.debug(`Creating new record by cloning translations from defaultEntityLocaleId: ${defaultEntityLocaleIdFromQuery}`);
            const records = await currentEntityRepository.find({
                where: [
                    { defaultEntityLocaleId: defaultEntityLocaleIdFromQuery },
                    { id: defaultEntityLocaleIdFromQuery }
                ]
            });
            this.logger.debug(`Found ${records.length} cloned records for new entity.`);
            return { records, defaultEntityLocaleId: defaultEntityLocaleIdFromQuery };
        }
        const entityRecord = await currentEntityRepository.findOne({ where: { id } });
        if (!entityRecord) {
            this.logger.warn(`No entity found for id ${id}`);
            return { records: [], defaultEntityLocaleId: null };
        }
        const defaultLocale = await this.entityManager.getRepository(locale_entity_1.Locale).findOne({ where: { isDefault: true } });
        let defaultEntityLocaleId;
        if (entityRecord.localeName === defaultLocale?.locale) {
            defaultEntityLocaleId = entityRecord.id;
            this.logger.debug(`Editing default locale record with id ${defaultEntityLocaleId}`);
        }
        else {
            defaultEntityLocaleId = entityRecord.defaultEntityLocaleId;
            this.logger.debug(`Editing non-default locale record. DefaultEntityLocaleId: ${defaultEntityLocaleId}`);
        }
        const records = await currentEntityRepository.find({
            where: [
                { defaultEntityLocaleId: defaultEntityLocaleId },
                { id: defaultEntityLocaleId }
            ]
        });
        this.logger.debug(`Found ${records.length} records in all locales for existing entity.`);
        return { records, defaultEntityLocaleId };
    }
    async getLayout(query, activeUser) {
        let { modelName, moduleName, viewType, id, populate, menuItemId, menuItemName, actionId, actionName } = query;
        const solidRequestContext = {
            activeUser: activeUser
        };
        const actionQuery = {
            populate: ["view"]
        };
        let action = null;
        if (actionId) {
            action = await this.actionMetadataService.findOne(actionId, actionQuery, solidRequestContext);
        }
        const menuItemQuery = {
            populate: ['action', 'action.model', 'module']
        };
        let menuItem = null;
        if (menuItemId) {
            menuItem = await this.menuItemMetadataService.findOne(menuItemId, menuItemQuery, solidRequestContext);
        }
        let viewModes = [];
        const menuItemModelId = menuItem?.action?.model?.id;
        const menuItemModuleId = menuItem?.module?.id;
        const collectionViewTypes = ['card', 'list', 'kanban', 'tree'];
        if (menuItemModelId && menuItemModuleId) {
            const actionQb = await this.actionMetadataService.repo.createSecurityRuleAwareQueryBuilder('action');
            const actionsForViewModes = await actionQb
                .leftJoinAndSelect('action.model', 'model')
                .leftJoinAndSelect('action.module', 'module')
                .leftJoinAndSelect('action.view', 'view')
                .where('model.id = :modelId', { modelId: menuItemModelId })
                .andWhere('module.id = :moduleId', { moduleId: menuItemModuleId })
                .andWhere('view.type IN (:...viewTypes)', { viewTypes: collectionViewTypes })
                .getMany();
            const canonicalActionsByViewType = new Map();
            for (const actionItem of actionsForViewModes) {
                const resolvedViewType = actionItem.view?.type;
                if (!resolvedViewType || canonicalActionsByViewType.has(resolvedViewType)) {
                    continue;
                }
                canonicalActionsByViewType.set(resolvedViewType, actionItem);
            }
            if (action?.view?.type && collectionViewTypes.includes(action.view.type) && action?.id) {
                canonicalActionsByViewType.set(action.view.type, action);
            }
            viewModes = collectionViewTypes
                .map((resolvedViewType) => canonicalActionsByViewType.get(resolvedViewType))
                .filter(Boolean)
                .map(actionItem => ({
                type: actionItem.view?.type ?? '',
                menuItemId: menuItem.id,
                menuItemName: menuItem.displayName,
                actionId: actionItem.id ?? '',
                actionName: actionItem.displayName ?? '',
            }));
        }
        const viewId = action?.view?.id;
        const entity = await this.repo.findOne({
            where: {
                model: { singularName: modelName },
                module: { name: moduleName },
                ...(actionId && viewId ? { id: action.view.id } : { type: viewType })
            },
            relations: {
                model: {
                    userKeyField: true,
                },
                module: true,
            }
        });
        if (!entity) {
            throw new common_1.BadRequestException(`Unable to identify view for module: ${moduleName}, model: ${modelName} and viewType: ${viewType}`);
        }
        if (!activeUser?.sub) {
            throw new common_1.BadRequestException(`Unable to identify user for module: ${moduleName}, model: ${modelName} and viewType: ${viewType}`);
        }
        if (actionId) {
            delete action.view;
            entity['action'] = action;
        }
        if (menuItemId) {
            entity['menu'] = menuItem;
        }
        const userLayout = await this.userViewMetadataService.repo.findOne({
            where: {
                user: { id: activeUser?.sub },
                viewMetadata: { id: entity.id },
            },
        });
        if (userLayout) {
            entity.layout = JSON.parse(userLayout.layout);
        }
        else {
            entity.layout = JSON.parse(entity.layout);
        }
        if (entity?.layout?.attrs?.createAction) {
            const actionName = entity.layout.attrs.createAction;
            entity.layout.attrs.createAction = await this.actionMetadataService.findOneByUserKey(actionName);
        }
        if (entity?.layout?.attrs?.editAction) {
            const actionName = entity.layout.attrs.editAction;
            entity.layout.attrs.editAction = await this.actionMetadataService.findOneByUserKey(actionName);
        }
        let workflowFieldName = null;
        let workflowField = null;
        if (viewType === 'form') {
            workflowFieldName = entity.layout?.attrs?.workflowField;
        }
        const fields = await this.modelMetadataHelperService.loadFieldHierarchy(modelName);
        const fieldsMap = new Map();
        for (let i = 0; i < fields.length; i++) {
            const field = fields[i];
            if (workflowFieldName && field.name === workflowFieldName) {
                workflowField = field;
            }
            if (field.type === 'relation') {
                const relationModel = await this.modelMetadataRepo.find({
                    where: {
                        singularName: field.relationCoModelSingularName
                    },
                    relations: {
                        userKeyField: true
                    }
                });
                if (relationModel) {
                    field['relationModel'] = relationModel[0];
                }
            }
            if (!fieldsMap.has(field.name)) {
                fieldsMap.set(field.name, field);
            }
        }
        let solidFormViewWorkflowData = [];
        if (viewType === 'form' && workflowField) {
            if (workflowField.type === 'selectionStatic') {
                solidFormViewWorkflowData = workflowField.selectionStaticValues.map(item => {
                    const [value, label] = item.split(":");
                    return { label, value };
                });
            }
            if (workflowField.type === 'relation' && workflowField.relationType === 'many-to-one') {
                const comodelCrudService = this.introspectService.getCRUDService(workflowField.relationCoModelSingularName);
                const data = await comodelCrudService.find({ limit: 100, offset: 0, });
                const records = data.records ?? [];
                const workflowFieldMetadata = fieldsMap.get(workflowFieldName);
                const workflowFielUserkey = workflowFieldMetadata['relationModel']['userKeyField']['name'];
                solidFormViewWorkflowData = records.map(item => ({ 'label': item[workflowFielUserkey], 'value': item['id'] }));
            }
        }
        const applicableLocales = [];
        if (entity.model.internationalisation) {
            const defaultEntityLocaleIdFromQuery = query?.defaultEntityLocaleId;
            const { records: entityRecordsInAllLocales, defaultEntityLocaleId } = await this.getEntityRecordsInAllLocales(modelName, id, defaultEntityLocaleIdFromQuery);
            const allLocales = await this.entityManager.getRepository(locale_entity_1.Locale).find({});
            for (const locale of allLocales) {
                const matchingRecord = entityRecordsInAllLocales.find(record => record.localeName === locale.locale);
                applicableLocales.push({
                    locale: locale.locale,
                    displayName: locale.displayName,
                    isDefault: locale.isDefault ? 'yes' : 'no',
                    defaultEntityLocaleId: defaultEntityLocaleId,
                    entityId: matchingRecord ? matchingRecord.id : null
                });
            }
        }
        const r = {
            'solidView': entity,
            'solidFieldsMetadata': Object.fromEntries(fieldsMap),
            'solidFormViewWorkflowData': solidFormViewWorkflowData,
            'applicableLocales': applicableLocales,
            'viewModes': viewModes
        };
        return r;
    }
    async loadFieldHierarchy(modelName) {
        const model = await this.modelMetadataRepo.findOne({
            where: {
                singularName: modelName,
            },
            relations: {
                fields: true,
                parentModel: {
                    fields: true,
                }
            }
        });
        const fields = [];
        if (model) {
            fields.push(...model.fields);
            if (model.parentModel) {
                fields.push(...model.parentModel.fields);
            }
        }
        return fields;
    }
    async findOneByUserKey(name, relations = {}) {
        const entity = await this.repo.findOne({
            where: {
                name: name,
            },
            relations: relations,
        });
        return entity;
    }
    async upsert(updateSolidViewDto) {
        const existingSolidView = await this.findOneByUserKey(updateSolidViewDto.name);
        if (existingSolidView) {
            const updatedSolidViewDto = { ...existingSolidView, ...updateSolidViewDto };
            return this.repo.save(updatedSolidViewDto);
        }
        else {
            const viewData = this.repo.create(updateSolidViewDto);
            return this.repo.save(viewData);
        }
    }
    async createIfNotPresent(updateSolidViewDto) {
        const existingSolidView = await this.findOneByUserKey(updateSolidViewDto.name);
        if (existingSolidView) {
        }
        else {
            const viewData = this.repo.create(updateSolidViewDto);
            return this.repo.save(viewData);
        }
    }
};
exports.ViewMetadataService = ViewMetadataService;
exports.ViewMetadataService = ViewMetadataService = ViewMetadataService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(4, (0, typeorm_1.InjectEntityManager)()),
    __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => model_metadata_repository_1.ModelMetadataRepository))),
    __metadata("design:paramtypes", [action_metadata_service_1.ActionMetadataService,
        menu_item_metadata_service_1.MenuItemMetadataService,
        solid_introspect_service_1.SolidIntrospectService,
        user_view_metadata_service_1.UserViewMetadataService,
        typeorm_2.EntityManager,
        view_metadata_repository_1.ViewMetadataRepository,
        model_metadata_repository_1.ModelMetadataRepository,
        model_metadata_helper_service_1.ModelMetadataHelperService,
        core_1.ModuleRef])
], ViewMetadataService);
//# sourceMappingURL=view-metadata.service.js.map