"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.pulseAlertCheckCronService = exports.PulseAlertCheckCronService = void 0;
const date_fns_1 = require("date-fns");
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 callControlPlaneApi_1 = require("../../util/callContolPlane/callControlPlaneApi");
const clickhouseService_1 = require("./clickhouseService");
const templates_1 = require("../../util/templates");
const constants_1 = require("../../constants");
const alertsService_1 = require("./alertsService");
const uuid_1 = require("uuid");
// Table names from pulseService
const EVAL_RESULTS_TABLE_NAME = 'eval_results';
const OTEL_TRACES_TABLE_NAME = 'otel_traces';
const GUARDRAIL_RESULTS_TABLE_NAME = 'guardrail_results';
// Format dates for ClickHouse compatibility
const formatDateForClickHouse = (date) => {
    return date.toISOString().replace('T', ' ').replace('Z', '');
};
class PulseAlertCheckCronService {
    constructor() {
        this.logger = logger_1.default;
    }
    /**
     * Calculate time window based on comparator and type
     */
    calculateTimeWindow(metricComparator, metricComparatorType) {
        const now = new Date();
        let start;
        if (metricComparatorType === 'minutes') {
            start = (0, date_fns_1.subMinutes)(now, metricComparator);
        }
        else if (metricComparatorType === 'hour') {
            start = (0, date_fns_1.subHours)(now, metricComparator);
        }
        else if (metricComparatorType === 'days') {
            start = (0, date_fns_1.subDays)(now, metricComparator);
        }
        else if (metricComparatorType === 'weeks') {
            start = (0, date_fns_1.subWeeks)(now, metricComparator);
        }
        else if (metricComparatorType === 'months') {
            start = (0, date_fns_1.subMonths)(now, metricComparator);
        }
        else {
            // Default to minutes
            start = (0, date_fns_1.subMinutes)(now, metricComparator);
        }
        return {
            start: formatDateForClickHouse(start),
            end: formatDateForClickHouse(now),
        };
    }
    getFormattedTimestamp() {
        const now = new Date();
        const pad = (n) => n.toString().padStart(2, '0');
        const day = pad(now.getDate());
        const month = pad(now.getMonth() + 1);
        const year = now.getFullYear().toString().slice(-2);
        const hours = pad(now.getHours());
        const minutes = pad(now.getMinutes());
        return `${day}-${month}-${year} ${hours}:${minutes}`;
    }
    /**
     * Get fail count from PostgreSQL eval_results table for functionalEvaluation and securityEvaluation
     */
    async getFailCountFromDatabase(params) {
        const { applicationId, projectId } = params;
        try {
            const db = await (0, database_1.getDb)();
            // Use fixed 5-minute time window for functionalEvaluation/securityEvaluation
            const now = new Date();
            const startDate = (0, date_fns_1.subMinutes)(now, 5);
            const endDate = now;
            // Ensure dates are proper Date objects
            const startDateObj = startDate instanceof Date ? startDate : new Date(startDate);
            const endDateObj = endDate instanceof Date ? endDate : new Date(endDate);
            // Build query conditions - for functionalEvaluation/securityEvaluation, get all failed evaluations
            const conditions = [
                (0, drizzle_orm_1.eq)(tables_1.evalResultsTable.applicationId, applicationId),
                (0, drizzle_orm_1.gte)(tables_1.evalResultsTable.createdAt, startDateObj),
                (0, drizzle_orm_1.lte)(tables_1.evalResultsTable.createdAt, endDateObj),
                (0, drizzle_orm_1.eq)(tables_1.evalResultsTable.success, 0), // Only failed evaluations
            ];
            if (params.type === constants_1.ALERT_TYPES.SECURITY_EVALUATION) {
                conditions.push((0, drizzle_orm_1.eq)(tables_1.evalResultsTable.isRedteam, 1));
            }
            else {
                conditions.push((0, drizzle_orm_1.eq)(tables_1.evalResultsTable.isRedteam, 0));
            }
            if (projectId) {
                conditions.push((0, drizzle_orm_1.eq)(tables_1.evalResultsTable.projectId, projectId));
            }
            // Query eval results with testRunId and testRun type - get all failed evaluations (success = 0)
            const evalResults = await db
                .select({
                id: tables_1.evalResultsTable.id,
                testCase: tables_1.evalResultsTable.testCase,
                response: tables_1.evalResultsTable.response,
                prompt: tables_1.evalResultsTable.prompt,
                applicationId: tables_1.evalResultsTable.applicationId,
                projectId: tables_1.evalResultsTable.projectId,
                workspaceId: tables_1.evalResultsTable.workspaceId,
                evalId: tables_1.evalResultsTable.evalId,
                gradingResult: tables_1.evalResultsTable.gradingResult,
                success: tables_1.evalResultsTable.success,
                metadata: tables_1.evalResultsTable.metadata,
                isRedteam: tables_1.evalResultsTable.isRedteam,
                testRunId: tables_1.testRunsToEvalsTable.testRunId,
                testRunType: tables_1.testRunsTable.type,
            })
                .from(tables_1.evalResultsTable)
                .leftJoin(tables_1.testRunsToEvalsTable, (0, drizzle_orm_1.eq)(tables_1.evalResultsTable.evalId, tables_1.testRunsToEvalsTable.evalId))
                .leftJoin(tables_1.testRunsTable, (0, drizzle_orm_1.eq)(tables_1.testRunsToEvalsTable.testRunId, tables_1.testRunsTable.id))
                .where((0, drizzle_orm_1.and)(...conditions));
            let failCount = 0;
            const TestRunsWithFailedResults = [];
            // Group eval results by testRunId
            const testRunGroups = new Map();
            for (const result of evalResults) {
                if (result.success === 0 && result.testRunId) {
                    if (!testRunGroups.has(result.testRunId)) {
                        testRunGroups.set(result.testRunId, []);
                    }
                    testRunGroups.get(result.testRunId).push(result);
                }
            }
            // Process each testRun group
            for (const [testRunId, testRunEvalResults] of testRunGroups.entries()) {
                const failedMetricNames = new Set();
                let totalTestCaseFailCount = 0;
                const failedResults = [];
                // Process all evalResults in this testRun group
                for (const result of testRunEvalResults) {
                    const gradingResult = result?.gradingResult;
                    const componentResults = gradingResult?.componentResults || [];
                    let currentResultFailed = false;
                    if (Array.isArray(componentResults) && componentResults.length > 0) {
                        // Get all failed metrics from componentResults
                        for (const component of componentResults) {
                            const parsed = typeof component === 'string' ? JSON.parse(component) : component;
                            // If component failed, add it to traces and count it
                            if (parsed?.pass === false) {
                                const assertionType = parsed.assertion?.type || '';
                                const displayLabel = parsed.assertion?.displayLabel || assertionType;
                                const metricName = displayLabel || assertionType || 'Unknown';
                                // Collect unique failed metric names for assertion_name
                                if (metricName && !failedMetricNames.has(metricName)) {
                                    failedMetricNames.add(metricName);
                                }
                                totalTestCaseFailCount++;
                                currentResultFailed = true;
                            }
                        }
                    }
                    if (currentResultFailed) {
                        failedResults.push(result);
                    }
                }
                // If there are any failed test cases in this testRun, add to traces and increment failCount
                if (totalTestCaseFailCount > 0) {
                    // Determine isRedteam from testRunType (first result's testRunType since all in group have same testRun)
                    const isRedteam = testRunEvalResults[0]?.testRunType === constants_1.TEST_RUN_TYPE.SECURITY;
                    TestRunsWithFailedResults.push({
                        failedResults,
                        failedMetricNames,
                        testCaseFailCount: totalTestCaseFailCount,
                        isRedteam,
                        testRunId,
                    });
                    failCount++; // Increment at testRun level
                }
            }
            return { failCount, TestRunsWithFailedResults };
        }
        catch (error) {
            this.logger.error(`Error querying database for alert check: ${error}`);
            return { failCount: 0, TestRunsWithFailedResults: [] };
        }
    }
    /**
     * Get fail count from ClickHouse eval_results table
     */
    async getFailCountFromClickHouse(params) {
        const { applicationId, metricType, subType, timeWindow, type, metricValue = 0 } = params;
        try {
            let query = '';
            if (type === constants_1.ALERT_TYPES.PULSE_SYSTEM_HEALTH && subType === 'noLogs') {
                // For log metric type "noLogs", check if there are no traces in the time window
                query = `
          SELECT 
            CASE 
              WHEN COUNT(DISTINCT TraceId) = 0 THEN 1 
              ELSE 0 
            END as fail_count,
            [] as traces
          FROM ${OTEL_TRACES_TABLE_NAME}
          WHERE ResourceAttributes['service.name'] = '${applicationId}'
          AND Timestamp >= '${timeWindow.start}'
          AND Timestamp <= '${timeWindow.end}'
        `;
            }
            else if (type === constants_1.ALERT_TYPES.PULSE_SYSTEM_HEALTH && subType === 'cost') {
                // For cost metric type, sum all costs in the time window
                query = `
          SELECT 
            COALESCE(SUM(toFloat64OrNull(SpanAttributes['gen_ai.usage.cost'])), 0) as fail_count,
            [] as traces
          FROM ${OTEL_TRACES_TABLE_NAME}
          WHERE ResourceAttributes['service.name'] = '${applicationId}'
          AND Timestamp >= '${timeWindow.start}'
          AND Timestamp <= '${timeWindow.end}'
          AND SpanAttributes['gen_ai.usage.cost'] IS NOT NULL
          AND SpanAttributes['gen_ai.usage.cost'] != ''
        `;
            }
            else if (type === 'metric') {
                // For metric types, count failed evaluations based on componentResults
                query = `
           SELECT 
             SUM(arrayCount(
               x -> JSONExtractBool(x, 'pass') = false 
               AND JSONExtractString(x, 'assertion', 'type') = '${metricType}',
               JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
             )) as fail_count,
             groupArray(
               JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
             ) as traces,
             groupArray(trace_id) as trace_ids
           FROM ${EVAL_RESULTS_TABLE_NAME}
           WHERE application_id = '${applicationId}'
           AND created_at >= '${timeWindow.start}'
           AND created_at <= '${timeWindow.end}'
           AND is_redteam = 0
           AND JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)') IS NOT NULL
           AND arrayExists(
             x -> JSONExtractBool(x, 'pass') = false 
             AND JSONExtractString(x, 'assertion', 'type') = '${metricType}',
             JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
           )
         `;
            }
            else if (type === 'plugins') {
                // For plugin types, count failed security evaluations
                query = `
           SELECT 
             SUM(arrayCount(
               x -> JSONExtractBool(x, 'pass') = false 
               AND replace(JSONExtractString(x, 'assertion', 'type'), 'trusys:redteam:', '') = '${metricType}',
               JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
             )) as fail_count,
             groupArray(
               JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
             ) as traces,
             groupArray(trace_id) as trace_ids
           FROM ${EVAL_RESULTS_TABLE_NAME}
           WHERE application_id = '${applicationId}'
           AND created_at >= '${timeWindow.start}'
           AND created_at <= '${timeWindow.end}'
           AND is_redteam = 1
           AND JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)') IS NOT NULL
           AND arrayExists(
             x -> JSONExtractBool(x, 'pass') = false 
             AND replace(JSONExtractString(x, 'assertion', 'type'), 'trusys:redteam:', '') = '${metricType}',
             JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
           )
         `;
            }
            else {
                // Default query for unknown types
                query = `
           SELECT 
             SUM(arrayCount(
               x -> JSONExtractBool(x, 'pass') = false 
               AND JSONExtractString(x, 'assertion', 'type') = '${metricType}',
               JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
             )) as fail_count,
             groupArray(
               JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
             ) as traces,
             groupArray(trace_id) as trace_ids
           FROM ${EVAL_RESULTS_TABLE_NAME}
           WHERE application_id = '${applicationId}'
           AND created_at >= '${timeWindow.start}'
           AND created_at <= '${timeWindow.end}'
           AND JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)') IS NOT NULL
           AND arrayExists(
             x -> JSONExtractBool(x, 'pass') = false 
             AND JSONExtractString(x, 'assertion', 'type') = '${metricType}',
             JSONExtract(CAST(grading_result AS String), 'componentResults', 'Array(String)')
           )
         `;
            }
            this.logger.debug(`ClickHouse query for alert check: ${query}`);
            const result = await clickhouseService_1.clickHouseService.query(query);
            if (result && result.length > 0) {
                let failCount = Number.parseInt(result[0].fail_count) || 0;
                let traces = [];
                // Process traces data for metric and plugin types
                if (type !== constants_1.ALERT_TYPES.PULSE_SYSTEM_HEALTH && result[0].traces && result[0].trace_ids) {
                    const tracesData = result[0].traces;
                    const traceIds = result[0].trace_ids;
                    if (Array.isArray(tracesData) && Array.isArray(traceIds)) {
                        traces = tracesData.flatMap((traceGroup, index) => {
                            const traceId = traceIds[index] || '';
                            if (Array.isArray(traceGroup)) {
                                return traceGroup
                                    .filter((component) => {
                                    // Only include failed metrics
                                    const parsed = typeof component === 'string' ? JSON.parse(component) : component;
                                    return (parsed.pass === false &&
                                        ((type === 'metric' && parsed.assertion?.type === metricType) ||
                                            (type === 'plugins' &&
                                                parsed.assertion?.type?.replace('trusys:redteam:', '') === metricType) ||
                                            (type !== 'metric' &&
                                                type !== 'plugins' &&
                                                parsed.assertion?.type === metricType)));
                                })
                                    .map((component) => {
                                    const parsed = typeof component === 'string' ? JSON.parse(component) : component;
                                    return {
                                        trace_id: traceId,
                                        assertion: {
                                            assertion_name: parsed.assertion?.type || '',
                                            result: parsed,
                                        },
                                    };
                                });
                            }
                            return [];
                        });
                    }
                }
                else {
                    if (type === constants_1.ALERT_TYPES.PULSE_SYSTEM_HEALTH && subType === 'cost' && result[0].fail_count > metricValue) {
                        traces = [{
                                trace_id: (0, uuid_1.v4)(),
                                assertion: {
                                    assertion_name: metricType ?? '',
                                    result: { cost: result[0].fail_count },
                                },
                            }];
                        failCount++;
                    }
                }
                return { failCount, traces };
            }
            return { failCount: 0, traces: [] };
        }
        catch (error) {
            this.logger.error(`Error querying ClickHouse for alert check: ${error}`);
            return { failCount: 0, traces: [] };
        }
    }
    /**
     * Get fail count from ClickHouse guardrail_results table for INPUT_GAURD_RAIL and OUTPUT_GAURD_RAIL
     * Returns count of failed components (not rows), with traces for each failed component
     */
    async getGuardrailFailCountFromClickHouse(params) {
        const { applicationId, guardrailStage, timeWindow, projectId, subType } = params;
        try {
            // Use parseDateTimeBestEffort to properly parse date strings for ClickHouse
            const whereParts = [
                `application_id = '${applicationId}'`,
                `guardrail_stage = '${guardrailStage}'`,
                `created_at >= parseDateTimeBestEffort('${timeWindow.start}')`,
                `created_at <= parseDateTimeBestEffort('${timeWindow.end}')`,
            ];
            if (projectId) {
                whereParts.push(`project_id = '${projectId}'`);
            }
            // If a specific guardrail subtype (validator type) is provided, filter by it
            if (subType) {
                whereParts.push(`JSONExtractString(component, 'assertion', 'type') = '${subType}'`);
            }
            // Query to get all guardrail results with failed components
            const query = `
        SELECT
          id,
          component,
          created_at
        FROM ${GUARDRAIL_RESULTS_TABLE_NAME}
        ARRAY JOIN JSONExtractArrayRaw(component_results) AS component
        WHERE ${whereParts.join(' AND ')}
          AND JSONExtractBool(component, 'pass') = false
        ORDER BY created_at DESC
      `;
            this.logger.debug(`Guardrail ClickHouse query: ${query}`);
            const rows = await clickhouseService_1.clickHouseService.query(query);
            if (!rows || rows.length === 0) {
                return { failCount: 0, traces: [] };
            }
            let failCount = 0;
            const traces = [];
            const assertionMap = {};
            // Process each row and extract failed components
            rows?.forEach((row) => {
                const componentResult = row.component
                    ? JSON.parse(row.component)
                    : {};
                // Only count failed components (pass === false)
                if (componentResult.pass === false) {
                    failCount++;
                    if (assertionMap[componentResult.assertion?.type]) {
                        assertionMap[componentResult.assertion?.type].count++;
                        traces?.map((trace) => {
                            if (trace.assertion.assertion_name === componentResult.assertion?.type) {
                                trace.assertion.count++;
                            }
                        });
                    }
                    else {
                        assertionMap[componentResult.assertion?.type] = {
                            assertion_name: componentResult.assertion?.type || '',
                            count: 1,
                        };
                        traces?.push({
                            trace_id: row.id || '',
                            assertion: {
                                assertion_name: componentResult.assertion?.type || '',
                                count: 1,
                                result: {
                                    ...componentResult,
                                    prompt: row.prompt || '',
                                    response: row.response || '',
                                    latency: row.latency || 0,
                                    error: row.error || null,
                                    created_at: row.created_at || '',
                                },
                            },
                        });
                    }
                }
            });
            this.logger.debug(`Guardrail alert check: found ${failCount} failed components from ${rows.length} guardrail results`);
            return { failCount, traces };
        }
        catch (error) {
            this.logger.error(`Error querying ClickHouse guardrail results for alert check: ${error}`);
            return { failCount: 0, traces: [] };
        }
    }
    /**
     * Check a single alert configuration
     */
    async checkSingleAlert(alert) {
        const { id: alertId, applicationId, alert: alertCondition, workspaceId = '' } = alert;
        let triggered = false;
        const failedMetrics = [];
        const { type, metricType, subType, metricValue, metricComparator, metricComparatorType } = alertCondition;
        // For system health alerts, metricComparator and metricComparatorType are required
        // For functional/security evaluation, they're not required (uses fixed 5-minute window)
        // For guardrail alerts, metricComparator and metricComparatorType are required
        if ((!metricComparator || !metricComparatorType) &&
            ![
                constants_1.ALERT_TYPES.FUNCTIONAL_EVALUATION,
                constants_1.ALERT_TYPES.SECURITY_EVALUATION,
                constants_1.ALERT_TYPES.PULSE_SYSTEM_HEALTH,
                constants_1.ALERT_TYPES.INPUT_GAURD_RAIL,
                constants_1.ALERT_TYPES.OUTPUT_GAURD_RAIL,
            ].includes(type)) {
            return {
                alertId,
                applicationId,
                workspaceId,
                triggered,
                failedMetrics: [],
            };
        }
        // Calculate time window
        const timeWindow = this.calculateTimeWindow(metricComparator || 5, metricComparatorType || 'minutes');
        // Get fail count and traces - use database for functionalEvaluation/securityEvaluation, ClickHouse for others
        let failCount = 0;
        let traces = [];
        let TestRunsWithFailedResults = [];
        if (type === constants_1.ALERT_TYPES.FUNCTIONAL_EVALUATION || type === constants_1.ALERT_TYPES.SECURITY_EVALUATION) {
            // Query from PostgreSQL database
            const result = await this.getFailCountFromDatabase({
                applicationId,
                metricType,
                timeWindow,
                type,
                projectId: alert.projectId,
            });
            failCount = result.failCount;
            TestRunsWithFailedResults = result.TestRunsWithFailedResults;
        }
        else if (type === constants_1.ALERT_TYPES.INPUT_GAURD_RAIL || type === constants_1.ALERT_TYPES.OUTPUT_GAURD_RAIL) {
            // Query from ClickHouse guardrail_results table
            const guardrailStage = type === constants_1.ALERT_TYPES.INPUT_GAURD_RAIL ? 'input' : 'output';
            const result = await this.getGuardrailFailCountFromClickHouse({
                applicationId,
                guardrailStage,
                timeWindow,
                projectId: alert.projectId,
                subType,
            });
            failCount = result.failCount;
            traces = result.traces;
        }
        else {
            // Query from ClickHouse for other types (including system health)
            const result = await this.getFailCountFromClickHouse({
                applicationId,
                metricType,
                subType,
                timeWindow,
                type,
                metricValue: Number(metricValue || 0),
            });
            failCount = result.failCount;
            traces = result.traces;
        }
        this.logger.debug(`Alert ${alertId}: failCount=${failCount}, threshold=${metricValue}, timeWindow=${timeWindow}`);
        const threshold = subType === 'noLogs' ? 0 : Number(metricValue || 0);
        // Check if alert should be triggered
        if (failCount >= threshold && failCount > 0) {
            triggered = true;
            failedMetrics.push({
                type,
                metricType,
                subType,
                metricValue,
                metricComparator,
                metricComparatorType,
                failCount: Number(failCount),
                threshold: Number(threshold),
                timeWindow,
                alertName: alert.name,
                alertCondition: alert.condition,
                traces,
                TestRunsWithFailedResults,
            });
        }
        return {
            alertId,
            applicationId,
            workspaceId,
            triggered,
            failedMetrics,
        };
    }
    /**
     * Send email notification
     */
    async sendEmailNotification(alert, alertCondition, alertResult, channel) {
        const emails = channel.config?.emails || [];
        const projectName = alert?.alert?.projectName || alert?.projectName || '';
        if (alertCondition?.type === constants_1.ALERT_TYPES.PULSE_SYSTEM_HEALTH) {
            if (alertCondition?.subType === 'noLogs') {
                await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/noLogs-alert`, {
                    method: 'POST',
                    params: {
                        appName: alert?.applicationName,
                        userEmails: emails,
                        timeWindow: `${alertCondition?.metricComparator} ${alertCondition?.metricComparatorType}`,
                        timestamp: this.getFormattedTimestamp(),
                    },
                }, '');
            }
            else if (alertCondition?.subType === 'cost') {
                await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/cost-limit-reached`, {
                    method: 'POST',
                    params: {
                        applicationName: alert?.applicationName,
                        userEmails: emails,
                        costLimit: alertResult?.traces[0]?.assertion?.result?.cost,
                        timeWindow: `${alertCondition?.metricComparator} ${alertCondition?.metricComparatorType}`,
                        timestamp: this.getFormattedTimestamp(),
                    },
                }, '');
            }
        }
        else if (alertResult.traces.length >= 0) {
            await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/evaluation-alert`, {
                method: 'POST',
                params: {
                    appName: alert?.applicationName,
                    metricType: alertCondition?.type,
                    userEmails: emails,
                    evaluationType: alertResult?.TestRunWithFailedResults?.isRedteam ? 'Security' : 'Functional',
                    evaluationID: alertResult?.TestRunWithFailedResults?.testRunId,
                    count: alertResult?.TestRunWithFailedResults?.testCaseFailCount,
                    alertDashboardLink: `${process.env.TRUSYS_URL}/workspaces/${alert?.workspaceId}/projects/${alert?.projectId}/alerts`,
                    alertConditionName: (0, alertsService_1.getAlertConditionTitle)(alert?.alert || {}),
                    timeWindow: `${alertCondition?.metricComparator} ${alertCondition?.metricComparatorType}`,
                    timestamp: this.getFormattedTimestamp(),
                    projectName,
                },
            }, '');
        }
        else if ([constants_1.ALERT_TYPES.INPUT_GAURD_RAIL, constants_1.ALERT_TYPES.OUTPUT_GAURD_RAIL].includes(alertCondition?.type) &&
            alertResult?.traces &&
            alertResult.traces.length > 0) {
            for (const trace of alertResult?.traces || []) {
                const count = trace?.assertion?.count || 1;
                const metricName = trace?.assertion?.assertion_name || '';
                const type = alertCondition?.type === constants_1.ALERT_TYPES.INPUT_GAURD_RAIL ? 'input' : 'output';
                await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/guard-rail-failure`, {
                    method: 'POST',
                    params: {
                        appName: alert?.applicationName,
                        timeWindow: `${alertCondition?.metricComparator} ${alertCondition?.metricComparatorType}`,
                        timestamp: this.getFormattedTimestamp(),
                        userEmails: emails,
                        metricName,
                        type,
                        count,
                        link: `${process.env.TRUSYS_URL}/workspaces/${alert?.workspaceId}/projects/${alert?.projectId}/alerts`,
                        projectName,
                    },
                }, '');
            }
        }
    }
    /**
     * Generic function to make API calls with configurable headers, authentication, and body
     */
    async makeApiCall(config) {
        const { url, method = 'POST', auth, username, password, apiKey, customFields = [], rawBody, jsonBodyType, jsonFormConfig = [], data, placeholders = {}, } = config;
        if (!url) {
            throw new Error('URL is required for API call');
        }
        // Prepare headers
        const headers = {
            'Content-Type': 'application/json',
        };
        // Add authentication headers based on auth type
        if (auth === 'basic' && username && password) {
            const credentials = Buffer.from(`${username}:${password}`).toString('base64');
            headers['Authorization'] = `Basic ${credentials}`;
        }
        else if (auth === 'apikey' && apiKey?.key && apiKey?.value) {
            headers[apiKey.key] = apiKey.value;
        }
        // Add custom fields as headers if any
        customFields.forEach((field) => {
            if (field.key && field.value) {
                headers[field.key] = field.value;
            }
        });
        // Prepare request body based on configuration
        let requestBody;
        if (rawBody) {
            // Use raw body if provided
            requestBody = rawBody;
            // Remove Content-Type header for raw body to let the user specify it
            headers['Content-Type'] = 'application/json';
        }
        else if (jsonBodyType === 'formatted' && jsonFormConfig.length > 0) {
            // Use formatted JSON body based on json_form_config
            const formattedData = {};
            jsonFormConfig.forEach((field) => {
                if (field.key && field.value !== undefined) {
                    // Replace placeholders in field value with actual data
                    let value = field.value;
                    Object.entries(placeholders).forEach(([placeholder, replacement]) => {
                        value = value.replace(new RegExp(`{{${placeholder}}}`, 'g'), String(replacement));
                    });
                    formattedData[field.key] = value;
                }
            });
            requestBody = JSON.stringify(formattedData);
            headers['Content-Type'] = 'application/json';
        }
        else {
            // Use provided data or empty object
            requestBody = JSON.stringify(data || {});
            headers['Content-Type'] = 'application/json';
        }
        const response = await fetch(url, {
            method,
            headers,
            body: requestBody,
        });
        if (!response.ok) {
            throw new Error(`API request failed with status ${response.status}: ${response.statusText}`);
        }
        return response;
    }
    /**
     * Process webhook body with nunjucks template replacement
     */
    processWebhookBodyWithNunjucks(bodyTemplate, variables) {
        try {
            const nunjucks = (0, templates_1.getNunjucksEngine)();
            return nunjucks.renderString(bodyTemplate, variables);
        }
        catch (error) {
            this.logger.error(`Error processing webhook body with nunjucks: ${error}`);
            // Fallback to simple string replacement if nunjucks fails
            let processedBody = bodyTemplate;
            Object.entries(variables).forEach(([key, value]) => {
                const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
                processedBody = processedBody.replace(regex, String(value));
            });
            return processedBody;
        }
    }
    /**
     * Prepare webhook variables for nunjucks template
     */
    prepareWebhookVariables(alert, alertResult, alertLogId, alertCondition) {
        const alertType = alertCondition?.type;
        const isFunctionalOrSecurityEval = alertType === constants_1.ALERT_TYPES.FUNCTIONAL_EVALUATION ||
            alertType === constants_1.ALERT_TYPES.SECURITY_EVALUATION;
        const isGuardrailAlert = alertType === constants_1.ALERT_TYPES.INPUT_GAURD_RAIL ||
            alertType === constants_1.ALERT_TYPES.OUTPUT_GAURD_RAIL;
        const evalResults = isFunctionalOrSecurityEval
            ? alertResult?.TestRunWithFailedResults?.failedResults || []
            : isGuardrailAlert
                ? alertResult?.traces || []
                : alertResult?.traces || [];
        // Process traces to create items array with item.* fields
        const items = (evalResults || []).map((trace, index) => {
            let assertionResult = {};
            let assertionName = '';
            let failureReason = '';
            const evalResult = isFunctionalOrSecurityEval ? trace : trace?.assertion?.result || {};
            const gradingResult = evalResult?.gradingResult || {};
            const componentResults = gradingResult?.componentResults || [];
            // Reset assertionNames for each trace
            let assertionNames = new Set();
            if (isFunctionalOrSecurityEval) {
                const failureReasons = [];
                let firstFailedComponent = null;
                assertionNames = alertResult?.TestRunWithFailedResults?.failedMetricNames || new Set();
                if (Array.isArray(componentResults)) {
                    componentResults?.forEach((component, compIndex) => {
                        const parsed = typeof component === 'string' ? JSON.parse(component) : component;
                        if (parsed.pass === false) {
                            if (parsed.reason) {
                                failureReasons.push(parsed.reason);
                            }
                            // Use the first failed componentResult for other fields
                            if (!firstFailedComponent) {
                                firstFailedComponent = parsed;
                                assertionResult = parsed;
                            }
                        }
                    });
                }
                failureReason = failureReasons.join('; ') || '';
                // If no componentResults or no failed components, use the evalResult structure
                if (!firstFailedComponent && componentResults.length === 0) {
                    assertionResult = evalResult;
                }
            }
            else if (isGuardrailAlert) {
                // For guardrail alerts, use the trace structure directly
                assertionResult = trace?.assertion?.result || {};
                assertionName = trace?.assertion?.assertion_name || assertionResult?.assertion?.type || '';
                failureReason = assertionResult?.reason || assertionResult?.failureReason || '';
                if (assertionName) {
                    assertionNames.add(assertionName);
                }
            }
            else {
                // For other types, use existing logic
                assertionResult = trace?.assertion?.result || {};
                assertionName = trace?.assertion?.assertion_name || assertionResult?.assertion?.type || '';
                failureReason = assertionResult?.reason || assertionResult?.failureReason || '';
            }
            // Format description with assertion details
            const descriptionParts = [];
            if (assertionNames.size > 0) {
                descriptionParts.push(`Assertions: ${Array.from(assertionNames).join(', ')}`);
            }
            else if (assertionName) {
                descriptionParts.push(`Assertion: ${assertionName}`);
            }
            if (assertionResult?.measuredValue !== undefined) {
                descriptionParts.push(`Measured value: ${assertionResult.measuredValue}`);
            }
            if (assertionResult?.score !== undefined) {
                descriptionParts.push(`Score: ${assertionResult.score}`);
            }
            descriptionParts.push(`Result: ${assertionResult?.pass === false ? 'Fail' : 'Pass'}`);
            if (failureReason) {
                descriptionParts.push(`Failure reason: ${failureReason}`);
            }
            if (assertionResult?.severity) {
                descriptionParts.push(`Severity: ${assertionResult.severity}`);
            }
            // Extract prompt - handle both string and object formats
            let promptValue = '';
            if (isFunctionalOrSecurityEval) {
                const promptData = evalResult?.prompt?.raw || evalResult?.prompt;
                if (typeof promptData === 'string') {
                    promptValue = promptData;
                }
                else if (promptData) {
                    promptValue = JSON.stringify(promptData);
                }
            }
            else if (isGuardrailAlert) {
                // For guardrail alerts, prompt is in assertionResult
                const promptData = assertionResult?.prompt || '';
                if (promptData) {
                    promptValue = typeof promptData === 'string' ? promptData : JSON.stringify(promptData);
                }
            }
            else {
                const promptData = assertionResult?.prompt || assertionResult?.testCase?.vars;
                if (promptData) {
                    promptValue = typeof promptData === 'string' ? promptData : JSON.stringify(promptData);
                }
            }
            return {
                title: (0, alertsService_1.getAlertConditionTitle)(alert?.alert),
                description: descriptionParts.join(', '),
                prompt: promptValue || '{}',
                severity: assertionResult?.severity || 'medium',
                score: assertionResult?.score ?? 0,
                assertion_name: assertionNames.size > 0
                    ? Array.from(assertionNames).join(', ')
                    : assertionName,
                assertion_score: assertionResult?.score ?? 0,
                assertion_result: assertionResult?.pass === false ? 'fail' : 'pass',
                assertion_failure_reason: failureReason,
                json: isFunctionalOrSecurityEval
                    ? JSON.stringify(evalResult)
                    : JSON.stringify(assertionResult),
            };
        });
        // Get first item for single item access (backward compatibility)
        const firstItem = items.length > 0 ? items[0] : {
            title: (0, alertsService_1.getAlertConditionTitle)(alert?.alert || {}),
            description: `No logs/evals found for the application ${alert?.applicationName} in the time window ${alertCondition?.metricComparator} ${alertCondition?.metricComparatorType}`,
            prompt: '-',
            severity: '-',
            score: 0,
            assertion_name: '',
            assertion_score: 0,
            assertion_result: '-',
            assertion_failure_reason: '-',
            json: '-',
        };
        // Create item object for single item access (nunjucks can access item.title, item.description, etc.)
        const item = {
            title: firstItem.title,
            description: firstItem.description,
            prompt: firstItem.prompt,
            severity: firstItem.severity,
            score: firstItem.score,
            assertion_name: firstItem.assertion_name,
            assertion_score: firstItem.assertion_score,
            assertion_result: firstItem.assertion_result,
            assertion_failure_reason: firstItem.assertion_failure_reason,
            json: firstItem.json,
        };
        return {
            // Basic alert fields
            project_id: alert?.projectId || '',
            workspace_id: alert?.workspaceId || '',
            application_id: alert?.applicationId || '',
            application_name: alert?.applicationName || '',
            alert_log_id: alertLogId,
            alert_id: alert?.id || '',
            alert_time: new Date().toISOString(),
            item,
            alert_condition: { name: alert?.alertName },
            traces: JSON.stringify(alertResult?.traces || []),
            items,
            alert_link: `${process.env.TRUSYS_URL}/workspaces/${alert?.workspaceId}/projects/${alert?.projectId}/alerts`,
            fail_count: alertResult?.failCount || 0,
            metric_type: alertCondition?.metricType || '',
            metric_value: alertCondition?.metricValue || '',
            // For FUNCTIONAL_EVALUATION/SECURITY_EVALUATION, time window is always "last 5 min"
            time_window: isFunctionalOrSecurityEval
                ? 'last 5 min'
                : (alertCondition?.metricComparator && alertCondition?.metricComparatorType
                    ? `${alertCondition.metricComparator} ${alertCondition.metricComparatorType}`.trim()
                    : ''),
        };
    }
    /**
     * Send webhook notification with nunjucks body processing
     */
    async sendWebhookNotificationWithNunjucks(alert, alertResult, channel, alertLogId, alertCondition) {
        const webhookConfig = channel.config;
        const webhookUrl = webhookConfig?.url;
        if (!webhookUrl) {
            this.logger.warn(`No webhook URL configured for channel ${channel.id}`);
            return;
        }
        if ([constants_1.ALERT_TYPES.FUNCTIONAL_EVALUATION, constants_1.ALERT_TYPES.SECURITY_EVALUATION].includes(alertCondition?.type) && alertResult?.TestRunWithFailedResults?.length === 0) {
            this.logger.warn(`No failed results found for test run ${alertResult?.TestRunWithFailedResults?.testRunId}`);
            return;
        }
        // Prepare variables for nunjucks template
        const variables = this.prepareWebhookVariables(alert, alertResult, alertLogId, alertCondition);
        // Process body with nunjucks if raw_body is provided
        let processedBody;
        if (webhookConfig?.raw_body) {
            processedBody = this.processWebhookBodyWithNunjucks(webhookConfig.raw_body, variables);
        }
        try {
            await this.makeApiCall({
                url: webhookUrl,
                method: webhookConfig?.method || 'POST',
                auth: webhookConfig?.auth,
                username: webhookConfig?.username,
                password: webhookConfig?.password,
                apiKey: webhookConfig?.apiKey,
                customFields: webhookConfig?.customFields || [],
                rawBody: processedBody || webhookConfig?.raw_body,
                jsonBodyType: webhookConfig?.json_body_type,
                jsonFormConfig: webhookConfig?.json_form_config || [],
                data: {
                    project_id: variables['project_id'],
                    workspace_id: variables['workspace_id'],
                    application_id: variables['application_id'],
                    application_name: variables['application_name'],
                    traces: alertResult?.traces || [],
                    alert_link: variables['alert_link'],
                    alert_log_id: variables['alert_log_id'],
                    alert_time: variables['alert_time'],
                },
                placeholders: variables,
            });
            this.logger.info(`Webhook notification sent successfully to ${webhookUrl}`);
        }
        catch (error) {
            this.logger.error(`Error sending webhook notification: ${error}`);
            throw error;
        }
    }
    /**
     * Send webhook notification
     */
    async sendWebhookNotification(alert, alertResult, channel, alertLogId) {
        const webhookConfig = channel.config;
        const webhookUrl = webhookConfig?.url;
        if (!webhookUrl) {
            this.logger.warn(`No webhook URL configured for channel ${channel.id}`);
            return;
        }
        const webhookData = {
            project_id: alert?.projectId,
            workspace_id: alert?.workspaceId,
            application_id: alert?.applicationId,
            application_name: alert?.applicationName,
            traces: alertResult?.traces || [],
            alert_link: `${process.env.TRUSYS_URL}/workspaces/${alert?.workspaceId}/projects/${alert?.projectId}/alerts`, // You can define this URL
            alert_log_id: alertLogId,
            alert_time: new Date().toISOString(),
        };
        try {
            await this.makeApiCall({
                url: webhookUrl,
                method: webhookConfig?.method || 'POST',
                auth: webhookConfig?.auth,
                username: webhookConfig?.username,
                password: webhookConfig?.password,
                apiKey: webhookConfig?.apiKey,
                customFields: webhookConfig?.customFields || [],
                rawBody: webhookConfig?.raw_body,
                jsonBodyType: webhookConfig?.json_body_type,
                jsonFormConfig: webhookConfig?.json_form_config || [],
                data: webhookData,
                placeholders: {
                    project_id: webhookData.project_id,
                    workspace_id: webhookData.workspace_id,
                    application_id: webhookData.application_id,
                    application_name: webhookData.application_name,
                    alert_log_id: webhookData.alert_log_id,
                    alert_time: webhookData.alert_time,
                    traces: JSON.stringify(webhookData.traces),
                    alert_type: alert?.alert?.type,
                },
            });
            this.logger.info(`Webhook notification sent successfully to ${webhookUrl}`);
        }
        catch (error) {
            this.logger.error(`Error sending webhook notification: ${error}`);
            throw error;
        }
    }
    /**
     * Store alert log in pulseAlertLogsTable when threshold is reached
     */
    async storeAlertLog(db, alert, alertResult) {
        try {
            const alertCondition = alert?.alert;
            const alertType = alertCondition?.type;
            const isFunctionalOrSecurityEval = alertType === constants_1.ALERT_TYPES.FUNCTIONAL_EVALUATION ||
                alertType === constants_1.ALERT_TYPES.SECURITY_EVALUATION;
            const isGuardrailAlert = alertType === constants_1.ALERT_TYPES.INPUT_GAURD_RAIL ||
                alertType === constants_1.ALERT_TYPES.OUTPUT_GAURD_RAIL;
            await db.insert(tables_1.pulseAlertLogsTable).values({
                id: `alert_log_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
                alertId: alert.id,
                metrics: {
                    type: alertCondition?.type,
                    metricType: alertCondition?.metricType,
                    subType: alertCondition?.subType,
                    metricValue: alertCondition?.metricValue,
                    metricComparator: alertCondition?.metricComparator,
                    metricComparatorType: alertCondition?.metricComparatorType,
                    failCount: alertResult?.failCount ?? 1,
                    threshold: alertResult?.threshold ?? 1,
                    timeWindow: alertResult?.timeWindow,
                    condition: alert?.condition,
                    traces: alertResult?.traces || [],
                },
                count: Number(alertResult?.failCount ?? 1),
                timestamp: new Date(),
                applicationId: alert.applicationId,
                projectId: alert.projectId,
                workspaceId: alert.workspaceId || null,
            });
            // Get notification channels for this alert
            const notificationChannels = await db
                .select({
                id: tables_1.notificationChannelTable.id,
                name: tables_1.notificationChannelTable.name,
                type: tables_1.notificationChannelTable.type,
                config: tables_1.notificationChannelTable.config,
            })
                .from(tables_1.notificationChannelTable)
                .innerJoin(tables_1.pulseMonitoringAlertsToNotificationChannelsTable, (0, drizzle_orm_1.eq)(tables_1.notificationChannelTable.id, tables_1.pulseMonitoringAlertsToNotificationChannelsTable.notificationChannelId))
                .where((0, drizzle_orm_1.eq)(tables_1.pulseMonitoringAlertsToNotificationChannelsTable.alertId, alert.id));
            // For FUNCTIONAL_EVALUATION/SECURITY_EVALUATION, send notification for each trace (each eval_result)
            // For guardrail alerts, send notification for each failed component (each trace)
            if ((isFunctionalOrSecurityEval && alertResult?.TestRunsWithFailedResults && alertResult.TestRunsWithFailedResults.length > 0) ||
                (isGuardrailAlert && alertResult?.traces && alertResult.traces.length > 0)) {
                // Handle functional/security evaluation alerts
                if (isFunctionalOrSecurityEval && alertResult?.TestRunsWithFailedResults) {
                    for (const testRun of alertResult.TestRunsWithFailedResults) {
                        // Create a single-trace alertResult for this notification
                        const singleTraceAlertResult = {
                            ...alertResult,
                            TestRunWithFailedResults: testRun,
                            failCount: 1,
                        };
                        // Generate unique alertLogId for each trace
                        const traceAlertLogId = `alert_log_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
                        // Send notifications for each channel
                        for (const channel of notificationChannels) {
                            try {
                                if (channel.type === constants_1.CHANNEL_TYPE.EMAIL) {
                                    // Email notification
                                    await this.sendEmailNotification(alert, alertCondition, singleTraceAlertResult, channel);
                                }
                                else if (channel.type === constants_1.CHANNEL_TYPE.WEBHOOK) {
                                    // Webhook notification - use new nunjucks-based function if raw_body exists
                                    if (channel.config?.raw_body) {
                                        await this.sendWebhookNotificationWithNunjucks(alert, singleTraceAlertResult, channel, traceAlertLogId, alertCondition);
                                    }
                                    else {
                                        // Use original function for backward compatibility
                                        await this.sendWebhookNotification(alert, singleTraceAlertResult, channel, traceAlertLogId);
                                    }
                                }
                            }
                            catch (error) {
                                this.logger.error(`Error sending notification via channel ${channel.id} for test run ${testRun.testRunId}: ${error}`);
                            }
                        }
                    }
                }
                // Handle guardrail alerts
                else if (isGuardrailAlert && alertResult?.traces) {
                    for (const trace of alertResult.traces) {
                        // Create a single-trace alertResult for this notification
                        const singleTraceAlertResult = {
                            ...alertResult,
                            traces: [trace],
                            failCount: 1,
                        };
                        // Generate unique alertLogId for each trace
                        const traceAlertLogId = `alert_log_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
                        // Send notifications for each channel
                        for (const channel of notificationChannels) {
                            try {
                                if (channel.type === constants_1.CHANNEL_TYPE.EMAIL) {
                                    // Email notification
                                    await this.sendEmailNotification(alert, alertCondition, singleTraceAlertResult, channel);
                                }
                                else if (channel.type === constants_1.CHANNEL_TYPE.WEBHOOK) {
                                    // Webhook notification - use new nunjucks-based function if raw_body exists
                                    if (channel.config?.raw_body) {
                                        await this.sendWebhookNotificationWithNunjucks(alert, singleTraceAlertResult, channel, traceAlertLogId, alertCondition);
                                    }
                                    else {
                                        // Use original function for backward compatibility
                                        await this.sendWebhookNotification(alert, singleTraceAlertResult, channel, traceAlertLogId);
                                    }
                                }
                            }
                            catch (error) {
                                this.logger.error(`Error sending notification via channel ${channel.id} for guardrail trace ${trace.trace_id}: ${error}`);
                            }
                        }
                    }
                }
            }
            else {
                // For other alert types, send single notification with all traces
                const alertLogId = `alert_log_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
                // Send notifications for each channel
                for (const channel of notificationChannels) {
                    try {
                        if (channel.type === constants_1.CHANNEL_TYPE.EMAIL) {
                            // Email notification
                            await this.sendEmailNotification(alert, alertCondition, alertResult, channel);
                        }
                        else if (channel.type === constants_1.CHANNEL_TYPE.WEBHOOK) {
                            // Webhook notification - use new nunjucks-based function if raw_body exists
                            if (channel.config?.raw_body) {
                                await this.sendWebhookNotificationWithNunjucks(alert, alertResult, channel, alertLogId, alertCondition);
                            }
                            else {
                                // Use original function for backward compatibility
                                await this.sendWebhookNotification(alert, alertResult, channel, alertLogId);
                            }
                        }
                    }
                    catch (error) {
                        this.logger.error(`Error sending notification via channel ${channel.id}: ${error}`);
                    }
                }
            }
            this.logger.info(`Alert log stored for alert ${alert.id} with fail count ${alertResult.failCount}`);
        }
        catch (error) {
            this.logger.error(`Error storing alert log for alert ${alert.id}: ${error}`);
        }
    }
    /**
     * Check all active alerts and update alertsTriggered count if conditions are met
     */
    async checkAllAlerts() {
        try {
            this.logger.info('Starting alert check for all active monitoring configurations');
            const db = await (0, database_1.getDb)();
            // Get all active alerts
            const activeAlerts = await db
                .select({
                id: tables_1.pulseMonitoringAlertsTable.id,
                alertName: tables_1.pulseMonitoringAlertsTable.name,
                applicationId: tables_1.pulseMonitoringAlertsTable.applicationId,
                alert: tables_1.pulseMonitoringAlertsTable.alert,
                condition: tables_1.pulseMonitoringAlertsTable.condition,
                alertsTriggered: tables_1.pulseMonitoringAlertsTable.alertsTriggered,
                projectId: tables_1.pulseMonitoringAlertsTable.projectId,
                workspaceId: tables_1.pulseMonitoringAlertsTable.workspaceId,
                status: tables_1.pulseMonitoringAlertsTable.status,
                applicationName: tables_1.applicationsTable.name,
            })
                .from(tables_1.pulseMonitoringAlertsTable)
                .where((0, drizzle_orm_1.eq)(tables_1.pulseMonitoringAlertsTable.status, 1))
                .innerJoin(tables_1.applicationsTable, (0, drizzle_orm_1.eq)(tables_1.pulseMonitoringAlertsTable.applicationId, tables_1.applicationsTable.id));
            this.logger.info(`Found ${activeAlerts.length} active alerts to check`);
            let totalAlertsChecked = 0;
            let totalAlertsTriggered = 0;
            const details = [];
            // Check each alert in batches of 5
            const batchSize = 5;
            for (let i = 0; i < activeAlerts.length; i += batchSize) {
                const batch = activeAlerts.slice(i, i + batchSize);
                await Promise.all(batch.map(async (alert) => {
                    try {
                        let alertsTriggered = 0;
                        const alertResult = await this.checkSingleAlert(alert);
                        totalAlertsChecked++;
                        if (alertResult?.triggered) {
                            alertsTriggered = alertResult?.failedMetrics?.length ?? 1;
                            totalAlertsTriggered += alertsTriggered;
                            // Store alert log in pulseAlertLogsTable
                            await Promise.all(alertResult?.failedMetrics?.map((metric) => this.storeAlertLog(db, alert, metric)) ?? []);
                        }
                        // Update the alertsTriggered count in the database
                        const currentAlertsTriggered = alert?.alertsTriggered ?? 0;
                        const newAlertsTriggered = currentAlertsTriggered + alertsTriggered;
                        // Ensure the value is within safe integer bounds
                        const safeAlertsTriggered = Math.max(0, Math.min(newAlertsTriggered, Number.MAX_SAFE_INTEGER));
                        try {
                            await db
                                .update(tables_1.pulseMonitoringAlertsTable)
                                .set({
                                alertsTriggered: safeAlertsTriggered,
                                updatedAt: new Date(),
                            })
                                .where((0, drizzle_orm_1.eq)(tables_1.pulseMonitoringAlertsTable.id, alert.id));
                        }
                        catch (updateError) {
                            this.logger.error(`Failed to update alertsTriggered for alert ${alert.id}: ${updateError?.message || updateError}. ` +
                                `Current value: ${currentAlertsTriggered}, Adding: ${alertsTriggered}, New value: ${safeAlertsTriggered}`);
                            // Continue processing other alerts even if this update fails
                            throw updateError;
                        }
                        details.push(alertResult);
                    }
                    catch (error) {
                        this.logger.error(`Error checking alert ${alert.id}: ${error?.message || error}. ` +
                            `Stack: ${error?.stack || 'N/A'}`);
                        details.push({
                            alertId: alert?.id,
                            applicationId: alert?.applicationId,
                            workspaceId: alert?.workspaceId || '',
                            triggered: false,
                            failedMetrics: [],
                        });
                    }
                }));
            }
            const result = {
                totalAlertsChecked,
                totalAlertsTriggered,
                details,
            };
            this.logger.info(`Alert check completed. ${totalAlertsTriggered} alerts triggered out of ${totalAlertsChecked} checked`);
            return result;
        }
        catch (error) {
            this.logger.error(`Error in checkAllAlerts: ${error}`);
            throw error;
        }
    }
}
exports.PulseAlertCheckCronService = PulseAlertCheckCronService;
// Export a singleton instance
exports.pulseAlertCheckCronService = new PulseAlertCheckCronService();
//# sourceMappingURL=pulseAlertCheckCronService.js.map