"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeTestRunTable = exports.getTestRunTable = exports.getTestRunDetails = exports.fetchResultDetails = exports.fetchTestRunTable = exports.findApplicationFromProvider = exports.calculateMetricsAndFailures = exports.CalulateTotalStats = exports.getEvalTables = exports.calculateReviewCountsFromDatabase = void 0;
const drizzle_orm_1 = require("drizzle-orm");
const drizzle_orm_2 = require("drizzle-orm");
const database_1 = require("../../../database");
const tables_1 = require("../../../database/tables");
const tables_2 = require("../../../database/tables");
const logger_1 = __importDefault(require("../../../logger"));
const testRun_1 = require("../../../models/testRun");
const get_upgrade_meta_1 = require("../../../util/get-upgrade-meta");
const restriction_message_1 = require("../../constants/restriction.message");
const calculateReviewCountsFromDatabase = async (db, resultId) => {
    // Get all eval IDs associated with this test run
    const testRunEvals = await db
        .select({ evalId: tables_1.testRunsToEvalsTable.evalId })
        .from(tables_1.testRunsToEvalsTable)
        .where((0, drizzle_orm_2.eq)(tables_1.testRunsToEvalsTable.testRunId, resultId));
    if (testRunEvals.length === 0) {
        return { total: 0, reviewed: 0 };
    }
    const evalIds = testRunEvals.map((rel) => rel.evalId);
    // Get all eval results for these evals
    const evalResults = await db
        .select({
        id: tables_1.evalResultsTable.id,
        gradingResult: tables_1.evalResultsTable.gradingResult,
    })
        .from(tables_1.evalResultsTable)
        .where((0, drizzle_orm_2.inArray)(tables_1.evalResultsTable.evalId, evalIds));
    // Calculate counts
    const total = evalResults.length;
    const reviewed = evalResults.filter((result) => result.gradingResult?.reviewedBy).length;
    return { total, reviewed };
};
exports.calculateReviewCountsFromDatabase = calculateReviewCountsFromDatabase;
const getEvalTables = async (params) => {
    const { resultId, filter } = params;
    {
        /* const evals = await db
        .select({
          evalId: testRunsToEvalsTable.evalId,
        })
        .from(testRunsToEvalsTable)
        .where(eq(testRunsToEvalsTable.testRunId, resultId));
    
      const evalLists: any =
        (await Promise.all(
          evals?.map((evalItem: { evalId: string }) => readResult(evalItem?.evalId)) ?? [],
        )) ?? [];
    
      // Get tables for each eval
      const evalTables: any = await Promise.all(
        evalLists?.map(async (evalItem: any) => {
          const eval_ = await Eval.findById(evalItem?.id);
          if (!eval_) {
            logger.error(`No evaluation found with ID ${evalItem?.id}`);
            return null;
          }
          const table = await eval_.getTable();
          return {
            evalId: evalItem?.id,
            table,
          };
        }),
      );
        */
    }
    const testRun_ = await testRun_1.TestRun.findById(resultId);
    if (!testRun_) {
        logger_1.default.error(`No test run found with ID ${resultId}`);
        return null;
    }
    const TestRunTable = await testRun_.getTable(filter);
    return { TestRunTable };
};
exports.getEvalTables = getEvalTables;
const CalulateTotalStats = (tableBody) => {
    return tableBody?.reduce((acc, result) => {
        result?.outputs?.map((output) => {
            acc.total += 1;
            if (output?.gradingResult) {
                if (output?.gradingResult?.pass) {
                    acc.successes += 1;
                }
                else {
                    acc.failures += 1;
                }
            }
            else {
                acc.errors += 1;
            }
        });
        return acc;
    }, { total: 0, successes: 0, failures: 0, errors: 0 });
};
exports.CalulateTotalStats = CalulateTotalStats;
const calculateMetricsAndFailures = (tableBody, applicationName) => {
    const metrics = {
        totalAssertions: 0,
        passedAssertions: 0,
        failedAssertions: 0,
        assertionTypes: {},
        failureDetails: [],
    };
    tableBody?.forEach((result, rowIndex) => {
        const testCase = result?.prompt || `Test Case ${rowIndex + 1}`;
        result?.outputs?.forEach((output, outputIndex) => {
            const provider = output?.provider || `Provider ${outputIndex + 1}`;
            if (provider === applicationName && output?.gradingResult?.componentResults) {
                output.gradingResult.componentResults.forEach((componentResult) => {
                    const assertionType = componentResult?.assertion?.displayLabel || componentResult?.assertion?.type;
                    if (componentResult?.assertion?.type === 'human') {
                        return; // This will safely skip processing this item in forEach
                    }
                    // Initialize assertion type metrics if not present
                    if (!metrics.assertionTypes[assertionType]) {
                        metrics.assertionTypes[assertionType] = {
                            total: 0,
                            passed: 0,
                            failed: 0,
                            error: 0,
                            averageScore: 0,
                        };
                    }
                    // Count total assertions
                    metrics.totalAssertions++;
                    metrics.assertionTypes[assertionType].total++;
                    // Count passed/failed assertions
                    if (componentResult?.pass) {
                        metrics.passedAssertions++;
                        metrics.assertionTypes[assertionType].passed++;
                    }
                    else {
                        metrics.failedAssertions++;
                        metrics.assertionTypes[assertionType].failed++;
                        // Add failure details
                        metrics.failureDetails.push({
                            testCase,
                            provider,
                            assertionType,
                            reason: componentResult?.reason || 'No reason provided',
                            score: componentResult?.score || 0,
                        });
                    }
                });
            }
        });
    });
    // Calculate average scores for each assertion type
    Object.keys(metrics.assertionTypes).forEach((assertionType) => {
        const typeData = metrics.assertionTypes[assertionType];
        if (typeData.total > 0) {
            typeData.averageScore = typeData.passed / typeData.total;
        }
        else {
            typeData.averageScore = 0;
        }
    });
    return metrics;
};
exports.calculateMetricsAndFailures = calculateMetricsAndFailures;
const findApplicationFromProvider = (applications, providerId) => {
    return applications?.find((app) => {
        const appProvider = app?.provider_config;
        if (!appProvider) {
            return false;
        }
        // For custom providers, match by apiUrl or webhook
        if (appProvider?.provider_type === 'custom' && 'http' === providerId) {
            return true;
        }
        else if (appProvider?.provider_type === 'llm' &&
            appProvider?.config?.provider?.id === providerId) {
            return true;
        }
        else if (app?.name === providerId) {
            return true;
        }
        return false;
        // For LLM providers, match by id
    });
};
exports.findApplicationFromProvider = findApplicationFromProvider;
const fetchTestRunTable = async (params) => {
    const { db, resultId, testRun, offset, parsedLimit, parsedPage, filter, orgId } = params;
    // Calculate review counts directly from database for accurate totals
    const reviewCounts = await (0, exports.calculateReviewCountsFromDatabase)(db, resultId);
    // Now get filtered data for display
    const evalData = await (0, exports.getEvalTables)({
        db,
        resultId,
        filter,
    });
    if (!evalData) {
        return null;
    }
    const { TestRunTable } = evalData;
    const tableBody = TestRunTable?.body || [];
    // Prepare applicationIds and libraryId for lookup
    const applicationIds = testRun[0]?.applicationIds ?? [];
    // Fetch applications
    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,
            provider_config: tables_1.applicationsTable.provider_config,
        })
            .from(tables_1.applicationsTable)
            .where((0, drizzle_orm_1.sql) `${tables_1.applicationsTable.id}::uuid  = ANY(${appLiteral})`);
    }
    const uniqueProviders = new Set(TestRunTable?.head?.prompts?.map((headItem) => headItem?.provider));
    const tableHead = Array.from(uniqueProviders)?.map((item) => {
        const providerApplication = (0, exports.findApplicationFromProvider)(applications, item);
        return {
            provider: item,
            applicationName: providerApplication?.name,
        };
    });
    const totalCount = tableBody?.length || 0;
    // Apply pagination to tableBody
    const paginatedTableBody = tableBody?.slice(offset, offset + parsedLimit) || [];
    const finalBody = paginatedTableBody?.map((body, rowIndex) => ({
        ...body,
        outputs: body?.outputs?.map((output) => ({
            ...output,
            prompt: output?.providerId === 'phone' ? 'Phone Call' : output?.prompt,
        })),
        prompt: body?.outputs?.[0]?.providerId === 'phone' ? 'Phone Call' : body?.outputs?.[0]?.prompt,
        rowIndex: offset + rowIndex,
    }));
    const { upgradeMetadata, isStarter, restrictionsData, } = await (0, get_upgrade_meta_1.getUpgradeMetaByOrgId)(orgId);
    if (isStarter) {
        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 ?? '';
    }
    return {
        resultTable: {
            head: {
                ...TestRunTable?.head,
                columns: tableHead,
            },
            body: finalBody,
        },
        pagination: {
            total: totalCount,
            page: parsedPage,
            limit: parsedLimit,
            totalPages: Math.ceil(totalCount / parsedLimit),
        },
        reviewCounts,
        upgradeMetadata,
    };
};
exports.fetchTestRunTable = fetchTestRunTable;
const fetchResultDetails = async (params) => {
    const { db, testRun, resultId, applicationId = null } = params;
    const evalData = await (0, exports.getEvalTables)({
        db,
        resultId,
    });
    if (!evalData) {
        return null;
    }
    const { TestRunTable } = evalData;
    // Prepare applicationIds and libraryId for lookup
    const applicationIds = testRun[0]?.applicationIds ?? [];
    const libraryId = testRun[0]?.libraryId;
    // Fetch applications
    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,
            provider_config: tables_1.applicationsTable.provider_config,
        })
            .from(tables_1.applicationsTable)
            .where((0, drizzle_orm_1.sql) `${tables_1.applicationsTable.id}::uuid  = ANY(${appLiteral})`);
    }
    // Fetch library
    let library = null;
    let totalPrompts = null;
    if (libraryId) {
        const libraryLiteral = drizzle_orm_1.sql.raw(`ARRAY['${libraryId}']::uuid[]`);
        const 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})`);
        library = libraries[0]?.name || null;
        // Calculate total number of prompts for this libraryId from the prompts table by joining promptsLibraryPromptsTable
        const promptCountResult = await db
            .select({ count: (0, drizzle_orm_1.sql) `count(*)` })
            .from(tables_1.promptsLibraryPromptsTable)
            .where((0, drizzle_orm_2.eq)(tables_1.promptsLibraryPromptsTable.libraryId, libraries[0]?.id));
        totalPrompts = promptCountResult[0]?.count ?? 0;
    }
    // Calculate total success and failure counts from all evals
    const totalStats = (0, exports.CalulateTotalStats)(TestRunTable?.body);
    // Calculate detailed metrics and failure analysis
    const reportsData = (0, exports.calculateMetricsAndFailures)(TestRunTable?.body, applications?.find((app) => app?.id === applicationId)?.name);
    const reviewCounts = await (0, exports.calculateReviewCountsFromDatabase)(db, testRun[0]?.id);
    // Format the response
    return {
        id: testRun[0]?.id,
        createdAt: testRun[0]?.createdAt,
        status: testRun[0]?.status,
        shareableReportAccessKey: testRun[0]?.shareableReportAccessKey,
        shareableReportExpiry: testRun[0]?.shareableReportExpiry,
        applicationsArray: applications?.map((app) => ({ name: app?.name, id: app?.id })),
        defaultApplication: applications?.find((app) => app?.id === applicationId)?.id,
        applications: applications?.map((app) => app?.name),
        applicationIds: applications?.map((app) => app?.id),
        promptLibrary: library ?? '-',
        totalPrompts,
        stats: totalStats,
        reportsData,
        reviewCounts,
    };
};
exports.fetchResultDetails = fetchResultDetails;
const fetchResultSummary = async (resultId) => {
    const parsedLimit = 999;
    const parsedPage = 1;
    const offset = (parsedPage - 1) * parsedLimit;
    const db = await (0, database_1.getDb)();
    const testRun = await db
        .select({
        applicationIds: tables_2.testRunsTable.applicationIds,
        libraryId: tables_2.testRunsTable.libraryId,
    })
        .from(tables_2.testRunsTable)
        .where((0, drizzle_orm_2.eq)(tables_2.testRunsTable.id, resultId))
        .limit(1);
    const fetchParams = { db, resultId, testRun, parsedLimit, parsedPage, offset, filter: 'all' };
    const fetchedTableResult = await (0, exports.fetchTestRunTable)(fetchParams);
    const table = fetchedTableResult?.resultTable;
    const testRunDetails = await (0, exports.fetchResultDetails)({
        db,
        resultId,
        testRun,
        applicationId: testRun[0].applicationIds[0],
    });
    return {
        id: resultId,
        table,
        summary: {
            table,
            id: testRunDetails?.id,
            createdAt: testRunDetails?.createdAt,
            status: testRunDetails?.status,
            applications: testRunDetails?.applications,
            promptLibrary: testRunDetails?.promptLibrary,
            totalPrompts: testRunDetails?.totalPrompts,
            stats: testRunDetails?.stats,
        },
        config: {},
    };
};
const getTestRunDetails = async (req, res) => {
    try {
        const { resultId } = req.params;
        const { applicationId = null, accessKey = null } = req.query;
        const db = await (0, database_1.getDb)();
        const testRun = await db
            .select({
            id: tables_2.testRunsTable.id,
            createdAt: tables_2.testRunsTable.createdAt,
            status: tables_2.testRunsTable.status,
            applicationIds: tables_2.testRunsTable.applicationIds,
            libraryId: tables_2.testRunsTable.libraryId,
            shareableReportExpiry: tables_2.testRunsTable.shareableReportExpiry,
            shareableReportAccessKey: tables_2.testRunsTable.shareableReportAccessKey,
        })
            .from(tables_2.testRunsTable)
            .where((0, drizzle_orm_2.eq)(tables_2.testRunsTable.id, resultId))
            .limit(1);
        if (!testRun.length) {
            res.status(404).json({
                success: false,
                message: 'Test run not found',
            });
            return;
        }
        if (accessKey) {
            if (testRun[0]?.shareableReportAccessKey !== accessKey) {
                res.status(401).json({
                    success: false,
                    message: 'Invalid access key or report expired',
                });
                return;
            }
            if (testRun[0]?.shareableReportExpiry && testRun[0]?.shareableReportExpiry < new Date()) {
                res.status(401).json({
                    success: false,
                    message: 'Shareable report expired',
                });
                return;
            }
        }
        const fetchParams = {
            db,
            resultId,
            testRun,
            applicationId: applicationId ?? testRun[0]?.applicationIds[0],
        };
        const response = await (0, exports.fetchResultDetails)(fetchParams);
        res.json({
            success: true,
            data: response,
        });
    }
    catch (error) {
        logger_1.default.error(`Failed to fetch test run details: ${error.message}`);
        res.status(500).json({
            success: false,
            message: error.message || 'Failed to fetch test run details',
        });
    }
};
exports.getTestRunDetails = getTestRunDetails;
const getTestRunTable = async (req, res) => {
    try {
        const { resultId } = req.params;
        const { page = '1', limit = '10', filter = 'all', orgId } = req.query;
        const parsedLimit = Number(limit);
        const parsedPage = Number(page);
        const offset = (parsedPage - 1) * parsedLimit;
        const db = await (0, database_1.getDb)();
        const testRun = await db
            .select({
            id: tables_2.testRunsTable.id,
            createdAt: tables_2.testRunsTable.createdAt,
            status: tables_2.testRunsTable.status,
            applicationIds: tables_2.testRunsTable.applicationIds,
            libraryId: tables_2.testRunsTable.libraryId,
            shareableReportExpiry: tables_2.testRunsTable.shareableReportExpiry,
        })
            .from(tables_2.testRunsTable)
            .where((0, drizzle_orm_2.eq)(tables_2.testRunsTable.id, resultId))
            .limit(1);
        if (!testRun.length) {
            res.status(404).json({
                success: false,
                message: 'Test run not found',
            });
            return;
        }
        const fetchParams = { db, resultId, testRun, parsedLimit, parsedPage, offset, filter, orgId };
        const response = await (0, exports.fetchTestRunTable)(fetchParams);
        if (!response) {
            res.status(404).json({
                success: false,
                message: 'Test run table not found',
            });
            return;
        }
        res.json({
            success: true,
            data: response,
        });
    }
    catch (error) {
        logger_1.default.error(`Failed to fetch test run table: ${error.message}`);
        res.status(500).json({
            success: false,
            message: error.message || 'Failed to fetch test run table',
        });
    }
};
exports.getTestRunTable = getTestRunTable;
const writeTestRunTable = async (req, res) => {
    try {
        const { resultId } = req.params;
        const response = await fetchResultSummary(resultId);
        res.json({
            success: true,
            data: response,
        });
    }
    catch (error) {
        logger_1.default.error(`Failed to fetch test run details: ${error.message}`);
        res.status(500).json({
            success: false,
            message: error.message || 'Failed to fetch test run details',
        });
    }
};
exports.writeTestRunTable = writeTestRunTable;
//# sourceMappingURL=get-result-details.js.map