"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.evalResultRecordToDenormInput = evalResultRecordToDenormInput;
exports.insertEvalDenormArtifacts = insertEvalDenormArtifacts;
exports.insertEvalDenormArtifactsBatch = insertEvalDenormArtifactsBatch;
exports.persistDenormOutputsBatch = persistDenormOutputsBatch;
const logger_1 = __importDefault(require("../../../logger"));
const database_1 = require("../../../database");
const tables_1 = require("../../../database/tables");
const clickhouseService_1 = require("../clickhouseService");
const config_1 = require("./config");
const deleteEvalDenorm_1 = require("./deleteEvalDenorm");
const denormalize_1 = require("./denormalize");
const mapClickHouseRows_1 = require("./mapClickHouseRows");
const types_1 = require("./types");
function toDenormInput(row) {
    return row;
}
/** Normalize EvalResult / parseReportsForClickHouse row for denormalize (camel + snake). */
function evalResultRecordToDenormInput(row, overrides) {
    return {
        id: String(row.id ?? ''),
        createdAt: (row.createdAt ?? row.created_at),
        evalId: String(overrides?.evalId ?? row.evalId ?? row.eval_id ?? ''),
        testIdx: Number(row.testIdx ?? row.test_idx ?? 0),
        testCase: (row.testCase ?? row.test_case),
        gradingResult: (row.gradingResult ?? row.grading_result),
        metadata: (row.metadata ?? {}),
        failureReason: Number(row.failureReason ?? row.failure_reason ?? 0),
        error: row.error ?? null,
        isRedteam: Number(row.isRedteam ?? row.is_redteam ?? 0),
        projectId: (row.projectId ?? row.project_id),
        applicationId: (row.applicationId ?? row.application_id),
        workspaceId: (row.workspaceId ?? row.workspace_id),
        traceId: (row.traceId ?? row.trace_id),
        sessionId: (row.sessionId ?? row.session_id),
        traceIds: (row.traceIds ?? row.trace_ids),
        testRunId: overrides?.testRunId ?? (row.testRunId ?? row.test_run_id),
    };
}
function analyticsToPgValues(analytics) {
    return {
        evalResultId: analytics.evalResultId,
        createdAt: analytics.createdAt,
        projectId: analytics.projectId,
        applicationId: analytics.applicationId,
        workspaceId: analytics.workspaceId,
        traceId: analytics.traceId,
        sessionId: analytics.sessionId,
        traceIds: analytics.traceIds,
        isRedteam: analytics.isRedteam,
        testRunId: analytics.testRunId,
        evalId: analytics.evalId,
        testIdx: analytics.testIdx,
        failureReason: analytics.failureReason,
        error: analytics.error,
        gradingPass: analytics.gradingPass,
        gradingScore: analytics.gradingScore,
        gradingReason: analytics.gradingReason,
        componentPassCount: analytics.componentPassCount,
        componentFailCount: analytics.componentFailCount,
        metricKeys: analytics.metricKeys,
        failedMetricKeys: analytics.failedMetricKeys,
        pluginId: analytics.pluginId,
        pluginPass: analytics.pluginPass,
        denormVersion: analytics.denormVersion,
    };
}
function componentToPgValues(c) {
    return {
        id: c.id,
        evalResultId: c.evalResultId,
        componentIndex: c.componentIndex,
        assertionType: c.assertionType,
        assertionDisplayLabel: c.assertionDisplayLabel,
        metricKey: c.metricKey,
        componentPass: c.componentPass,
        componentScore: c.componentScore,
        componentReason: c.componentReason,
        assertionExpected: c.assertionExpected,
        assertionActual: c.assertionActual,
        projectId: c.projectId,
        applicationId: c.applicationId,
        workspaceId: c.workspaceId,
        createdAt: c.createdAt,
        traceId: c.traceId,
        sessionId: c.sessionId,
        traceIds: c.traceIds,
        testRunId: c.testRunId,
        evalId: c.evalId,
        isRedteam: c.isRedteam,
        failureReason: c.failureReason,
        error: c.error,
        pluginId: c.pluginId,
        denormVersion: c.denormVersion,
    };
}
async function persistDenormToPostgres(output) {
    await persistDenormOutputsToPostgres([output]);
}
async function persistDenormOutputsToPostgres(outputs) {
    if (outputs.length === 0) {
        return;
    }
    const db = (0, database_1.getDb)();
    await db
        .insert(tables_1.evalResultsAnalyticsTable)
        .values(outputs.map((o) => analyticsToPgValues(o.analytics)))
        .onConflictDoNothing();
    const componentValues = outputs.flatMap((o) => o.components.map(componentToPgValues));
    if (componentValues.length > 0) {
        await db.insert(tables_1.evalResultComponentsTable).values(componentValues).onConflictDoNothing();
    }
}
async function replaceDenormInClickHouse(outputs) {
    if (outputs.length === 0) {
        return;
    }
    const evalResultIds = [...new Set(outputs.map((o) => o.analytics.evalResultId))];
    await (0, deleteEvalDenorm_1.deleteEvalDenormByEvalResultIds)(evalResultIds);
    const analyticsRows = outputs.map((o) => (0, mapClickHouseRows_1.analyticsRowToClickHouse)(o.analytics));
    await clickhouseService_1.clickHouseService.insert(types_1.EVAL_RESULTS_ANALYTICS_TABLE, analyticsRows);
    const componentRows = outputs.flatMap((o) => o.components.map(mapClickHouseRows_1.componentRowToClickHouse));
    if (componentRows.length > 0) {
        await clickhouseService_1.clickHouseService.insert(types_1.EVAL_RESULT_COMPONENTS_TABLE, componentRows);
    }
}
async function persistDenormToClickHouse(output) {
    await replaceDenormInClickHouse([output]);
}
/**
 * Dual-write denormalized analytics + component rows (Postgres + ClickHouse). Best-effort.
 */
