"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTestRunList = exports.CalulateTestRunTotalStats = void 0;
const drizzle_orm_1 = require("drizzle-orm");
const database_1 = require("../../../database");
const tables_1 = require("../../../database/tables");
const logger_1 = __importDefault(require("../../../logger"));
const get_upgrade_meta_1 = require("../../../util/get-upgrade-meta");
const decimal_js_1 = __importDefault(require("decimal.js"));
const time_1 = require("../../../util/time");
const constants_1 = require("../../../constants");
const restriction_message_1 = require("../../constants/restriction.message");
const CalulateTestRunTotalStats = (evalResult) => {
    const stats = evalResult?.reduce((acc, result) => {
        acc.total += 1;
        if (result?.gradingResult?.pass) {
            acc.successes += 1;
        }
        else {
            acc.failures += 1;
        }
        return acc;
    }, { total: 0, successes: 0, failures: 0 });
    stats.errors = (evalResult.length ?? 0) - stats?.total;
    return stats;
};
exports.CalulateTestRunTotalStats = CalulateTestRunTotalStats;
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
/** Parses `applicationIds` (comma-separated and/or repeated) and legacy `applicationId`. */
const parseApplicationIdsFilter = (applicationIdsRaw, applicationIdLegacy) => {
    const parts = [];
    const pushSplit = (raw) => {
        if (typeof raw !== 'string') {
            return;
        }
        for (const piece of raw.split(',')) {
            const t = piece.trim();
            if (t) {
                parts.push(t);
            }
        }
    };
    if (Array.isArray(applicationIdsRaw)) {
        for (const entry of applicationIdsRaw) {
            pushSplit(entry);
        }
    }
    else {
        pushSplit(applicationIdsRaw);
    }
    pushSplit(applicationIdLegacy);
    const unique = [...new Set(parts.filter((p) => UUID_V4_RE.test(p)))];
    return unique;
};
const getTestRunList = async (req, res) => {
    try {
        const db = await (0, database_1.getDb)();
        const { projectId, page = '1', limit = '10', orgId, applicationIds: applicationIdsQuery, applicationId: applicationIdQuery, } = req.query;
        const parsedLimit = Number(limit);
        const parsedPage = Number(page);
        const offset = (parsedPage - 1) * parsedLimit;
        const applicationIdsFilter = parseApplicationIdsFilter(applicationIdsQuery, applicationIdQuery);
        const listWhereConditions = [
            (0, drizzle_orm_1.eq)(tables_1.testRunsTable.projectId, projectId),
            (0, drizzle_orm_1.eq)(tables_1.testRunsTable.type, constants_1.TEST_RUN_TYPE.EVAL),
        ];
        if (applicationIdsFilter.length > 0) {
            const overlapConditions = applicationIdsFilter.map((id) => (0, drizzle_orm_1.sql) `${tables_1.testRunsTable.applicationIds}::jsonb @> ${drizzle_orm_1.sql.raw(`'${JSON.stringify([id])}'::jsonb`)}`);
            listWhereConditions.push(overlapConditions.length === 1 ? overlapConditions[0] : (0, drizzle_orm_1.or)(...overlapConditions));
        }
        const listWhere = (0, drizzle_orm_1.and)(...listWhereConditions);
        const totalCount = await db
            .select({ count: (0, drizzle_orm_1.sql) `count(*)` })
            .from(tables_1.testRunsTable)
            .where(listWhere);
        const testRuns = await db
            .select({
            id: tables_1.testRunsTable.id,
            createdAt: tables_1.testRunsTable.createdAt,
            status: tables_1.testRunsTable.status,
            applicationIds: tables_1.testRunsTable.applicationIds,
            libraryId: tables_1.testRunsTable.libraryId,
            scheduleJobId: tables_1.testRunsToScheduleJobsTable.scheduleJobId,
        })
            .from(tables_1.testRunsTable)
            .leftJoin(tables_1.testRunsToScheduleJobsTable, (0, drizzle_orm_1.eq)(tables_1.testRunsToScheduleJobsTable.testRunId, tables_1.testRunsTable.id))
            .where(listWhere)
            .limit(parsedLimit)
            .offset(offset)
            .orderBy((0, drizzle_orm_1.desc)(tables_1.testRunsTable.createdAt));
        // Step 2: fetch all evalResults for these testRuns
        const runIds = testRuns.map((r) => r.id);
        const evalResults = await db
            .select({
            testRunId: tables_1.testRunsToEvalsTable.testRunId,
            evalResultId: tables_1.evalResultsTable.id,
            gradingResult: tables_1.evalResultsTable.gradingResult,
        })
            .from(tables_1.testRunsToEvalsTable)
            .innerJoin(tables_1.evalResultsTable, (0, drizzle_orm_1.eq)(tables_1.testRunsToEvalsTable.evalId, tables_1.evalResultsTable.evalId))
            .where((0, drizzle_orm_1.inArray)(tables_1.testRunsToEvalsTable.testRunId, runIds));
        // Step 3: group evalResultIds by testRun
        const grouped = {};
        for (const row of evalResults) {
            if (!grouped[row.testRunId]) {
                grouped[row.testRunId] = [];
            }
            grouped[row.testRunId].push(row);
        }
        const applicationIds = [...new Set(testRuns?.flatMap((tr) => tr.applicationIds || []))];
        let applications = [];
        if (applicationIds.length > 0) {
            const appLiteral = drizzle_orm_1.sql.raw(`ARRAY[${applicationIds.map((id) => `'${id}'`).join(',')}]::uuid[]`);
            applications = await db
                .select({
                id: tables_1.applicationsTable.id,
                name: tables_1.applicationsTable.name,
            })
                .from(tables_1.applicationsTable)
                .where((0, drizzle_orm_1.sql) `${tables_1.applicationsTable.id}::uuid = ANY(${appLiteral})`);
        }
        const libraryIds = testRuns.map((tr) => tr.libraryId).filter((id) => id !== null);
        let libraries = [];
        if (libraryIds.length > 0) {
            const libraryLiteral = drizzle_orm_1.sql.raw(`ARRAY[${libraryIds.map((id) => `'${id}'`).join(',')}]::uuid[]`);
            libraries = await db
                .select({
                id: tables_1.promptsLibraryTable.id,
                name: tables_1.promptsLibraryTable.name,
            })
                .from(tables_1.promptsLibraryTable)
                .where((0, drizzle_orm_1.sql) `${tables_1.promptsLibraryTable.id}::uuid = ANY(${libraryLiteral})`);
        }
        // Create lookup maps
        const applicationMap = new Map(applications?.map((app) => [app?.id, app?.name]));
        const libraryMap = new Map(libraries?.map((lib) => [lib?.id, lib?.name]));
        // Format the response
        const formattedTestRuns = testRuns.map((tr) => ({
            id: tr.id,
            createdAt: tr.createdAt,
            status: tr.status,
            scheduleJobId: tr.scheduleJobId,
            applications: tr.applicationIds?.map((id) => applicationMap.get(id) || 'Unknown Application'),
            promptLibrary: tr.libraryId ? libraryMap.get(tr.libraryId) || 'Unknown Library' : null,
            stats: (() => {
                const TestRunEvalResults = grouped[tr.id] || [];
                return (0, exports.CalulateTestRunTotalStats)(TestRunEvalResults);
            })(),
        }));
        const { restrictionsData, upgradeMetadata, isEnterprise, isStarter, globalBanner, wallet, thresholds, usages, } = await (0, get_upgrade_meta_1.getUpgradeMetaByOrgId)(orgId, {
            fetchMetricUsage: true,
        });
        if (!isEnterprise && restrictionsData?.functional_evals > 0) {
            const whereConditions = (0, time_1.createTimespanWhereConditions)({
                table: tables_1.testRunsTable,
                timeColumn: 'createdAt',
                orgId: orgId,
                timespan: restrictionsData?.functional_evals_timespan || 'lifetime',
                extraConditions: (0, drizzle_orm_1.and)((0, drizzle_orm_1.sql) `${tables_1.testRunsTable.type} = ${constants_1.TEST_RUN_TYPE.EVAL}`, (0, drizzle_orm_1.sql) `${tables_1.testRunsTable.status} = ${constants_1.TEST_RUN_STATUS.COMPLETED}`),
            });
            const count = await db
                .select({ count: (0, drizzle_orm_1.sql) `count(*)` })
                .from(tables_1.testRunsTable)
                .where(whereConditions)
                .then((result) => Number(result[0]?.count ?? 0));
            if (count !== 0
                && new decimal_js_1.default(count).gte(restrictionsData?.functional_evals ?? 0)) {
                upgradeMetadata.upgradeRequired = true;
                upgradeMetadata.topUpRequired = false;
                upgradeMetadata.upgradeTitle = restriction_message_1.RESTRICTION_MESSAGE.upgrade.title;
                upgradeMetadata.upgradeMessage = restriction_message_1.RESTRICTION_MESSAGE.upgrade.message;
                upgradeMetadata.currentPlan = restrictionsData?.currentPlan ?? '';
                upgradeMetadata.currentPlanId = restrictionsData?.currentPlanId ?? '';
                globalBanner.show = true;
                globalBanner.data.content = `You're out of allotted functional evaluation runs with your ${restrictionsData?.currentPlan ?? ''} plan. `;
                globalBanner.data.type = 'upgrade';
                globalBanner.data.action = 'Upgrade Now';
                globalBanner.data.actionType = 'upgrade';
                globalBanner.data.actionContent = `to continue evaluating your application`;
            }
        }
        if (!isEnterprise && new decimal_js_1.default(wallet?.credits ?? 0).gt(0) && new decimal_js_1.default(wallet?.credits ?? 0).lte(thresholds?.walletNotificationThreshold ?? 0)) {
            globalBanner.show = true;
            globalBanner.data.content = `You're low on TRU CREDITS`;
            globalBanner.data.type = 'credits';
            globalBanner.data.action = 'Add TRU CREDITS';
            globalBanner.data.actionType = 'credits';
            globalBanner.data.actionContent = `to continue evaluating your application`;
        }
        if (!isEnterprise && new decimal_js_1.default(wallet?.credits ?? 0).lte(0)) {
            globalBanner.show = true;
            globalBanner.data.content = `You're low on TRU CREDITS`;
            globalBanner.data.type = 'credits';
            globalBanner.data.action = 'Add TRU CREDITS';
            globalBanner.data.actionType = 'credits';
            globalBanner.data.actionContent = `to continue evaluating your application`;
            upgradeMetadata.upgradeRequired = false;
            upgradeMetadata.topUpRequired = true;
            upgradeMetadata.upgradeTitle = restriction_message_1.RESTRICTION_MESSAGE.lowCredits.title;
            upgradeMetadata.upgradeMessage = restriction_message_1.RESTRICTION_MESSAGE.lowCredits.message;
            upgradeMetadata.currentPlan = restrictionsData?.currentPlan ?? '';
            upgradeMetadata.currentPlanId = restrictionsData?.currentPlanId ?? '';
        }
        if (isStarter
            && new decimal_js_1.default(usages?.metric ?? 0).gt(0)
            && new decimal_js_1.default(usages?.metric ?? 0).gte(restrictionsData?.metrics ?? 0)) {
            globalBanner.show = true;
            globalBanner.data.content = `You're out of allotted metrics with your ${restrictionsData?.currentPlan ?? ''} plan. `;
            globalBanner.data.type = 'upgrade';
            globalBanner.data.action = 'Upgrade Now';
            globalBanner.data.actionType = 'upgrade';
            globalBanner.data.actionContent = `to run more functional evals`;
            upgradeMetadata.upgradeRequired = true;
            upgradeMetadata.topUpRequired = false;
            upgradeMetadata.upgradeTitle = restriction_message_1.RESTRICTION_MESSAGE.metrics.title;
            upgradeMetadata.upgradeMessage = restriction_message_1.RESTRICTION_MESSAGE.metrics.message;
            upgradeMetadata.currentPlan = restrictionsData?.currentPlan ?? '';
            upgradeMetadata.currentPlanId = restrictionsData?.currentPlanId ?? '';
        }
        const result = {
            data: formattedTestRuns,
            pagination: {
                total: Number(totalCount[0].count),
                page: parsedPage,
                limit: parsedLimit,
                totalPages: Math.ceil(Number(totalCount[0].count) / parsedLimit),
            },
            upgradeMetadata,
            globalBanner,
        };
        res.json({
            success: true,
            data: result,
        });
    }
    catch (error) {
        logger_1.default.error(`Failed to fetch test run list: ${error.message}`);
        res.status(500).json({
            success: false,
            message: error.message || 'Failed to fetch test run list',
        });
    }
};
exports.getTestRunList = getTestRunList;
//# sourceMappingURL=get-result-list.js.map