"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); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComputedEntityFieldSubscriber = void 0;
const lodash_1 = require("lodash");
const common_1 = require("@nestjs/common");
const create_field_metadata_dto_1 = require("../dtos/create-field-metadata.dto");
const solid_registry_1 = require("../helpers/solid-registry");
const publisher_factory_service_1 = require("../services/queues/publisher-factory.service");
let ComputedEntityFieldSubscriber = class ComputedEntityFieldSubscriber {
    constructor(solidRegistry, publisherFactory) {
        this.solidRegistry = solidRegistry;
        this.publisherFactory = publisherFactory;
        this.logger = new common_1.Logger(this.constructor.name);
    }
    bindToDataSource(dataSource) {
        this.dataSource = dataSource;
        this.dataSource.subscribers.push(this);
    }
    async beforeInsert(event) {
        const modelName = (0, lodash_1.camelCase)(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
        const eventContext = this.sanitizeEventContext(event, 'beforeInsert');
        await this.handleComputedFieldEvaluation(event.entity, create_field_metadata_dto_1.ComputedFieldTriggerOperation.beforeInsert, modelName, eventContext);
    }
    async beforeUpdate(event) {
        const modelName = (0, lodash_1.camelCase)(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
        const eventContext = this.sanitizeEventContext(event, 'beforeUpdate');
        await this.handleComputedFieldEvaluation(event.entity, create_field_metadata_dto_1.ComputedFieldTriggerOperation.beforeUpdate, modelName, eventContext);
    }
    afterInsert(event) {
        const modelName = (0, lodash_1.camelCase)(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
        const eventContext = this.sanitizeEventContext(event, 'afterInsert');
        this.handleComputedFieldEvaluationJob(event.entity, create_field_metadata_dto_1.ComputedFieldTriggerOperation.afterInsert, modelName, eventContext);
    }
    afterUpdate(event) {
        const modelName = (0, lodash_1.camelCase)(event.metadata?.name ?? event.entity?.constructor?.name ?? event.databaseEntity?.constructor?.name ?? '');
        const eventContext = this.sanitizeEventContext(event, 'afterUpdate');
        this.handleComputedFieldEvaluationJob(event.entity, create_field_metadata_dto_1.ComputedFieldTriggerOperation.afterUpdate, modelName, eventContext);
    }
    afterRemove(event) {
        const modelName = (0, lodash_1.camelCase)(event.metadata?.name ?? event.entity?.constructor?.name ?? event.databaseEntity?.constructor?.name ?? '');
        const eventContext = this.sanitizeEventContext(event, 'afterRemove');
        this.handleComputedFieldEvaluationJob(event.databaseEntity, create_field_metadata_dto_1.ComputedFieldTriggerOperation.afterRemove, modelName, eventContext);
    }
    async handleComputedFieldEvaluation(entity, currentOperation, modelName, eventContext) {
        if (!entity) {
            return;
        }
        const computedFieldsTobeEvaluated = this.getComputedFieldsForEvaluation(this.solidRegistry.getComputedFieldMetadata(), currentOperation, modelName);
        for (const computedField of computedFieldsTobeEvaluated) {
            await this.evaluateComputedField(this.attachContext(computedField, eventContext), entity, currentOperation);
        }
    }
    handleComputedFieldEvaluationJob(entity, currentOperation, modelName, eventContext) {
        if (!entity) {
            return;
        }
        const computedFieldsTobeEvaluated = this.getComputedFieldsForEvaluation(this.solidRegistry.getComputedFieldMetadata(), currentOperation, modelName);
        for (const computedField of computedFieldsTobeEvaluated) {
            this.enqueueComputedFieldEvaluationJob(this.attachContext(computedField, eventContext), entity, eventContext);
        }
    }
    getComputedFieldsForEvaluation(computedFieldMetadata = [], currentOperation, currentModelName) {
        return computedFieldMetadata.filter((computedField) => computedField.computedFieldTriggerConfig.some((trigger) => trigger.operations.includes(currentOperation) &&
            trigger.modelName === currentModelName));
    }
    async evaluateComputedField(computedFieldMetadata, entity, currentOperation) {
        if (this.shouldSkipPreComputeOnInsert(computedFieldMetadata, entity, currentOperation)) {
            return;
        }
        const computedValue = await this.preComputeValue(computedFieldMetadata, entity);
        if (computedValue) {
            entity[computedFieldMetadata.fieldName] = computedValue;
        }
    }
    shouldSkipPreComputeOnInsert(computedFieldMetadata, entity, currentOperation) {
        if (currentOperation !== create_field_metadata_dto_1.ComputedFieldTriggerOperation.beforeInsert) {
            return false;
        }
        if (!entity) {
            return false;
        }
        const fieldName = computedFieldMetadata.fieldName;
        if (!fieldName) {
            return false;
        }
        const hasValue = Object.prototype.hasOwnProperty.call(entity, fieldName) && entity[fieldName] !== undefined && entity[fieldName] !== null;
        return hasValue;
    }
    async preComputeValue(computedFieldMetadata, entity) {
        try {
            const provider = this.solidRegistry.getComputedFieldProvider(computedFieldMetadata.computedFieldValueProviderName);
            const providerInstance = provider.instance;
            const computedValue = await providerInstance.preComputeValue(entity, computedFieldMetadata);
            return computedValue;
        }
        catch (error) {
            throw new common_1.InternalServerErrorException(`Error evaluating computed field ${computedFieldMetadata.fieldName} for model ${computedFieldMetadata.modelName} for triggered entity ${entity.constructor.name}: ${error.message}`);
        }
    }
    enqueueComputedFieldEvaluationJob(computedField, databaseEntity, eventContext) {
        const payload = {
            ...computedField,
            databaseEntity,
        };
        this.publisherFactory.publish({ payload }, 'ComputedFieldEvaluationPublisher');
    }
    attachContext(computedField, eventContext) {
        if (!eventContext)
            return computedField;
        return {
            ...computedField,
            computedFieldValueProviderCtxt: {
                ...(computedField.computedFieldValueProviderCtxt || {}),
            },
            eventContext,
        };
    }
    sanitizeEventContext(event, eventType) {
        if (!event)
            return undefined;
        const base = {
            metadataName: event.metadata?.name,
            eventType: eventType,
        };
        if ("entityId" in event && event.entityId) {
            base.entityId = event.entityId;
        }
        else if (event.entity && event.entity.id != null) {
            base.entityId = event.entity.id;
        }
        else if ("databaseEntity" in event && event.databaseEntity?.id != null) {
            base.entityId = event.databaseEntity.id;
        }
        if ("updatedColumns" in event && event.updatedColumns) {
            base.updatedColumns = event.updatedColumns.map((c) => c.propertyName);
        }
        if ("updatedRelations" in event && event.updatedRelations) {
            base.updatedRelations = event.updatedRelations.map((r) => r.propertyName);
        }
        if (event.entity)
            base.entity = event.entity;
        if ("databaseEntity" in event && event.databaseEntity) {
            base.databaseEntity = event.databaseEntity;
        }
        return base;
    }
};
exports.ComputedEntityFieldSubscriber = ComputedEntityFieldSubscriber;
exports.ComputedEntityFieldSubscriber = ComputedEntityFieldSubscriber = __decorate([
    (0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }),
    __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => publisher_factory_service_1.PublisherFactory))),
    __metadata("design:paramtypes", [solid_registry_1.SolidRegistry,
        publisher_factory_service_1.PublisherFactory])
], ComputedEntityFieldSubscriber);
//# sourceMappingURL=computed-entity-field.subscriber.js.map