"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRedteamReportDrawerResults = 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 evalResult_1 = __importDefault(require("../../../models/evalResult"));
const types_1 = require("../../../types");
const redteam_report_shared_1 = require("./redteam-report-shared");
/** Express query values are strings; clients sometimes send literal "undefined"/"null". */
function queryString(v) {
    if (v == null) {
        return undefined;
    }
    const s = Array.isArray(v) ? v[0] : v;
    if (typeof s !== 'string') {
        return undefined;
    }
    const t = s.trim();
    if (t === '' || t === 'undefined' || t === 'null') {
        return undefined;
    }
    return t;
}
function normalizeExecuteRows(result) {
    if (Array.isArray(result)) {
        return result;
    }
    if (result !== null &&
        typeof result === 'object' &&
        'rows' in result &&
        Array.isArray(result.rows)) {
        return result.rows;
    }
    return [];
}
/**
 * Json metadata may be NULL; treat as {} for ->> extractions.
 * Non-basic: effective strategy matches (same coalesce as getStrategyIdFromTest-style lookup).
 * Basic: rows where strategyId is not stored on metadata (basic attacks omit it).
 */
function strategyFilter(strategy) {
    const metaStrategy = (0, drizzle_orm_1.sql) `NULLIF(TRIM(COALESCE(COALESCE(${tables_1.evalResultsTable.metadata}, '{}'::jsonb)->>'strategyId', '')), '')`;
    if (strategy === 'basic') {
        return (0, drizzle_orm_1.sql) `${metaStrategy} IS NULL`;
    }
    return (0, drizzle_orm_1.sql) `COALESCE(NULLIF(COALESCE(${tables_1.evalResultsTable.metadata}, '{}'::jsonb)->>'strategyId', ''), NULLIF(${tables_1.evalResultsTable.testCase}->'metadata'->>'strategyId', ''), 'basic') = ${strategy}`;
}
/** Full plugin id or short form without trusys:redteam: prefix. */
function pluginMatches(plugin) {
    const meta = (0, drizzle_orm_1.sql) `COALESCE(${tables_1.evalResultsTable.metadata}, '{}'::jsonb)`;
    return (0, drizzle_orm_1.or)((0, drizzle_orm_1.eq)((0, drizzle_orm_1.sql) `${meta}->>'pluginId'`, plugin), (0, drizzle_orm_1.eq)((0, drizzle_orm_1.sql) `replace((${meta}->>'pluginId')::text, 'trusys:redteam:', '')`, plugin));
}
function bucketMatches(bucket) {
    if (bucket === 'flagged') {
        return (0, drizzle_orm_1.and)((0, drizzle_orm_1.ne)(tables_1.evalResultsTable.failureReason, types_1.ResultFailureReason.ERROR), (0, drizzle_orm_1.sql) `coalesce((${tables_1.evalResultsTable.gradingResult}->>'pass')::boolean, false) = false`);
    }
    return (0, drizzle_orm_1.sql) `coalesce((${tables_1.evalResultsTable.gradingResult}->>'pass')::boolean, false) = true`;
}
const getRedteamReportDrawerResults = async (req, res) => {
    try {
        const { resultId } = req.params;
        const plugin = queryString(req.query.plugin);
        const strategy = queryString(req.query.strategy);
        const bucket = queryString(req.query.bucket);
        const aggregate = queryString(req.query.aggregate);
        const page = queryString(req.query.page) ?? '1';
        const limit = queryString(req.query.limit) ?? '100';
        if (!resultId) {
            res.status(400).json({ error: 'resultId is required' });
            return;
        }
        const db = await (0, database_1.getDb)();
        const rows = await db
            .select({
            testRunId: tables_1.testRunsTable.id,
            evalId: tables_1.testRunsToEvalsTable.evalId,
        })
            .from(tables_1.testRunsTable)
            .leftJoin(tables_1.testRunsToEvalsTable, (0, drizzle_orm_1.eq)(tables_1.testRunsTable.id, tables_1.testRunsToEvalsTable.testRunId))
            .where((0, drizzle_orm_1.eq)(tables_1.testRunsTable.id, resultId))
            .limit(1);
        if (!rows.length) {
            res.status(404).json({ error: 'Redteam result not found' });
            return;
        }
        const evalId = rows[0]?.evalId;
        if (!evalId) {
            res.status(200).json({
                data: { items: [], total: 0, page: 1, limit: 25, pluginStats: undefined },
            });
            return;
        }
        const [evalRow] = await db
            .select({ config: (0, drizzle_orm_1.sql) `${tables_1.evalsTable.config} -> 'redteam'`, })
            .from(tables_1.evalsTable)
            .where((0, drizzle_orm_1.eq)(tables_1.evalsTable.id, evalId))
            .limit(1);
        const resultDetails = { config: evalRow?.config ?? {} };
        if (aggregate === 'pluginStats') {
            if (!strategy) {
                res.status(400).json({ error: 'strategy is required for aggregate=pluginStats' });
                return;
            }
            const aggRows = normalizeExecuteRows(strategy === 'basic'
                ? await db.execute((0, drizzle_orm_1.sql) `
              SELECT
                COALESCE(metadata, '{}'::jsonb)->>'pluginId' AS plugin,
                SUM(
                  CASE
                    WHEN NOT COALESCE((grading_result->>'pass')::boolean, false)
                      AND failure_reason != ${types_1.ResultFailureReason.ERROR}
                    THEN 1
                    ELSE 0
                  END
                )::int AS passes,
                COUNT(*)::int AS total
              FROM eval_results
              WHERE eval_id = ${evalId}
                AND NULLIF(TRIM(COALESCE(COALESCE(metadata, '{}'::jsonb)->>'strategyId', '')), '') IS NULL
              GROUP BY COALESCE(metadata, '{}'::jsonb)->>'pluginId'
              HAVING COALESCE(metadata, '{}'::jsonb)->>'pluginId' IS NOT NULL
            `)
                : await db.execute((0, drizzle_orm_1.sql) `
              SELECT
                COALESCE(metadata, '{}'::jsonb)->>'pluginId' AS plugin,
                SUM(
                  CASE
                    WHEN NOT COALESCE((grading_result->>'pass')::boolean, false)
                      AND failure_reason != ${types_1.ResultFailureReason.ERROR}
                    THEN 1
                    ELSE 0
                  END
                )::int AS passes,
                COUNT(*)::int AS total
              FROM eval_results
              WHERE eval_id = ${evalId}
                AND COALESCE(
                  NULLIF(COALESCE(metadata, '{}'::jsonb)->>'strategyId', ''),
                  NULLIF(test_case->'metadata'->>'strategyId', ''),
                  'basic'
                ) = ${strategy}
              GROUP BY COALESCE(metadata, '{}'::jsonb)->>'pluginId'
              HAVING COALESCE(metadata, '{}'::jsonb)->>'pluginId' IS NOT NULL
            `));
            const pluginStats = [];
            for (const row of aggRows) {
                const p = row.plugin;
                const passes = Number(row.passes ?? 0);
                const total = Number(row.total ?? 0);
                if (typeof p !== 'string' || !p) {
                    continue;
                }
                pluginStats.push({
                    plugin: p,
                    passes,
                    total,
                    failRate: total > 0 ? (passes / total) * 100 : 0,
                });
            }
            pluginStats.sort((a, b) => a.failRate - b.failRate);
            res.json({ data: { pluginStats, items: [], total: 0, page: 1, limit: 0 } });
            return;
        }
        if (!bucket || (bucket !== 'flagged' && bucket !== 'passed')) {
            res.status(400).json({ error: 'bucket must be flagged or passed' });
            return;
        }
        const pageNum = Math.max(1, Number.parseInt(String(page), 10) || 1);
        const limitNum = Math.min(500, Math.max(1, Number.parseInt(String(limit), 10) || 100));
        const whereParts = [
            (0, drizzle_orm_1.eq)(tables_1.evalResultsTable.evalId, evalId),
            bucketMatches(bucket),
        ];
        if (strategy) {
            whereParts.push(strategyFilter(strategy));
        }
        if (plugin) {
            whereParts.push(pluginMatches(plugin));
        }
        const whereClause = (0, drizzle_orm_1.and)(...whereParts);
        const [countRow] = await db.select({ total: (0, drizzle_orm_1.count)() }).from(tables_1.evalResultsTable).where(whereClause);
        const total = Number(countRow?.total ?? 0);
        const offset = (pageNum - 1) * limitNum;
        const dbRows = await db
            .select()
            .from(tables_1.evalResultsTable)
            .where(whereClause)
            .orderBy((0, drizzle_orm_1.asc)(tables_1.evalResultsTable.testIdx))
            .limit(limitNum)
            .offset(offset);
        const items = dbRows.map((row) => {
            const er = new evalResult_1.default({
                ...row,
                failureReason: (row.failureReason ?? types_1.ResultFailureReason.NONE),
            });
            const r = er.toEvaluateResult();
            return (0, redteam_report_shared_1.mapRowToDrawerItem)(r, resultDetails, bucket);
        });
        res.json({
            data: {
                items,
                total,
                page: pageNum,
                limit: limitNum,
            },
        });
    }
    catch (error) {
        logger_1.default.error(`Error fetching redteam drawer results: ${error}`);
        res.status(500).json({ error: 'Failed to fetch redteam drawer results' });
    }
};
exports.getRedteamReportDrawerResults = getRedteamReportDrawerResults;
//# sourceMappingURL=get-redteam-report-drawer-results.js.map