async function insertEvalDenormArtifacts(row, options) {
    if (!(0, config_1.isEvalDenormWritesEnabled)()) {
        return;
    }
    const denormVersion = options?.denormVersion ?? types_1.DENORM_VERSION_LIVE;
    const input = toDenormInput(row);
    if (!input.id) {
        return;
    }
    try {
        const output = (0, denormalize_1.denormalizeEvalResult)(input, denormVersion);
        if (!options?.skipPostgres) {
            await persistDenormToPostgres(output);
        }
        if (!options?.skipClickHouse) {
            await persistDenormToClickHouse(output);
        }
    }
    catch (error) {
        logger_1.default.warn(`insertEvalDenormArtifacts failed for eval_result_id=${input.id}: ${error}`);
    }
}
async function insertEvalDenormArtifactsBatch(rows, options) {
    if (!(0, config_1.isEvalDenormWritesEnabled)() || rows.length === 0) {
        return;
    }
    const denormVersion = options?.denormVersion ?? types_1.DENORM_VERSION_LIVE;
    const outputs = [];
    for (const row of rows) {
        const input = toDenormInput(row);
        if (!input.id) {
            continue;
        }
        outputs.push((0, denormalize_1.denormalizeEvalResult)(input, denormVersion));
    }
    if (outputs.length === 0) {
        return;
    }
    await persistDenormOutputsBatch(outputs, options);
}
/**
 * Persist pre-denormalized rows (avoids re-running denormalize in hot backfill loops).
 */
async function persistDenormOutputsBatch(outputs, options) {
    if (!(0, config_1.isEvalDenormWritesEnabled)() || outputs.length === 0) {
        return;
    }
    try {
        if (!options?.skipPostgres) {
            await persistDenormOutputsToPostgres(outputs);
        }
        if (!options?.skipClickHouse) {
            await replaceDenormInClickHouse(outputs);
        }
    }
    catch (error) {
        logger_1.default.warn(`persistDenormOutputsBatch failed (${outputs.length} rows): ${error}`);
        if (options?.rethrowOnError) {
            throw error;
        }
    }
}
//# sourceMappingURL=insertEvalDenormArtifacts.js.map