"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiTraceComparisonRunRepository = void 0;
exports.createMultiTraceComparisonRunRepository = createMultiTraceComparisonRunRepository;
const crypto_1 = __importDefault(require("crypto"));
const multiTraceComparison_1 = require("../types/multiTraceComparison");
const enterpriseAuditService_1 = require("./enterpriseAuditService");
function parseJson(value, fallback) {
    if (!value)
        return fallback;
    try {
        return JSON.parse(value);
    }
    catch {
        return fallback;
    }
}
function mapRun(row, inputRows) {
    const orderedInputs = [...inputRows].sort((a, b) => a.ordinal - b.ordinal);
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        ...(row.created_by ? { createdBy: row.created_by } : {}),
        inputSnapshotIds: orderedInputs.map(input => input.snapshot_id),
        ...(row.baseline_snapshot_id ? { baselineSnapshotId: row.baseline_snapshot_id } : {}),
        query: row.query,
        status: row.status,
        ...(row.result_json ? { result: parseJson(row.result_json, undefined) } : {}),
        ...(row.report_id ? { reportId: row.report_id } : {}),
        ...(row.error ? { error: row.error } : {}),
        schemaVersion: row.schema_version,
        createdAt: row.created_at,
        ...(row.completed_at ? { completedAt: row.completed_at } : {}),
    };
}
function uniqueSnapshotIds(baselineSnapshotId, candidates) {
    const seen = new Set();
    const ids = [];
    for (const id of [baselineSnapshotId, ...candidates]) {
        const normalized = id?.trim();
        if (!normalized || seen.has(normalized))
            continue;
        seen.add(normalized);
        ids.push(normalized);
    }
    return ids;
}
class MultiTraceComparisonRunRepository {
    constructor(db) {
        this.db = db;
    }
    createRun(scope, input) {
        const now = Date.now();
        const id = input.id || crypto_1.default.randomUUID();
        const baselineSnapshotId = input.baselineSnapshotId?.trim() || undefined;
        const inputSnapshotIds = uniqueSnapshotIds(baselineSnapshotId, input.candidateSnapshotIds);
        if (inputSnapshotIds.length < 2) {
            throw new Error('comparison run requires at least two snapshots');
        }
        const write = this.db.transaction(() => {
            this.db.prepare(`
        INSERT INTO multi_trace_comparison_runs
          (id, tenant_id, workspace_id, created_by, baseline_snapshot_id,
           query, status, result_json, report_id, error, schema_version,
           created_at, completed_at)
        VALUES
          (@id, @tenantId, @workspaceId, @createdBy, @baselineSnapshotId,
           @query, @status, NULL, NULL, NULL, @schemaVersion, @createdAt, NULL)
      `).run({
                id,
                tenantId: scope.tenantId,
                workspaceId: scope.workspaceId,
                createdBy: scope.userId ?? null,
                baselineSnapshotId: baselineSnapshotId ?? null,
                query: input.query,
                status: input.status ?? 'pending',
                schemaVersion: multiTraceComparison_1.MULTI_TRACE_COMPARISON_RUN_SCHEMA_VERSION,
                createdAt: now,
            });
            const insertInput = this.db.prepare(`
        INSERT INTO multi_trace_comparison_inputs
          (comparison_id, snapshot_id, role, ordinal)
        VALUES
          (@comparisonId, @snapshotId, @role, @ordinal)
      `);
            inputSnapshotIds.forEach((snapshotId, ordinal) => {
                insertInput.run({
                    comparisonId: id,
                    snapshotId,
                    role: snapshotId === baselineSnapshotId ? 'baseline' : 'candidate',
                    ordinal,
                });
            });
            (0, enterpriseAuditService_1.recordEnterpriseAuditEvent)(this.db, {
                tenantId: scope.tenantId,
                workspaceId: scope.workspaceId,
                actorUserId: scope.userId,
                action: 'comparison.created',
                resourceType: 'multi_trace_comparison_run',
                resourceId: id,
                metadata: {
                    baselineSnapshotId: baselineSnapshotId ?? null,
                    inputSnapshotCount: inputSnapshotIds.length,
                    status: input.status ?? 'pending',
                },
            });
        });
        write();
        const run = this.getRun(scope, id);
        if (!run) {
            throw new Error('Failed to persist comparison run');
        }
        return run;
    }
    getRun(scope, comparisonId) {
        const row = this.db.prepare(`
      SELECT *
      FROM multi_trace_comparison_runs
      WHERE tenant_id = @tenantId
        AND workspace_id = @workspaceId
        AND id = @comparisonId
        AND (created_by = @userId OR created_by IS NULL)
      LIMIT 1
    `).get({
            tenantId: scope.tenantId,
            workspaceId: scope.workspaceId,
            userId: scope.userId ?? null,
            comparisonId,
        });
        if (!row)
            return null;
        const inputRows = this.db.prepare(`
      SELECT snapshot_id, role, ordinal
      FROM multi_trace_comparison_inputs
      WHERE comparison_id = ?
      ORDER BY ordinal ASC
    `).all(row.id);
        return mapRun(row, inputRows);
    }
    updateRun(scope, comparisonId, input) {
        const completedAt = input.completedAt
            ?? (['completed', 'failed', 'needs_selection'].includes(input.status)
                ? Date.now()
                : null);
        const resultJson = input.result ? JSON.stringify(input.result) : null;
        const hasBaselineSnapshotId = input.baselineSnapshotId !== undefined;
        const update = this.db.transaction(() => {
            const result = this.db.prepare(`
        UPDATE multi_trace_comparison_runs
        SET status = @status,
            baseline_snapshot_id = CASE
              WHEN @hasBaselineSnapshotId = 1 THEN @baselineSnapshotId
              ELSE baseline_snapshot_id
            END,
            result_json = @resultJson,
            report_id = @reportId,
            error = @error,
            completed_at = @completedAt
        WHERE tenant_id = @tenantId
          AND workspace_id = @workspaceId
          AND id = @comparisonId
          AND (created_by = @userId OR created_by IS NULL)
      `).run({
                tenantId: scope.tenantId,
                workspaceId: scope.workspaceId,
                userId: scope.userId ?? null,
                comparisonId,
                status: input.status,
                hasBaselineSnapshotId: hasBaselineSnapshotId ? 1 : 0,
                baselineSnapshotId: input.baselineSnapshotId ?? null,
                resultJson,
                reportId: input.reportId ?? null,
                error: input.error ?? null,
                completedAt,
            });
            if (result.changes > 0 && hasBaselineSnapshotId) {
                this.db.prepare(`
          UPDATE multi_trace_comparison_inputs
          SET role = CASE
            WHEN snapshot_id = @baselineSnapshotId THEN 'baseline'
            ELSE 'candidate'
          END
          WHERE comparison_id = @comparisonId
        `).run({
                    comparisonId,
                    baselineSnapshotId: input.baselineSnapshotId,
                });
            }
            return result;
        })();
        if (update.changes === 0)
            return null;
        (0, enterpriseAuditService_1.recordEnterpriseAuditEvent)(this.db, {
            tenantId: scope.tenantId,
            workspaceId: scope.workspaceId,
            actorUserId: scope.userId,
            action: 'comparison.updated',
            resourceType: 'multi_trace_comparison_run',
            resourceId: comparisonId,
            metadata: {
                status: input.status,
                baselineSnapshotId: input.baselineSnapshotId ?? null,
                reportId: input.reportId ?? null,
            },
        });
        return this.getRun(scope, comparisonId);
    }
}
exports.MultiTraceComparisonRunRepository = MultiTraceComparisonRunRepository;
function createMultiTraceComparisonRunRepository(db) {
    return new MultiTraceComparisonRunRepository(db);
}
//# sourceMappingURL=multiTraceComparisonStore.js.map