"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutboxService = void 0;
const api_prisma_1 = require("@hed-hog/api-prisma");
const common_1 = require("@nestjs/common");
const types_1 = require("../types");
let OutboxService = class OutboxService {
    constructor(prisma) {
        this.prisma = prisma;
    }
    toDomainEvent(record) {
        return {
            id: String(record.id),
            eventName: record.event_name,
            sourceModule: record.source_module,
            aggregateType: record.aggregate_type,
            aggregateId: record.aggregate_id,
            payload: record.payload,
            status: record.status,
            attemptCount: record.attempt_count,
            lastError: record.last_error,
            availableAt: record.available_at,
            processedAt: record.processed_at,
            createdAt: record.created_at,
            updatedAt: record.updated_at,
        };
    }
    toDatabaseId(eventId) {
        return typeof eventId === 'number' ? eventId : Number(eventId);
    }
    toDatabaseUpdate(partialUpdate) {
        if (!partialUpdate) {
            return undefined;
        }
        return {
            attempt_count: partialUpdate.attemptCount,
            last_error: partialUpdate.lastError,
            available_at: partialUpdate.availableAt,
            processed_at: partialUpdate.processedAt,
        };
    }
    /**
     * Write a new event to the outbox
     */
    async createEvent(dto, persistenceClient) {
        const client = persistenceClient !== null && persistenceClient !== void 0 ? persistenceClient : this.prisma;
        const record = await client.outbox_event.create({
            data: {
                event_name: dto.eventName,
                source_module: dto.sourceModule,
                aggregate_type: dto.aggregateType,
                aggregate_id: dto.aggregateId,
                payload: dto.payload,
                status: types_1.OutboxEventStatus.PENDING,
                available_at: new Date(),
                attempt_count: 0,
            },
        });
        return this.toDomainEvent(record);
    }
    /**
     * Find pending events available for processing, ordered by creation time
     */
    async findPending(limit, leaseMs) {
        const now = new Date();
        const leaseThreshold = new Date(now.getTime() - leaseMs);
        const records = await this.prisma.outbox_event.findMany({
            where: {
                OR: [
                    { status: types_1.OutboxEventStatus.PENDING },
                    {
                        status: types_1.OutboxEventStatus.PROCESSING,
                        available_at: { lte: leaseThreshold },
                    },
                ],
            },
            orderBy: { created_at: 'asc' },
            take: limit,
        });
        return records.map((record) => this.toDomainEvent(record));
    }
    /**
     * Update event status and related fields
     */
    async updateStatus(eventId, status, partialUpdate) {
        const record = await this.prisma.outbox_event.update({
            where: { id: this.toDatabaseId(eventId) },
            data: Object.assign({ status }, this.toDatabaseUpdate(partialUpdate)),
        });
        return this.toDomainEvent(record);
    }
    /**
     * Mark event as processing and set lease time
     */
    async markProcessing(eventId, leaseMs) {
        const leaseUntil = new Date(Date.now() + leaseMs);
        return this.updateStatus(eventId, types_1.OutboxEventStatus.PROCESSING, {
            availableAt: leaseUntil,
        });
    }
    /**
     * Increment attempt count and optionally update error
     */
    async incrementAttempt(eventId, error) {
        const record = await this.prisma.outbox_event.findUnique({
            where: { id: this.toDatabaseId(eventId) },
        });
        if (!record) {
            throw new Error(`Event ${eventId} not found`);
        }
        const event = this.toDomainEvent(record);
        return this.updateStatus(eventId, event.status, {
            attemptCount: event.attemptCount + 1,
            lastError: error || null,
        });
    }
    /**
     * Get event by ID
     */
    async getById(eventId) {
        const record = await this.prisma.outbox_event.findUnique({
            where: { id: this.toDatabaseId(eventId) },
        });
        return record ? this.toDomainEvent(record) : null;
    }
    /**
     * Count events by status
     */
    async countByStatus(status) {
        return this.prisma.outbox_event.count({
            where: { status },
        });
    }
};
exports.OutboxService = OutboxService;
exports.OutboxService = OutboxService = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [api_prisma_1.PrismaService])
], OutboxService);
//# sourceMappingURL=outbox.service.js.map