"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 AuditSubscriber_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuditSubscriber = void 0;
const common_1 = require("@nestjs/common");
const string_helper_1 = require("../helpers/string.helper");
const solid_registry_1 = require("../helpers/solid-registry");
const request_context_service_1 = require("../services/request-context.service");
const publisher_factory_service_1 = require("../services/queues/publisher-factory.service");
let AuditSubscriber = AuditSubscriber_1 = class AuditSubscriber {
    constructor(publisherFactory, solidRegistry, requestContextService) {
        this.publisherFactory = publisherFactory;
        this.solidRegistry = solidRegistry;
        this.requestContextService = requestContextService;
        this.logger = new common_1.Logger(AuditSubscriber_1.name);
        this.perTxn = new WeakMap();
    }
    bindToDataSource(dataSource) {
        this.dataSource = dataSource;
        this.dataSource.subscribers.push(this);
    }
    enqueue(event, payload) {
        const qr = event.queryRunner;
        const arr = this.perTxn.get(qr) ?? [];
        arr.push(payload);
        this.perTxn.set(qr, arr);
    }
    shouldTrackAudit(metadata) {
        return this.solidRegistry.isAuditableModel((0, string_helper_1.lowerFirst)(metadata.name));
    }
    activeUserId() {
        return this.requestContextService.getActiveUser()?.sub ?? null;
    }
    async afterInsert(event) {
        if (!this.shouldTrackAudit(event.metadata))
            return;
        this.enqueue(event, {
            eventType: 'insert',
            modelName: event.metadata.name,
            entityId: event.entity?.id ?? null,
            occurredAt: new Date().toISOString(),
            after: event.entity ?? null,
            userId: this.activeUserId(),
        });
    }
    async afterUpdate(event) {
        if (!this.shouldTrackAudit(event.metadata))
            return;
        this.enqueue(event, {
            eventType: 'update',
            modelName: event.metadata.name,
            entityId: event.entity?.id ?? null,
            occurredAt: new Date().toISOString(),
            after: event.entity ?? null,
            before: event.databaseEntity ?? null,
            updatedColumnNames: (event.updatedColumns ?? []).map(c => c.propertyName),
            userId: this.activeUserId(),
        });
    }
    async afterRemove(event) {
        if (!this.shouldTrackAudit(event.metadata))
            return;
        this.enqueue(event, {
            eventType: 'delete',
            modelName: event.metadata.name,
            entityId: event.databaseEntity?.id ?? null,
            occurredAt: new Date().toISOString(),
            before: event.databaseEntity,
            userId: this.activeUserId(),
        });
    }
    async afterTransactionCommit(event) {
        const batch = this.perTxn.get(event.queryRunner) ?? [];
        this.perTxn.delete(event.queryRunner);
        const results = await Promise.allSettled(batch.map(payload => this.publisherFactory.publish({ payload }, 'ChatterQueuePublisher')));
        results.forEach((result, i) => {
            if (result.status === 'rejected') {
                this.logger.error(`Failed to publish audit event for ${batch[i].modelName}#${batch[i].entityId}`, result.reason);
            }
        });
    }
    afterTransactionRollback(event) {
        this.perTxn.delete(event.queryRunner);
    }
};
exports.AuditSubscriber = AuditSubscriber;
exports.AuditSubscriber = AuditSubscriber = AuditSubscriber_1 = __decorate([
    (0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }),
    __metadata("design:paramtypes", [publisher_factory_service_1.PublisherFactory,
        solid_registry_1.SolidRegistry,
        request_context_service_1.RequestContextService])
], AuditSubscriber);
//# sourceMappingURL=audit.subscriber.js.map