"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 ChatterMessageService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChatterMessageService = void 0;
const local_date_time_transformer_1 = require("../transformers/typeorm/local-date-time-transformer");
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const strings_1 = require("@angular-devkit/core/src/utils/strings");
const chatter_message_constants_1 = require("../constants/chatter-message.constants");
const error_messages_1 = require("../constants/error-messages");
const model_metadata_helper_service_1 = require("../helpers/model-metadata-helper.service");
const string_helper_1 = require("../helpers/string.helper");
const chatter_message_details_repository_1 = require("../repository/chatter-message-details.repository");
const chatter_message_repository_1 = require("../repository/chatter-message.repository");
const field_metadata_repository_1 = require("../repository/field-metadata.repository");
const media_repository_1 = require("../repository/media.repository");
const model_metadata_repository_1 = require("../repository/model-metadata.repository");
const crud_service_1 = require("./crud.service");
const chatter_message_details_entity_1 = require("../entities/chatter-message-details.entity");
const chatter_message_entity_1 = require("../entities/chatter-message.entity");
const mediaStorageProviders_1 = require("./mediaStorageProviders");
const request_context_service_1 = require("./request-context.service");
const common_2 = require("@nestjs/common");
let ChatterMessageService = ChatterMessageService_1 = class ChatterMessageService extends crud_service_1.CRUDService {
    constructor(entityManager, repo, chatterMessageDetailsRepo, mediaRepository, fieldMetadataRepo, moduleRef, modelMetadataRepo, requestContextService, modelMetadataHelperService) {
        super(entityManager, repo, 'chatterMessage', 'solid-core', moduleRef);
        this.entityManager = entityManager;
        this.repo = repo;
        this.chatterMessageDetailsRepo = chatterMessageDetailsRepo;
        this.mediaRepository = mediaRepository;
        this.fieldMetadataRepo = fieldMetadataRepo;
        this.moduleRef = moduleRef;
        this.modelMetadataRepo = modelMetadataRepo;
        this.requestContextService = requestContextService;
        this.modelMetadataHelperService = modelMetadataHelperService;
        this._logger = new common_2.Logger(ChatterMessageService_1.name);
    }
    resolveMessageUserId(userId) {
        if (userId) {
            return userId;
        }
        return this.requestContextService.getActiveUser()?.sub ?? null;
    }
    resolveMessageUser(userId) {
        const resolvedUserId = this.resolveMessageUserId(userId);
        return resolvedUserId ? { id: resolvedUserId } : null;
    }
    stampMessageAuditFields(chatterMessage, userId) {
        const resolvedUserId = this.resolveMessageUserId(userId);
        chatterMessage.user = resolvedUserId ? { id: resolvedUserId } : null;
        chatterMessage.createdBy = resolvedUserId;
        chatterMessage.updatedBy = resolvedUserId;
    }
    isEditableCustomNoteMessage(message) {
        if (message.messageType !== chatter_message_constants_1.CHATTER_MESSAGE_TYPE.CUSTOM) {
            return false;
        }
        return [chatter_message_constants_1.CHATTER_MESSAGE_SUBTYPE.CUSTOM, chatter_message_constants_1.CHATTER_MESSAGE_SUBTYPE.NOTE].includes(message.messageSubType);
    }
    parseAttachmentIds(value) {
        if (!value || typeof value !== 'string')
            return [];
        return value
            .split(',')
            .map(v => Number(v.trim()))
            .filter(v => Number.isInteger(v) && v > 0);
    }
    async markCompleted(id) {
        const activeUser = this.requestContextService.getActiveUser();
        if (!activeUser) {
            throw new common_1.BadRequestException(error_messages_1.ERROR_MESSAGES.FORBIDDEN);
        }
        const message = await this.repo.findOne({ where: { id } });
        if (!message) {
            throw new common_1.NotFoundException(`Entity [solid-core.chatterMessage] with id ${id} not found`);
        }
        message.status = chatter_message_constants_1.CHATTER_MESSAGE_STATUS.COMPLETED;
        return this.repo.save(message);
    }
    async updateCustomNoteMessage(id, updateDto, files = []) {
        const activeUser = this.requestContextService.getActiveUser();
        if (!activeUser) {
            throw new common_1.ForbiddenException(error_messages_1.ERROR_MESSAGES.FORBIDDEN);
        }
        const message = await this.repo.findOne({ where: { id }, relations: { user: true } });
        if (!message) {
            throw new common_1.NotFoundException(`Entity [solid-core.chatterMessage] with id ${id} not found`);
        }
        if (!this.isEditableCustomNoteMessage(message)) {
            throw new common_1.BadRequestException('Only custom note messages can be edited.');
        }
        if (!message.user?.id || message.user.id !== activeUser.sub) {
            throw new common_1.ForbiddenException('You can only edit your own custom note messages.');
        }
        const removeAttachmentIds = this.parseAttachmentIds(updateDto?.removeAttachmentIds);
        const hasMessageBody = typeof updateDto?.messageBody === 'string';
        const trimmedMessageBody = (updateDto?.messageBody ?? '').trim();
        const hasNewFiles = Array.isArray(files) && files.length > 0;
        if (!hasMessageBody && removeAttachmentIds.length === 0 && !hasNewFiles) {
            throw new common_1.BadRequestException('No note changes submitted.');
        }
        if (hasMessageBody && trimmedMessageBody.length === 0) {
            throw new common_1.BadRequestException('Message body cannot be empty.');
        }
        if (hasMessageBody) {
            message.messageBody = trimmedMessageBody;
        }
        message.updatedBy = activeUser.sub;
        message.updatedAt = new Date();
        const savedMessage = await this.repo.save(message);
        if (removeAttachmentIds.length > 0 || hasNewFiles) {
            const model = await this.modelMetadataService.findOneBySingularName('chatterMessage', {
                fields: {
                    model: true,
                    mediaStorageProvider: true,
                },
                module: true,
            });
            const mediaFields = model.fields.filter(field => field.type === 'mediaSingle' || field.type === 'mediaMultiple');
            const attachmentFieldIds = mediaFields.map(field => field.id);
            if (removeAttachmentIds.length > 0 && attachmentFieldIds.length > 0) {
                const mediaToRemove = await this.mediaRepository.find({
                    where: {
                        id: (0, typeorm_2.In)(removeAttachmentIds),
                        entityId: savedMessage.id,
                        modelMetadata: { id: model.id },
                        fieldMetadata: { id: (0, typeorm_2.In)(attachmentFieldIds) },
                    },
                    relations: {
                        mediaStorageProviderMetadata: true,
                        fieldMetadata: true,
                    },
                });
                for (const media of mediaToRemove) {
                    const storageType = media.mediaStorageProviderMetadata?.type;
                    const storageProvider = await (0, mediaStorageProviders_1.getMediaStorageProvider)(this.moduleRef, storageType);
                    await storageProvider.deleteByMediaRecord(media);
                }
                if (mediaToRemove.length > 0) {
                    await this.mediaRepository.remove(mediaToRemove);
                }
            }
            for (const mediaField of mediaFields) {
                const storageProviderMetadata = mediaField.mediaStorageProvider;
                const storageProviderType = storageProviderMetadata.type;
                const storageProvider = await (0, mediaStorageProviders_1.getMediaStorageProvider)(this.moduleRef, storageProviderType);
                const media = files.filter(multerFile => multerFile.fieldname === mediaField.name);
                if (media.length > 0) {
                    await storageProvider.store(media, savedMessage, mediaField);
                }
            }
        }
        return savedMessage;
    }
    async postMessage(postDto, files = []) {
        const chatterMessage = new chatter_message_entity_1.ChatterMessage();
        chatterMessage.messageType = chatter_message_constants_1.CHATTER_MESSAGE_TYPE.CUSTOM;
        chatterMessage.messageSubType = postDto.messageSubType || chatter_message_constants_1.CHATTER_MESSAGE_SUBTYPE.CUSTOM;
        chatterMessage.status = postDto.status ?? chatter_message_constants_1.CHATTER_MESSAGE_STATUS.PENDING;
        chatterMessage.messageBody = postDto.messageBody;
        chatterMessage.coModelEntityId = postDto.coModelEntityId;
        chatterMessage.coModelName = postDto.coModelName;
        chatterMessage.modelUserKey = postDto.modelUserKey ?? null;
        const model = await this.modelMetadataRepo.findOne({
            where: { singularName: (0, string_helper_1.lowerFirst)(postDto.coModelName) },
            relations: { userKeyField: true }
        });
        chatterMessage.modelDisplayName = model?.displayName ?? null;
        this.stampMessageAuditFields(chatterMessage);
        const savedMessage = await this.repo.save(chatterMessage);
        if (files && files.length > 0) {
            const model = await this.modelMetadataService.findOneBySingularName('chatterMessage', {
                fields: {
                    model: true,
                    mediaStorageProvider: true,
                },
                module: true,
            });
            const mediaFields = model.fields.filter(field => field.type === 'mediaSingle' || field.type === 'mediaMultiple');
            for (const mediaField of mediaFields) {
                const media = files.filter(multerFile => multerFile.fieldname === mediaField.name);
                if (media.length > 0) {
                    const storageProviderMetadata = mediaField.mediaStorageProvider;
                    const storageProviderType = storageProviderMetadata.type;
                    const storageProvider = await (0, mediaStorageProviders_1.getMediaStorageProvider)(this.moduleRef, storageProviderType);
                    await storageProvider.store(media, savedMessage, mediaField);
                }
            }
        }
        return savedMessage;
    }
    async postAuditMessageOnInsert(entity, modelName, messageQueue = false, userId) {
        if (!entity) {
            return;
        }
        const model = await this.modelMetadataRepo.findOne({
            where: {
                singularName: (0, string_helper_1.lowerFirst)(modelName)
            },
            relations: {
                fields: true,
                module: true,
                userKeyField: true
            }
        });
        if (!model || !model.enableAuditTracking) {
            return;
        }
        const auditFields = model.fields.filter(field => field.enableAuditTracking &&
            !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
            !(field.type === 'relation' && field.relationType === 'one-to-many'));
        const chatterMessage = new chatter_message_entity_1.ChatterMessage();
        chatterMessage.messageType = chatter_message_constants_1.CHATTER_MESSAGE_TYPE.AUDIT;
        chatterMessage.messageSubType = chatter_message_constants_1.CHATTER_MESSAGE_SUBTYPE.AUDIT_INSERT;
        chatterMessage.status = chatter_message_constants_1.CHATTER_MESSAGE_STATUS.PENDING;
        chatterMessage.coModelEntityId = entity.id;
        chatterMessage.coModelName = model?.singularName;
        chatterMessage.modelDisplayName = model?.displayName;
        chatterMessage.modelUserKey = entity[model?.userKeyField?.name];
        chatterMessage.messageBody = `New ${model?.displayName} created`;
        this.stampMessageAuditFields(chatterMessage, userId);
        const savedMessage = await this.repo.save(chatterMessage);
        for (const field of auditFields) {
            const fieldValue = entity[field.name];
            if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
                const messageDetail = new chatter_message_details_entity_1.ChatterMessageDetails();
                messageDetail.chatterMessage = savedMessage;
                messageDetail.fieldName = field.name;
                messageDetail.fieldDisplayName = field.displayName;
                messageDetail.fieldType = field.type;
                messageDetail.oldValue = null;
                messageDetail.oldValueDisplay = null;
                messageDetail.newValue = this.formatFieldValue(field, fieldValue);
                messageDetail.newValueDisplay = await this.formatFieldValueDisplay(field, fieldValue);
                await this.chatterMessageDetailsRepo.save(messageDetail);
            }
        }
    }
    async postAuditMessageOnUpdate(entity, modelName, databaseEntity, updatedColumns = [], messageQueue = false, userId) {
        if (!databaseEntity || !entity) {
            return;
        }
        const model = await this.modelMetadataRepo.findOne({
            where: {
                singularName: (0, string_helper_1.lowerFirst)(modelName)
            },
            relations: {
                fields: true,
                module: true,
                userKeyField: true
            }
        });
        if (!model || !model.enableAuditTracking) {
            return;
        }
        const modelFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName);
        const auditFields = modelFields.filter(field => field.enableAuditTracking &&
            !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
            !(field.type === 'relation' && field.relationType === 'one-to-many'));
        const updatedFieldNames = new Set(updatedColumns.map(col => col.propertyName));
        const allNonRelationFields = auditFields.filter(field => field.type !== 'relation');
        const allRelationFields = auditFields.filter(field => field.type === 'relation');
        let potentialNonRelationFields = [];
        if (updatedColumns.length > 0) {
            potentialNonRelationFields = allNonRelationFields.filter(field => updatedFieldNames.has(field.name));
        }
        else {
            potentialNonRelationFields = allNonRelationFields;
        }
        const potentialRelationFields = allRelationFields;
        const changedNonRelationFields = potentialNonRelationFields.filter(field => {
            const newValue = entity[field.name];
            const oldValue = databaseEntity[field.name];
            return this.hasValueChanged(newValue, oldValue);
        });
        const changedRelationFields = [];
        if (potentialRelationFields.length > 0) {
            const metadata = this.entityManager.connection.entityMetadatas.find(m => m.name === modelName);
            const populatedOldEntity = await this.populateRelationFields(databaseEntity, potentialRelationFields, metadata);
            for (const field of potentialRelationFields) {
                const newValue = entity[field.name];
                const oldValue = populatedOldEntity[field.name];
                if (this.hasRelationValueChanged(field, newValue, oldValue)) {
                    changedRelationFields.push({
                        field,
                        newValue,
                        oldValue
                    });
                }
            }
        }
        const allChangedFields = [
            ...changedNonRelationFields.map(field => ({
                field,
                newValue: entity[field.name],
                oldValue: databaseEntity[field.name]
            })),
            ...changedRelationFields
        ];
        if (allChangedFields.length === 0) {
            return;
        }
        const chatterMessage = new chatter_message_entity_1.ChatterMessage();
        chatterMessage.messageType = chatter_message_constants_1.CHATTER_MESSAGE_TYPE.AUDIT;
        chatterMessage.messageSubType = chatter_message_constants_1.CHATTER_MESSAGE_SUBTYPE.AUDIT_UPDATE;
        chatterMessage.status = chatter_message_constants_1.CHATTER_MESSAGE_STATUS.PENDING;
        chatterMessage.coModelEntityId = entity?.id;
        chatterMessage.coModelName = model?.singularName;
        chatterMessage.modelDisplayName = model.displayName;
        chatterMessage.modelUserKey = entity[model?.userKeyField?.name];
        chatterMessage.messageBody = `${model?.displayName} updated`;
        this.stampMessageAuditFields(chatterMessage, userId);
        const savedMessage = await this.repo.save(chatterMessage);
        for (const { field, newValue, oldValue } of allChangedFields) {
            const messageDetail = new chatter_message_details_entity_1.ChatterMessageDetails();
            messageDetail.chatterMessage = savedMessage;
            messageDetail.fieldName = field.name;
            messageDetail.fieldDisplayName = field.displayName;
            messageDetail.fieldType = field.type;
            messageDetail.oldValue = this.formatFieldValue(field, oldValue);
            messageDetail.newValue = this.formatFieldValue(field, newValue);
            messageDetail.oldValueDisplay = await this.formatFieldValueDisplay(field, oldValue);
            messageDetail.newValueDisplay = await this.formatFieldValueDisplay(field, newValue);
            await this.chatterMessageDetailsRepo.save(messageDetail);
        }
    }
    async postAuditMessageOnDelete(modelName, databaseEntity, messageQueue = false, userId) {
        const model = await this.modelMetadataRepo.findOne({
            where: {
                singularName: (0, string_helper_1.lowerFirst)(modelName)
            },
            relations: {
                module: true,
                userKeyField: true
            }
        });
        if (!model || !model.enableAuditTracking) {
            return;
        }
        const modelFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName);
        const auditFields = modelFields.filter(field => field.enableAuditTracking &&
            !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
            !(field.type === 'relation' && field.relationType === 'one-to-many'));
        const relationFields = auditFields.filter(field => field.type === 'relation');
        const entityMetadata = this.entityManager.connection.entityMetadatas.find(m => m.name === modelName);
        const populatedEntity = relationFields.length > 0 && entityMetadata
            ? await this.populateRelationFields(databaseEntity, relationFields, entityMetadata)
            : { ...databaseEntity };
        const chatterMessage = new chatter_message_entity_1.ChatterMessage();
        chatterMessage.messageType = chatter_message_constants_1.CHATTER_MESSAGE_TYPE.AUDIT;
        chatterMessage.messageSubType = chatter_message_constants_1.CHATTER_MESSAGE_SUBTYPE.AUDIT_DELETE;
        chatterMessage.status = chatter_message_constants_1.CHATTER_MESSAGE_STATUS.PENDING;
        chatterMessage.coModelEntityId = databaseEntity?.id;
        chatterMessage.coModelName = model?.singularName;
        chatterMessage.modelDisplayName = model?.displayName;
        chatterMessage.modelUserKey = databaseEntity[model?.userKeyField?.name];
        chatterMessage.messageBody = `${model?.displayName} deleted`;
        this.stampMessageAuditFields(chatterMessage, userId);
        const savedMessage = await this.repo.save(chatterMessage);
        for (const field of auditFields) {
            const fieldValue = populatedEntity[field.name];
            if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
                const messageDetail = new chatter_message_details_entity_1.ChatterMessageDetails();
                messageDetail.chatterMessage = savedMessage;
                messageDetail.fieldName = field.name;
                messageDetail.fieldDisplayName = field.displayName;
                messageDetail.fieldType = field.type;
                messageDetail.oldValue = this.formatFieldValue(field, fieldValue);
                messageDetail.oldValueDisplay = await this.formatFieldValueDisplay(field, fieldValue);
                messageDetail.newValue = null;
                messageDetail.newValueDisplay = null;
                await this.chatterMessageDetailsRepo.save(messageDetail);
            }
        }
    }
    formatFieldValue(field, value) {
        if (value === null || value === undefined) {
            return '';
        }
        if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
            return `${value}`;
        }
        if (field.type === 'relation') {
            if (field.relationType === "many-to-one") {
                return value.id;
            }
            if (field.relationType === 'many-to-many') {
                return value.map(item => item.id).join(', ');
            }
        }
        if (value instanceof Date) {
            return (0, local_date_time_transformer_1.serializeDate)(value);
        }
        return value.toString();
    }
    async formatFieldValueDisplay(field, value) {
        if (value === null || value === undefined) {
            return '';
        }
        if (field.type === 'selectionStatic' || field.type === 'selectionDynamic') {
            return `${value}`;
        }
        if (['date', 'datetime', 'time'].includes(field.type)) {
            return null;
        }
        if (field.type === 'relation') {
            if (field.relationType === "many-to-one") {
                if (value.name) {
                    return value.name;
                }
                try {
                    const relatedModel = await this.modelMetadataRepo.findOne({
                        where: { singularName: field.relationCoModelSingularName || field.relation },
                        relations: { userKeyField: true }
                    });
                    if (relatedModel && relatedModel.userKeyField) {
                        const userKeyFieldName = relatedModel.userKeyField.name;
                        return value[userKeyFieldName] ? value[userKeyFieldName].toString() : '';
                    }
                    if (value.id) {
                        return value.id.toString();
                    }
                }
                catch (error) {
                    console.error('Error fetching related model metadata:', error);
                    return value.id ? value.id.toString() : '';
                }
            }
            if (field.relationType === 'many-to-many') {
                return value.map(item => item.name).join(', ');
            }
        }
        return value.toString();
    }
    hasValueChanged(newValue, oldValue) {
        if (newValue === oldValue) {
            return false;
        }
        if (newValue === null && oldValue === null) {
            return false;
        }
        if (newValue === undefined && oldValue === undefined) {
            return false;
        }
        if (newValue && oldValue && typeof newValue === 'object' && typeof oldValue === 'object') {
            if (newValue.id !== undefined && oldValue.id !== undefined) {
                return newValue.id !== oldValue.id;
            }
            if (Array.isArray(newValue) && Array.isArray(oldValue)) {
                if (newValue.length !== oldValue.length) {
                    return true;
                }
                const newIds = newValue.map(item => item.id || item).sort();
                const oldIds = oldValue.map(item => item.id || item).sort();
                return JSON.stringify(newIds) !== JSON.stringify(oldIds);
            }
        }
        if (Array.isArray(newValue) && Array.isArray(oldValue)) {
            return JSON.stringify(newValue) !== JSON.stringify(oldValue);
        }
        return true;
    }
    hasRelationValueChanged(field, newValue, oldValue) {
        if (newValue === oldValue) {
            return false;
        }
        if ((newValue === null || newValue === undefined) && (oldValue === null || oldValue === undefined)) {
            return false;
        }
        if (field.relationType === 'many-to-one') {
            const newId = this.extractRelationId(newValue);
            const oldId = this.extractRelationId(oldValue);
            return newId !== oldId;
        }
        if (field.relationType === 'many-to-many') {
            const newIds = this.extractRelationIds(newValue);
            const oldIds = this.extractRelationIds(oldValue);
            if (newIds.length !== oldIds.length) {
                return true;
            }
            newIds.sort();
            oldIds.sort();
            return JSON.stringify(newIds) !== JSON.stringify(oldIds);
        }
        return this.hasValueChanged(newValue, oldValue);
    }
    extractRelationId(value) {
        if (value === null || value === undefined) {
            return null;
        }
        if (typeof value === 'string' || typeof value === 'number') {
            return value;
        }
        if (typeof value === 'object' && value.id !== undefined) {
            return value.id;
        }
        return null;
    }
    extractRelationIds(value) {
        if (!Array.isArray(value)) {
            const id = this.extractRelationId(value);
            return id !== null ? [id] : [];
        }
        return value.map(item => this.extractRelationId(item)).filter(id => id !== null);
    }
    async populateRelationFields(databaseEntity, relationFields, metadata) {
        const populatedEntity = { ...databaseEntity };
        for (const field of relationFields) {
            const relationValue = databaseEntity[field.name];
            if (relationValue === null || relationValue === undefined) {
                populatedEntity[field.name] = relationValue;
                continue;
            }
            const relationMetadata = metadata.relations.find(rel => rel.propertyName === field.name);
            if (!relationMetadata) {
                populatedEntity[field.name] = relationValue;
                continue;
            }
            const targetEntity = relationMetadata.inverseEntityMetadata || relationMetadata.type;
            if (field.relationType === 'many-to-one') {
                const relationId = this.extractRelationId(relationValue);
                if (relationId) {
                    const relatedEntity = await this.entityManager.findOne(targetEntity, {
                        where: { id: relationId }
                    });
                    populatedEntity[field.name] = relatedEntity;
                }
                else {
                    populatedEntity[field.name] = relationValue;
                }
            }
            else if (field.relationType === 'many-to-many' || field.relationType === 'manyToMany') {
                const relationIds = this.extractRelationIds(relationValue);
                if (relationIds.length > 0) {
                    const relatedEntities = await this.entityManager.findByIds(targetEntity, relationIds);
                    populatedEntity[field.name] = relatedEntities;
                }
                else {
                    populatedEntity[field.name] = relationValue;
                }
            }
            else {
                populatedEntity[field.name] = relationValue;
            }
        }
        return populatedEntity;
    }
    logHeapUsed(label) {
        const mb = () => Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
        this._logger.log(`heapUsedMB(${label}): ${mb()}`);
    }
    async getChatterMessages(entityId, entityName, query) {
        const { limit = 25, offset = 0, populate = [], populateMedia = [], filters } = query;
        this.logHeapUsed('getChatterMessages-start');
        const model = await this.modelMetadataRepo.findOne({
            where: {
                singularName: entityName
            },
        });
        this.logHeapUsed('getChatterMessages-modelLoaded');
        const oneToManyFields = await this.fieldMetadataRepo.find({
            where: {
                model: { id: model.id },
                type: 'relation',
                relationType: 'one-to-many'
            }
        });
        this.logHeapUsed('getChatterMessages-oneToManyFieldsLoaded');
        const relatedEntitiesMap = new Map();
        for (const field of oneToManyFields) {
            if (field.enableAuditTracking === false) {
                this._logger.log(`Skipping field ${field.name} for chatter message retrieval because audit tracking is disabled`);
                continue;
            }
            const coModelName = field.relationCoModelSingularName;
            const coModelFieldName = field.relationCoModelFieldName;
            const coModel = await this.modelMetadataRepo.findOne({
                where: { singularName: coModelName }
            });
            if (coModel) {
                const dsName = coModel.dataSource || 'default';
                const em = dsName === 'default' ? this.entityManager : this.moduleRef.get(`${dsName}EntityManager`, { strict: false });
                const relatedEntityRepository = em.getRepository((0, strings_1.classify)(coModelName));
                const relatedEntities = await relatedEntityRepository.find({
                    select: {
                        id: true,
                    },
                    where: { [coModelFieldName]: { id: entityId } },
                    take: 5,
                });
                const relatedIds = relatedEntities.map((entity) => entity.id);
                relatedEntitiesMap.set(field.name, relatedIds);
            }
        }
        this.logHeapUsed('getChatterMessages-relatedEntitiesLoaded');
        const qb = await this.repo.createSecurityRuleAwareQueryBuilder('entity');
        this.logHeapUsed('getChatterMessages-queryBuilderReady');
        const orConditions = [];
        const parameters = {};
        orConditions.push('(entity.coModelName = :entityName AND entity.coModelEntityId = :entityId)');
        parameters.entityName = entityName;
        parameters.entityId = entityId;
        let paramIndex = 0;
        for (const [fieldName, relatedIds] of relatedEntitiesMap.entries()) {
            if (relatedIds.length > 0) {
                const field = oneToManyFields.find(f => f.name === fieldName);
                if (field) {
                    const coModelName = field.relationCoModelSingularName;
                    const idsParamName = `relatedIds${paramIndex}`;
                    orConditions.push(`(entity.coModelName = :coModelName${paramIndex} AND entity.coModelEntityId IN (:...${idsParamName}))`);
                    parameters[`coModelName${paramIndex}`] = coModelName;
                    parameters[idsParamName] = relatedIds;
                    paramIndex++;
                }
            }
        }
        qb.where(new typeorm_2.Brackets(qb => {
            qb.where(orConditions.join(' OR '), parameters);
        }));
        const relations = ['chatterMessageDetails', 'user'];
        if (populate && populate.length > 0) {
            const normalizedPopulate = this.crudHelperService.normalize(populate);
            relations.push(...normalizedPopulate.filter(rel => !relations.includes(rel)));
        }
        relations.forEach(relation => {
            qb.leftJoinAndSelect(`entity.${relation}`, relation);
        });
        if (filters) {
            qb.andWhere(new typeorm_2.Brackets(whereQb => {
                this.crudHelperService.applyFilters(whereQb, filters, 'entity', qb);
            }));
        }
        qb.orderBy('entity.createdAt', 'DESC');
        qb.skip(offset).take(limit);
        const [entities, count] = await qb.getManyAndCount();
        this.logHeapUsed('getChatterMessages-entitiesLoaded');
        const DATE_FIELD_TYPES = ['date', 'datetime', 'time'];
        for (const entity of entities) {
            for (const detail of entity.chatterMessageDetails ?? []) {
                if (!detail.fieldType || !DATE_FIELD_TYPES.includes(detail.fieldType))
                    continue;
                if (detail.oldValue) {
                    const d = local_date_time_transformer_1.LocalDateTimeTransformer.from(detail.oldValue);
                    if (d)
                        detail.oldValue = d.toISOString();
                }
                if (detail.newValue) {
                    const d = local_date_time_transformer_1.LocalDateTimeTransformer.from(detail.newValue);
                    if (d)
                        detail.newValue = d.toISOString();
                }
            }
        }
        if (populateMedia && populateMedia.length > 0) {
            const normalizedPopulateMedia = this.crudHelperService.normalize(populateMedia);
            this.logHeapUsed('getChatterMessages-beforePopulateMedia');
            await this['handlePopulateMedia'](normalizedPopulateMedia, entities);
            this.logHeapUsed('getChatterMessages-afterPopulateMedia');
        }
        const currentPage = Math.floor(offset / limit) + 1;
        const totalPages = Math.ceil(count / limit);
        const nextPage = currentPage < totalPages ? currentPage + 1 : null;
        const prevPage = currentPage > 1 ? currentPage - 1 : null;
        return {
            meta: {
                totalRecords: count,
                currentPage: currentPage,
                nextPage: nextPage,
                prevPage: prevPage,
                totalPages: totalPages,
                perPage: +limit,
            },
            records: entities
        };
    }
};
exports.ChatterMessageService = ChatterMessageService;
exports.ChatterMessageService = ChatterMessageService = ChatterMessageService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectEntityManager)()),
    __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => model_metadata_repository_1.ModelMetadataRepository))),
    __metadata("design:paramtypes", [typeorm_2.EntityManager,
        chatter_message_repository_1.ChatterMessageRepository,
        chatter_message_details_repository_1.ChatterMessageDetailsRepository,
        media_repository_1.MediaRepository,
        field_metadata_repository_1.FieldMetadataRepository,
        core_1.ModuleRef,
        model_metadata_repository_1.ModelMetadataRepository,
        request_context_service_1.RequestContextService,
        model_metadata_helper_service_1.ModelMetadataHelperService])
], ChatterMessageService);
//# sourceMappingURL=chatter-message.service.js.map