import logger from "../Logger";
import LogAggregationService from "../../Services/LogAggregationService";
import VMUtil from "../VM/VMAPI";
import APIRequestCriteria from "./Criteria/APIRequestCriteria";
import CustomCodeMonitoringCriteria from "./Criteria/CustomCodeMonitorCriteria";
import IncomingEmailCriteria from "./Criteria/IncomingEmailCriteria";
import IncomingRequestCriteria from "./Criteria/IncomingRequestCriteria";
import SSLMonitorCriteria from "./Criteria/SSLMonitorCriteria";
import ServerMonitorCriteria from "./Criteria/ServerMonitorCriteria";
import SyntheticMonitoringCriteria from "./Criteria/SyntheticMonitor";
import LogMonitorCriteria from "./Criteria/LogMonitorCriteria";
import MetricMonitorCriteria from "./Criteria/MetricMonitorCriteria";
import TraceMonitorCriteria from "./Criteria/TraceMonitorCriteria";
import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
import ProfileMonitorCriteria from "./Criteria/ProfileMonitorCriteria";
import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
import DomainMonitorCriteria from "./Criteria/DomainMonitorCriteria";
import DnssecMonitorCriteria from "./Criteria/DnssecMonitorCriteria";
import ExternalStatusPageMonitorCriteria from "./Criteria/ExternalStatusPageMonitorCriteria";
import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
import FilterCondition from "../../../Types/Filter/FilterCondition";
import MonitorType from "../../../Types/Monitor/MonitorType";
import { CheckOn } from "../../../Types/Monitor/CriteriaFilter";
import OneUptimeDate from "../../../Types/Date";
import Typeof from "../../../Types/Typeof";
import URL from "../../../Types/API/URL";
import IP from "../../../Types/IP/IP";
import Hostname from "../../../Types/API/Hostname";
import Port from "../../../Types/Port";
import { DashboardClientUrl } from "../../EnvironmentConfig";
export default class MonitorCriteriaEvaluator {
    static async processMonitorStep(input) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        const criteria = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.monitorCriteria;
        if (!criteria || !criteria.data) {
            return input.probeApiIngestResponse;
        }
        for (const criteriaInstance of criteria.data.monitorCriteriaInstanceArray) {
            /*
             * Record disabled criteria in the summary (so the user can see why
             * nothing happened) but skip evaluation, status changes, incidents,
             * and alerts.
             */
            if (((_b = criteriaInstance.data) === null || _b === void 0 ? void 0 : _b.isEnabled) === false) {
                const skipReason = "This criteria is disabled and was not evaluated.";
                const skippedCriteriaResult = {
                    criteriaId: (_c = criteriaInstance.data) === null || _c === void 0 ? void 0 : _c.id,
                    criteriaName: (_d = criteriaInstance.data) === null || _d === void 0 ? void 0 : _d.name,
                    filterCondition: ((_e = criteriaInstance.data) === null || _e === void 0 ? void 0 : _e.filterCondition) || FilterCondition.All,
                    met: false,
                    message: skipReason,
                    filters: [],
                    skipped: true,
                    skipReason: skipReason,
                };
                input.evaluationSummary.criteriaResults.push(skippedCriteriaResult);
                continue;
            }
            const criteriaResult = {
                criteriaId: (_f = criteriaInstance.data) === null || _f === void 0 ? void 0 : _f.id,
                criteriaName: (_g = criteriaInstance.data) === null || _g === void 0 ? void 0 : _g.name,
                filterCondition: ((_h = criteriaInstance.data) === null || _h === void 0 ? void 0 : _h.filterCondition) || FilterCondition.All,
                met: false,
                message: "",
                filters: [],
            };
            input.evaluationSummary.criteriaResults.push(criteriaResult);
            const rootCause = await MonitorCriteriaEvaluator.processMonitorCriteriaInstance({
                dataToProcess: input.dataToProcess,
                monitorStep: input.monitorStep,
                monitor: input.monitor,
                probeApiIngestResponse: input.probeApiIngestResponse,
                criteriaInstance: criteriaInstance,
                criteriaResult: criteriaResult,
            });
            if (!criteriaResult.message) {
                criteriaResult.message = criteriaResult.met
                    ? "Criteria met."
                    : "Criteria was not met.";
            }
            const criteriaEvent = {
                type: criteriaResult.met ? "criteria-met" : "criteria-not-met",
                title: `${criteriaResult.met ? "Criteria met" : "Criteria not met"}: ${criteriaResult.criteriaName || "Unnamed criteria"}`,
                message: criteriaResult.message,
                relatedCriteriaId: criteriaResult.criteriaId,
                at: OneUptimeDate.getCurrentDate(),
            };
            input.evaluationSummary.events.push(criteriaEvent);
            if (rootCause) {
                input.probeApiIngestResponse.criteriaMetId = (_j = criteriaInstance.data) === null || _j === void 0 ? void 0 : _j.id;
                input.probeApiIngestResponse.rootCause = `
**Created because the following criteria was met**:

**Criteria Name**: ${(_k = criteriaInstance.data) === null || _k === void 0 ? void 0 : _k.name}
`;
                const contextBlock = await MonitorCriteriaEvaluator.buildRootCauseContext({
                    dataToProcess: input.dataToProcess,
                    monitorStep: input.monitorStep,
                    monitor: input.monitor,
                    criteriaInstance: criteriaInstance,
                });
                /*
                 * For metric monitors, render the metric identity (name, unit,
                 * filter attrs, breaching series) before the comparison line so
                 * the reader has context when they reach "Filter Conditions Met".
                 */
                const isMetricMonitor = input.monitor.monitorType === MonitorType.Metrics;
                if (contextBlock && isMetricMonitor) {
                    input.probeApiIngestResponse.rootCause += `
${contextBlock}
`;
                }
                input.probeApiIngestResponse.rootCause += `
**Filter Conditions Met**: ${rootCause}
`;
                if (contextBlock && !isMetricMonitor) {
                    input.probeApiIngestResponse.rootCause += `
${contextBlock}
`;
                }
                if (input.dataToProcess.failureCause) {
                    input.probeApiIngestResponse.rootCause += `
**Cause**: ${input.dataToProcess.failureCause || ""}
`;
                }
                /*
                 * When this is a metric-style monitor with per-series results,
                 * compute the per-series match list so MonitorResource can fan
                 * out one incident per affected series. We do this *after* the
                 * scalar criteriaMetId/rootCause is populated so legacy readers
                 * still see a usable response if perSeriesMatches is ignored.
                 */
                const perSeriesMatches = await MonitorCriteriaEvaluator.collectPerSeriesMatches({
                    dataToProcess: input.dataToProcess,
                    monitor: input.monitor,
                    monitorStep: input.monitorStep,
                    criteriaInstance,
                });
                if (perSeriesMatches.length > 0) {
                    input.probeApiIngestResponse.perSeriesMatches = perSeriesMatches;
                }
                break;
            }
        }
        return input.probeApiIngestResponse;
    }
    /**
     * For metric-backed monitors (Metrics/Kubernetes/Docker) with
     * per-series aggregated results, re-evaluate the matched criteria
     * once per series and return one entry per series that breached.
     * Returns an empty array when the monitor is not series-aware or
     * no series matched — the caller falls back to the single-incident
     * path in that case.
     */
    static async collectPerSeriesMatches(input) {
        var _a, _b, _c, _d;
        if (input.monitor.monitorType !== MonitorType.Metrics &&
            input.monitor.monitorType !== MonitorType.Kubernetes &&
            input.monitor.monitorType !== MonitorType.Docker) {
            return [];
        }
        const metricResponse = input.dataToProcess;
        const seriesBreakdown = metricResponse.seriesBreakdown;
        if (!seriesBreakdown || seriesBreakdown.length === 0) {
            return [];
        }
        const criteriaId = (_a = input.criteriaInstance.data) === null || _a === void 0 ? void 0 : _a.id;
        if (!criteriaId) {
            return [];
        }
        const filterCondition = ((_b = input.criteriaInstance.data) === null || _b === void 0 ? void 0 : _b.filterCondition) || FilterCondition.All;
        const filters = ((_c = input.criteriaInstance.data) === null || _c === void 0 ? void 0 : _c.filters) || [];
        /*
         * Evaluate every metric-value filter against every series. We only
         * handle CheckOn.MetricValue for now — the criteria evaluator's
         * other filter types don't carry series information.
         */
        const metricFilters = filters.filter((f) => {
            return f.checkOn === CheckOn.MetricValue;
        });
        if (metricFilters.length === 0) {
            return [];
        }
        /*
         * Key: fingerprint. Value: results of each metric filter for that
         * series, tracked so we can apply FilterCondition.All/Any per series.
         */
        const resultsByFingerprint = new Map();
        for (const criteriaFilter of metricFilters) {
            const evaluations = await MetricMonitorCriteria.evaluateAllSeries({
                dataToProcess: input.dataToProcess,
                criteriaFilter,
                monitorStep: input.monitorStep,
            });
            for (const evaluation of evaluations) {
                const fingerprint = evaluation.fingerprint;
                if (!fingerprint) {
                    continue;
                }
                const existing = resultsByFingerprint.get(fingerprint);
                if (existing) {
                    existing.filterResults.push(evaluation);
                }
                else {
                    resultsByFingerprint.set(fingerprint, {
                        labels: evaluation.labels,
                        filterResults: [evaluation],
                    });
                }
            }
        }
        const matches = [];
        for (const [fingerprint, entry] of resultsByFingerprint) {
            // Were all/any of the metric filters met for this series?
            const matched = entry.filterResults.filter((r) => {
                return r.rootCause !== null;
            });
            const seriesMatched = filterCondition === FilterCondition.All
                ? matched.length === entry.filterResults.length &&
                    entry.filterResults.length > 0
                : matched.length > 0;
            if (!seriesMatched) {
                continue;
            }
            /*
             * For the per-series rootCause, concatenate the matched filter
             * messages so the incident message reflects exactly what
             * breached on this specific series.
             */
            const rootCauseLines = matched.map((r) => {
                return `- ${r.rootCause}`;
            });
            matches.push({
                criteriaMetId: criteriaId,
                fingerprint,
                labels: entry.labels,
                rootCause: rootCauseLines.join("\n"),
                metricContext: (_d = matched[0]) === null || _d === void 0 ? void 0 : _d.context,
            });
        }
        return matches;
    }
    static async processMonitorCriteriaInstance(input) {
        return MonitorCriteriaEvaluator.isMonitorInstanceCriteriaFiltersMet({
            dataToProcess: input.dataToProcess,
            monitorStep: input.monitorStep,
            monitor: input.monitor,
            probeApiIngestResponse: input.probeApiIngestResponse,
            criteriaInstance: input.criteriaInstance,
            criteriaResult: input.criteriaResult,
        });
    }
    static async isMonitorInstanceCriteriaFiltersMet(input) {
        var _a, _b;
        const filterCondition = ((_a = input.criteriaInstance.data) === null || _a === void 0 ? void 0 : _a.filterCondition) || FilterCondition.All;
        const matchedFilterMessages = [];
        let hasMatch = false;
        let allFiltersMet = true;
        for (const criteriaFilter of ((_b = input.criteriaInstance.data) === null || _b === void 0 ? void 0 : _b.filters) || []) {
            const rootCause = await MonitorCriteriaEvaluator.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                monitorStep: input.monitorStep,
                monitor: input.monitor,
                probeApiIngestResponse: input.probeApiIngestResponse,
                criteriaInstance: input.criteriaInstance,
                criteriaFilter: criteriaFilter,
            });
            const didMeetCriteria = Boolean(rootCause);
            const filterMessage = MonitorCriteriaMessageBuilder.buildCriteriaFilterMessage({
                monitor: input.monitor,
                criteriaFilter: criteriaFilter,
                dataToProcess: input.dataToProcess,
                monitorStep: input.monitorStep,
                didMeetCriteria: didMeetCriteria,
                matchMessage: rootCause,
            });
            const filterSummary = {
                checkOn: criteriaFilter.checkOn,
                filterType: criteriaFilter.filterType,
                value: criteriaFilter.value,
                met: didMeetCriteria,
                message: filterMessage,
            };
            input.criteriaResult.filters.push(filterSummary);
            if (didMeetCriteria) {
                hasMatch = true;
                matchedFilterMessages.push(filterMessage);
            }
            else if (filterCondition === FilterCondition.All) {
                allFiltersMet = false;
            }
        }
        if (filterCondition === FilterCondition.All) {
            if (allFiltersMet && input.criteriaResult.filters.length > 0) {
                let message = "All filters met.";
                if (matchedFilterMessages.length > 0) {
                    message += matchedFilterMessages
                        .map((item) => {
                        return `\n- ${item}`;
                    })
                        .join("");
                }
                input.criteriaResult.met = true;
                input.criteriaResult.message = message;
                return message;
            }
            input.criteriaResult.met = false;
            input.criteriaResult.message =
                "One or more filters did not meet the configured conditions.";
            return null;
        }
        if (filterCondition === FilterCondition.Any) {
            if (hasMatch) {
                const firstMatch = matchedFilterMessages[0] ||
                    "At least one filter met the configured condition.";
                input.criteriaResult.met = true;
                input.criteriaResult.message = firstMatch;
                return firstMatch;
            }
            input.criteriaResult.met = false;
            input.criteriaResult.message =
                "No filters met the configured conditions.";
            return null;
        }
        return null;
    }
    static async isMonitorInstanceCriteriaFilterMet(input) {
        var _a, _b;
        if (input.criteriaFilter.checkOn === CheckOn.JavaScriptExpression) {
            let storageMap = {};
            if (input.monitor.monitorType === MonitorType.API ||
                input.monitor.monitorType === MonitorType.Website) {
                let responseBody = null;
                try {
                    responseBody = JSON.parse(input.dataToProcess
                        .responseBody || "{}");
                }
                catch (err) {
                    logger.error(err, {
                        projectId: (_a = input.monitor.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    });
                    responseBody = input.dataToProcess
                        .responseBody;
                }
                if (typeof responseBody === Typeof.String &&
                    (responseBody === null || responseBody === void 0 ? void 0 : responseBody.toString()) === "") {
                    responseBody = {};
                }
                storageMap = {
                    responseBody: responseBody,
                    responseHeaders: input.dataToProcess
                        .responseHeaders,
                    responseStatusCode: input.dataToProcess
                        .responseCode,
                    responseTimeInMs: input.dataToProcess
                        .responseTimeInMs,
                    isOnline: input.dataToProcess.isOnline,
                };
            }
            if (input.monitor.monitorType === MonitorType.IncomingRequest) {
                storageMap = {
                    requestBody: input.dataToProcess
                        .requestBody,
                    requestHeaders: input.dataToProcess
                        .requestHeaders,
                };
            }
            let expression = input.criteriaFilter.value;
            expression = VMUtil.replaceValueInPlace(storageMap, expression, false);
            const code = `return Boolean(${expression});`;
            let result = null;
            try {
                result = await VMUtil.runCodeInSandbox({
                    code: code,
                    options: {
                        args: {},
                    },
                });
            }
            catch (err) {
                logger.error(err, {
                    projectId: (_b = input.monitor.projectId) === null || _b === void 0 ? void 0 : _b.toString(),
                });
                return null;
            }
            if (result && result.returnValue) {
                return `JavaScript Expression - ${expression} - evaluated to true.`;
            }
            return null;
        }
        if (input.monitor.monitorType === MonitorType.API ||
            input.monitor.monitorType === MonitorType.Website ||
            input.monitor.monitorType === MonitorType.IP ||
            input.monitor.monitorType === MonitorType.Ping ||
            input.monitor.monitorType === MonitorType.Port) {
            const apiRequestCriteriaResult = await APIRequestCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (apiRequestCriteriaResult) {
                return apiRequestCriteriaResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.CustomJavaScriptCode &&
            input.dataToProcess.customCodeMonitorResponse) {
            const criteriaResult = await CustomCodeMonitoringCriteria.isMonitorInstanceCriteriaFilterMet({
                monitorResponse: input.dataToProcess
                    .customCodeMonitorResponse,
                criteriaFilter: input.criteriaFilter,
            });
            if (criteriaResult) {
                return criteriaResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.SyntheticMonitor &&
            input.dataToProcess.syntheticMonitorResponse) {
            const criteriaResult = await SyntheticMonitoringCriteria.isMonitorInstanceCriteriaFilterMet({
                monitorResponse: input.dataToProcess
                    .syntheticMonitorResponse || [],
                criteriaFilter: input.criteriaFilter,
            });
            if (criteriaResult) {
                return criteriaResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.IncomingRequest) {
            const incomingRequestResult = await IncomingRequestCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (incomingRequestResult) {
                return incomingRequestResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.IncomingEmail) {
            const incomingEmailResult = await IncomingEmailCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (incomingEmailResult) {
                return incomingEmailResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.SSLCertificate) {
            const sslMonitorResult = await SSLMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (sslMonitorResult) {
                return sslMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Server) {
            const serverMonitorResult = await ServerMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (serverMonitorResult) {
                return serverMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Logs) {
            const logMonitorResult = await LogMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (logMonitorResult) {
                return logMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Metrics ||
            input.monitor.monitorType === MonitorType.Kubernetes ||
            input.monitor.monitorType === MonitorType.Docker) {
            const metricMonitorResult = await MetricMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
                monitorStep: input.monitorStep,
            });
            if (metricMonitorResult) {
                return metricMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Traces) {
            const traceMonitorResult = await TraceMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (traceMonitorResult) {
                return traceMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Exceptions) {
            const exceptionMonitorResult = await ExceptionMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (exceptionMonitorResult) {
                return exceptionMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Profiles) {
            const profileMonitorResult = await ProfileMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (profileMonitorResult) {
                return profileMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.SNMP) {
            const snmpMonitorResult = await SnmpMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (snmpMonitorResult) {
                return snmpMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.DNS) {
            const dnsMonitorResult = await DnsMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (dnsMonitorResult) {
                return dnsMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.Domain) {
            const domainMonitorResult = await DomainMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (domainMonitorResult) {
                return domainMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.DNSSEC) {
            const dnssecMonitorResult = await DnssecMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (dnssecMonitorResult) {
                return dnssecMonitorResult;
            }
        }
        if (input.monitor.monitorType === MonitorType.ExternalStatusPage) {
            const externalStatusPageResult = await ExternalStatusPageMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
                dataToProcess: input.dataToProcess,
                criteriaFilter: input.criteriaFilter,
            });
            if (externalStatusPageResult) {
                return externalStatusPageResult;
            }
        }
        return null;
    }
    static async buildRootCauseContext(input) {
        // Handle Kubernetes monitors with rich resource context
        if (input.monitor.monitorType === MonitorType.Kubernetes) {
            return await MonitorCriteriaEvaluator.buildKubernetesRootCauseContext(input);
        }
        // Handle Docker monitors with resource context
        if (input.monitor.monitorType === MonitorType.Docker) {
            return MonitorCriteriaEvaluator.buildDockerRootCauseContext(input);
        }
        // Handle generic Metric monitors with metric identity + breaching series
        if (input.monitor.monitorType === MonitorType.Metrics &&
            input.criteriaInstance) {
            return MonitorCriteriaEvaluator.buildMetricRootCauseContext({
                criteriaInstance: input.criteriaInstance,
                monitor: input.monitor,
            });
        }
        const requestDetails = [];
        const responseDetails = [];
        const failureDetails = [];
        const probeResponse = MonitorCriteriaDataExtractor.getProbeMonitorResponse(input.dataToProcess);
        const destination = MonitorCriteriaEvaluator.getMonitorDestinationString({
            monitorStep: input.monitorStep,
            probeResponse: probeResponse,
        });
        if (destination) {
            requestDetails.push(`- Destination: ${destination}`);
        }
        const port = MonitorCriteriaEvaluator.getMonitorPortString({
            monitorStep: input.monitorStep,
            probeResponse: probeResponse,
        });
        if (port) {
            requestDetails.push(`- Destination Port: ${port}`);
        }
        const requestMethod = MonitorCriteriaEvaluator.getRequestMethodString({
            monitor: input.monitor,
            monitorStep: input.monitorStep,
        });
        if (requestMethod) {
            requestDetails.push(`- Request Method: ${requestMethod}`);
        }
        if ((probeResponse === null || probeResponse === void 0 ? void 0 : probeResponse.responseCode) !== undefined) {
            responseDetails.push(`- Response Status Code: ${probeResponse.responseCode}`);
        }
        const responseTime = MonitorCriteriaEvaluator.formatMilliseconds(probeResponse === null || probeResponse === void 0 ? void 0 : probeResponse.responseTimeInMs);
        if (responseTime) {
            responseDetails.push(`- Response Time: ${responseTime}`);
        }
        if ((probeResponse === null || probeResponse === void 0 ? void 0 : probeResponse.isTimeout) !== undefined) {
            responseDetails.push(`- Timed Out: ${probeResponse.isTimeout ? "Yes" : "No"}`);
        }
        // Add Request Failed Details if available
        if (probeResponse === null || probeResponse === void 0 ? void 0 : probeResponse.requestFailedDetails) {
            const requestFailedDetails = probeResponse.requestFailedDetails;
            if (requestFailedDetails.failedPhase) {
                failureDetails.push(`- Failed Phase: ${requestFailedDetails.failedPhase}`);
            }
            if (requestFailedDetails.errorCode) {
                failureDetails.push(`- Error Code: ${requestFailedDetails.errorCode}`);
            }
            if (requestFailedDetails.errorDescription) {
                failureDetails.push(`- Error Description: ${requestFailedDetails.errorDescription}`);
            }
            if (requestFailedDetails.rawErrorMessage) {
                failureDetails.push(`- Raw Error Message: ${requestFailedDetails.rawErrorMessage}`);
            }
        }
        const sections = [];
        if (requestDetails.length > 0) {
            sections.push(`**Request Details**\n${requestDetails.join("\n")}`);
        }
        if (responseDetails.length > 0) {
            sections.push(`\n\n**Response Snapshot**\n${responseDetails.join("\n")}`);
        }
        if (failureDetails.length > 0) {
            sections.push(`\n\n**Request Failed Details**\n${failureDetails.join("\n")}`);
        }
        if (!sections.length) {
            return null;
        }
        return sections.join("\n");
    }
    static buildMetricRootCauseContext(input) {
        var _a;
        /*
         * Pick the first populated metric context across the instance's filters.
         * Only metric-value filters populate this at evaluation time, so this
         * effectively returns the context for the filter that ran.
         */
        const ctx = (((_a = input.criteriaInstance.data) === null || _a === void 0 ? void 0 : _a.filters) || [])
            .map((f) => {
            return f.metricCriteriaContext;
        })
            .find((c) => {
            return Boolean(c);
        });
        if (!ctx) {
            return null;
        }
        const lines = [];
        lines.push(`- Metric: \`${ctx.metricName}\``);
        if (ctx.alias) {
            lines.push(`- Alias: \`${ctx.alias}\``);
        }
        if (ctx.unit) {
            lines.push(`- Unit: ${ctx.unit}`);
        }
        if (ctx.aggregationType) {
            lines.push(`- Aggregation: ${ctx.aggregationType}`);
        }
        if (ctx.isFormula && ctx.formulaExpression) {
            lines.push(`- Formula: \`${ctx.formulaExpression}\``);
        }
        if (ctx.timeWindowMinutes) {
            lines.push(`- Time Window: last ${ctx.timeWindowMinutes} minutes`);
        }
        /*
         * For formulas, enumerate the underlying variables so the on-call
         * engineer can trace `c = a + b` back to "a is container.cpu.time,
         * b is container.memory.usage in bytes" without clicking away.
         */
        if (ctx.components && ctx.components.length > 0) {
            const componentLines = ctx.components.map((component) => {
                const unitSuffix = component.unit
                    ? ` — unit: ${component.unit}`
                    : "";
                const typeSuffix = component.isFormula ? " (formula)" : "";
                return `  - \`${component.alias}\` = \`${component.name}\`${typeSuffix}${unitSuffix}`;
            });
            lines.push(`- Components:\n${componentLines.join("\n")}`);
        }
        const filterKeys = Object.keys(ctx.filterAttributes || {});
        if (filterKeys.length > 0) {
            const filterLines = filterKeys.map((k) => {
                const v = ctx.filterAttributes[k];
                return `  - \`${k}\` = \`${String(v)}\``;
            });
            lines.push(`- Filters:\n${filterLines.join("\n")}`);
        }
        if (ctx.groupBy.length > 0) {
            lines.push(`- Grouped By: ${ctx.groupBy
                .map((g) => {
                return `\`${g}\``;
            })
                .join(", ")}`);
        }
        const sections = [`**Metric Details**\n${lines.join("\n")}`];
        const breachingSamples = ctx.breachingSamples && ctx.breachingSamples.length > 0
            ? ctx.breachingSamples
            : ctx.breachingSample
                ? [ctx.breachingSample]
                : [];
        if (breachingSamples.length > 0) {
            sections.push(`\n\n${MonitorCriteriaEvaluator.formatBreachingSamplesSection({
                samples: breachingSamples,
                totalSamples: ctx.totalSamplesInWindow,
                unit: ctx.unit,
                metricName: ctx.metricName,
                alias: ctx.alias,
                components: ctx.components || [],
            })}`);
        }
        const deepLink = MonitorCriteriaEvaluator.buildMetricExplorerDeepLink({
            monitor: input.monitor,
            ctx,
        });
        if (deepLink) {
            sections.push(`\n\n[Open metric in dashboard](${deepLink})`);
        }
        return sections.join("\n");
    }
    /**
     * Build the **Breaching Samples** markdown section — a table of
     * timestamps, values, and any group-by attributes. Caps the row count
     * so a 30-minute window at 1-second granularity doesn't dump thousands
     * of lines onto the root-cause page; the caller can always drill in
     * via the metric explorer link.
     *
     * Timestamps are emitted as inline code wrapping ISO 8601 strings so
     * the client-side markdown viewer can localize them to the viewer's
     * timezone (without losing the canonical instant).
     */
    static formatBreachingSamplesSection(input) {
        const MAX_ROWS = 20;
        // Sort chronologically and de-duplicate any accidental repeats
        const sorted = [...input.samples].sort((a, b) => {
            return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
        });
        const displayedSamples = sorted.slice(0, MAX_ROWS);
        // Collect attribute keys that appear on any displayed sample
        const attrKeySet = new Set();
        for (const s of displayedSamples) {
            for (const k of Object.keys(s.attributes || {})) {
                attrKeySet.add(k);
            }
        }
        const attrKeys = Array.from(attrKeySet);
        /*
         * Escape pipe characters that could appear in the metric display
         * name (formulas like "a | b" are unlikely but possible) so they
         * don't break GitHub-flavored-markdown tables.
         */
        const escapeCell = (value) => {
            return value.replace(/\|/g, "\\|");
        };
        /*
         * Column layout:
         *   Timestamp | Metric | Alias | Value | <component_1> | ... | <attr_1> | ...
         *
         * The component columns let the reader see what each variable of a
         * formula resolved to at the breach time — e.g. "when c = a + b
         * breached 100, a was 55, b was 46". They are omitted for plain
         * metric criteria.
         */
        const headerCells = [
            "Timestamp",
            "Metric",
            "Alias",
            "Value",
        ];
        for (const component of input.components) {
            const label = component.unit
                ? `${component.alias} (${component.unit})`
                : component.alias;
            headerCells.push(label);
        }
        headerCells.push(...attrKeys);
        const unitSuffix = input.unit ? ` ${input.unit}` : "";
        const headerRow = `| ${headerCells.join(" | ")} |`;
        const dividerRow = `| ${headerCells
            .map(() => {
            return "---";
        })
            .join(" | ")} |`;
        const metricCell = `\`${escapeCell(input.metricName)}\``;
        const aliasCell = input.alias
            ? `\`${escapeCell(input.alias)}\``
            : "-";
        const dataRows = displayedSamples.map((s) => {
            const timestampIso = new Date(s.timestamp).toISOString();
            const cells = [
                `\`${timestampIso}\``,
                metricCell,
                aliasCell,
                `${MonitorCriteriaEvaluator.formatNumberForDisplay(s.value)}${unitSuffix}`,
            ];
            for (const component of input.components) {
                const match = (s.componentValues || []).find((cv) => {
                    return cv.alias === component.alias;
                });
                if (match && typeof match.value === "number") {
                    const componentUnitSuffix = component.unit
                        ? ` ${component.unit}`
                        : "";
                    cells.push(`${MonitorCriteriaEvaluator.formatNumberForDisplay(match.value)}${componentUnitSuffix}`);
                }
                else {
                    cells.push("-");
                }
            }
            for (const k of attrKeys) {
                const v = s.attributes[k];
                cells.push(v === undefined || v === null ? "-" : String(v));
            }
            return `| ${cells.join(" | ")} |`;
        });
        const lines = [
            `**Breaching Samples**`,
            MonitorCriteriaEvaluator.formatBreachingSamplesSummary({
                breachingCount: sorted.length,
                totalSamples: input.totalSamples,
            }),
            "",
            headerRow,
            dividerRow,
            ...dataRows,
        ];
        if (sorted.length > displayedSamples.length) {
            lines.push(`\n_Showing the first ${displayedSamples.length} of ${sorted.length} breaching samples._`);
        }
        return lines.join("\n");
    }
    static formatBreachingSamplesSummary(input) {
        if (typeof input.totalSamples === "number" &&
            input.totalSamples > 0 &&
            input.totalSamples >= input.breachingCount) {
            return `${input.breachingCount} of ${input.totalSamples} samples breached the threshold.`;
        }
        return `${input.breachingCount} sample${input.breachingCount === 1 ? "" : "s"} breached the threshold.`;
    }
    static formatNumberForDisplay(value) {
        if (!Number.isFinite(value)) {
            return String(value);
        }
        if (Number.isInteger(value)) {
            return value.toString();
        }
        return Number(value.toFixed(2)).toString();
    }
    static buildMetricExplorerDeepLink(input) {
        var _a, _b;
        const projectId = (_a = input.monitor.projectId) === null || _a === void 0 ? void 0 : _a.toString();
        if (!projectId) {
            return null;
        }
        /*
         * Metric explorer expects a JSON-encoded `metricQueries` param plus
         * optional start/end times. The shape is documented by
         * MetricExplorer.getMetricQueriesFromQuery(): it reads metricName,
         * attributes, and aggregationType (correctly spelled, unlike the
         * internal persisted field).
         */
        const aggregationType = input.ctx.aggregationType || undefined;
        const query = Object.assign({ metricName: input.ctx.metricName, attributes: input.ctx.filterAttributes || {} }, (aggregationType ? { aggregationType } : {}));
        // Time window: breach moment +- 15 minutes (or fall back to last hour).
        const now = OneUptimeDate.getCurrentDate();
        const breachTime = (_b = input.ctx.breachingSample) === null || _b === void 0 ? void 0 : _b.timestamp;
        const startTime = breachTime
            ? OneUptimeDate.addRemoveMinutes(breachTime, -30)
            : OneUptimeDate.addRemoveHours(now, -1);
        const endTime = breachTime
            ? OneUptimeDate.addRemoveMinutes(breachTime, 15)
            : now;
        const params = new URLSearchParams();
        params.set("metricQueries", JSON.stringify([query]));
        params.set("startTime", OneUptimeDate.toString(startTime));
        params.set("endTime", OneUptimeDate.toString(endTime));
        /*
         * The route that actually reads these URL params is the metric
         * explorer at /metrics/view — the /metrics index is the metric list.
         */
        return `${DashboardClientUrl.toString()}/${projectId}/metrics/view?${params.toString()}`;
    }
    static async buildKubernetesRootCauseContext(input) {
        var _a;
        const metricResponse = input.dataToProcess;
        const breakdown = metricResponse.kubernetesResourceBreakdown;
        if (!breakdown) {
            return null;
        }
        const sections = [];
        // Cluster context
        const clusterDetails = [];
        clusterDetails.push(`- Cluster: ${breakdown.clusterName}`);
        clusterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
        if (breakdown.attributes["k8s.namespace.name"]) {
            clusterDetails.push(`- Namespace: ${breakdown.attributes["k8s.namespace.name"]}`);
        }
        sections.push(`**Kubernetes Cluster Details**\n${clusterDetails.join("\n")}`);
        // Affected resources
        if (breakdown.affectedResources && breakdown.affectedResources.length > 0) {
            const resourceLines = [];
            // Sort by metric value descending (worst first) and filter out zero-value resources
            const sortedResources = [
                ...breakdown.affectedResources,
            ]
                .filter((r) => {
                return r.metricValue > 0;
            })
                .sort((a, b) => {
                return b.metricValue - a.metricValue;
            });
            if (sortedResources.length === 0) {
                return sections.join("\n");
            }
            // Show top 10 affected resources
            const resourcesToShow = sortedResources.slice(0, 10);
            // Determine which columns are present across all resources
            const hasNamespace = resourcesToShow.some((r) => {
                return r.namespace;
            });
            const hasWorkload = resourcesToShow.some((r) => {
                return r.workloadType && r.workloadName;
            });
            const hasPod = resourcesToShow.some((r) => {
                return r.podName;
            });
            const hasContainer = resourcesToShow.some((r) => {
                return r.containerName;
            });
            const hasNode = resourcesToShow.some((r) => {
                return r.nodeName;
            });
            // Build table header
            const headerCells = [];
            if (hasNamespace) {
                headerCells.push("Namespace");
            }
            if (hasWorkload) {
                headerCells.push("Workload Type");
                headerCells.push("Workload");
            }
            if (hasPod) {
                headerCells.push("Pod");
            }
            if (hasContainer) {
                headerCells.push("Container");
            }
            if (hasNode) {
                headerCells.push("Node");
            }
            headerCells.push("Value");
            const headerRow = `| ${headerCells.join(" | ")} |`;
            const separatorRow = `| ${headerCells
                .map(() => {
                return "---";
            })
                .join(" | ")} |`;
            resourceLines.push(headerRow);
            resourceLines.push(separatorRow);
            for (const resource of resourcesToShow) {
                const cells = [];
                if (hasNamespace) {
                    cells.push(resource.namespace ? `\`${resource.namespace}\`` : "-");
                }
                if (hasWorkload) {
                    cells.push(resource.workloadType ? `${resource.workloadType}` : "-");
                    cells.push(resource.workloadName ? `\`${resource.workloadName}\`` : "-");
                }
                if (hasPod) {
                    cells.push(resource.podName ? `\`${resource.podName}\`` : "-");
                }
                if (hasContainer) {
                    cells.push(resource.containerName ? `\`${resource.containerName}\`` : "-");
                }
                if (hasNode) {
                    cells.push(resource.nodeName ? `\`${resource.nodeName}\`` : "-");
                }
                cells.push(`**${resource.metricValue}**`);
                resourceLines.push(`| ${cells.join(" | ")} |`);
            }
            if (sortedResources.length > 10) {
                resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
            }
            sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
            // Add root cause analysis based on metric type
            const analysis = MonitorCriteriaEvaluator.buildKubernetesRootCauseAnalysis({
                breakdown: breakdown,
                topResource: resourcesToShow[0],
            });
            if (analysis) {
                sections.push(`\n\n**Root Cause Analysis**\n${analysis}`);
            }
            // Fetch recent container logs for the top affected resource during CrashLoopBackOff
            if ((breakdown.metricName === "k8s.container.restarts" ||
                breakdown.metricName.includes("restart")) &&
                input.monitor.projectId) {
                const topResource = resourcesToShow[0];
                try {
                    const logAttributes = {};
                    if (breakdown.clusterName) {
                        logAttributes["resource.k8s.cluster.name"] = breakdown.clusterName;
                    }
                    if (topResource.podName) {
                        logAttributes["resource.k8s.pod.name"] = topResource.podName;
                    }
                    if (topResource.containerName) {
                        logAttributes["resource.k8s.container.name"] =
                            topResource.containerName;
                    }
                    if (topResource.namespace) {
                        logAttributes["resource.k8s.namespace.name"] =
                            topResource.namespace;
                    }
                    const now = OneUptimeDate.getCurrentDate();
                    const fifteenMinutesAgo = OneUptimeDate.addRemoveMinutes(now, -15);
                    const logs = await LogAggregationService.getExportLogs({
                        projectId: input.monitor.projectId,
                        startTime: fifteenMinutesAgo,
                        endTime: now,
                        limit: 50,
                        attributes: logAttributes,
                    });
                    if (logs.length > 0) {
                        const logLines = logs.map((log) => {
                            const timestamp = log["time"] ? String(log["time"]) : "";
                            const severity = log["severityText"]
                                ? String(log["severityText"])
                                : "INFO";
                            const body = log["body"] ? String(log["body"]) : "";
                            return `\`${timestamp}\` **${severity}** ${body}`;
                        });
                        sections.push(`\n\n**Recent Container Logs** (${topResource.podName || "unknown pod"} / ${topResource.containerName || "unknown container"}, last 15 minutes)\n\n${logLines.join("\n\n")}`);
                    }
                }
                catch (err) {
                    const k8sLogAttributes = {
                        projectId: (_a = input.monitor.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    };
                    logger.error("Failed to fetch container logs for root cause context", k8sLogAttributes);
                    logger.error(err, k8sLogAttributes);
                }
            }
        }
        return sections.join("\n");
    }
    static buildDockerRootCauseContext(input) {
        var _a, _b, _c, _d, _e, _f, _g;
        const metricResponse = input.dataToProcess;
        const sections = [];
        // Docker host context
        const dockerMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.dockerMonitor;
        if (dockerMonitor) {
            const hostDetails = [];
            hostDetails.push(`- Host: ${dockerMonitor.hostIdentifier || "Unknown"}`);
            if ((_b = dockerMonitor.containerFilters) === null || _b === void 0 ? void 0 : _b.containerName) {
                hostDetails.push(`- Container Name Filter: ${dockerMonitor.containerFilters.containerName}`);
            }
            if ((_c = dockerMonitor.containerFilters) === null || _c === void 0 ? void 0 : _c.containerImage) {
                hostDetails.push(`- Container Image Filter: ${dockerMonitor.containerFilters.containerImage}`);
            }
            // Add metric name from the query config
            if (((_e = (_d = dockerMonitor.metricViewConfig) === null || _d === void 0 ? void 0 : _d.queryConfigs) === null || _e === void 0 ? void 0 : _e.length) > 0 &&
                dockerMonitor.metricViewConfig.queryConfigs[0]) {
                const metricName = (_g = (_f = dockerMonitor.metricViewConfig
                    .queryConfigs[0].metricQueryData) === null || _f === void 0 ? void 0 : _f.filterData) === null || _g === void 0 ? void 0 : _g.metricName;
                if (metricName) {
                    hostDetails.push(`- Metric: \`${metricName}\``);
                }
            }
            sections.push(`**Docker Host Details**\n${hostDetails.join("\n")}`);
        }
        // Metric results summary
        if (metricResponse.metricResult && metricResponse.metricResult.length > 0) {
            const resultDetails = [];
            for (const result of metricResponse.metricResult) {
                if (result.data && result.data.length > 0) {
                    resultDetails.push(`- ${result.data.length} metric data point(s) returned`);
                }
            }
            if (resultDetails.length > 0) {
                sections.push(`\n\n**Metric Summary**\n${resultDetails.join("\n")}`);
            }
        }
        return sections.length > 0 ? sections.join("\n") : null;
    }
    static buildKubernetesRootCauseAnalysis(input) {
        const { breakdown, topResource } = input;
        const metricName = breakdown.metricName;
        const lines = [];
        if (metricName === "k8s.container.restarts" ||
            metricName.includes("restart")) {
            lines.push(`Container restart count is elevated, indicating a potential CrashLoopBackOff condition.`);
            if (topResource.containerName) {
                lines.push(`The container \`${topResource.containerName}\` in pod \`${topResource.podName || "unknown"}\` has restarted **${topResource.metricValue}** times.`);
            }
            lines.push(`Common causes: application crash on startup, misconfigured environment variables, missing dependencies, OOM (Out of Memory) kills, failed health checks, or missing config maps/secrets.`);
            lines.push(`Recommended actions: Check container logs with \`kubectl logs ${topResource.podName || "<pod-name>"} -c ${topResource.containerName || "<container>"} --previous\` and inspect events with \`kubectl describe pod ${topResource.podName || "<pod-name>"}\`.`);
        }
        else if (metricName === "k8s.pod.phase" &&
            breakdown.attributes["k8s.pod.phase"] === "Pending") {
            lines.push(`Pods are stuck in Pending phase and unable to be scheduled.`);
            lines.push(`Common causes: insufficient CPU/memory resources on nodes, node affinity/taint restrictions preventing scheduling, PersistentVolumeClaim pending, or resource quota exceeded.`);
            if (topResource.podName) {
                lines.push(`Recommended actions: Check scheduling events with \`kubectl describe pod ${topResource.podName}\` and verify node resources with \`kubectl describe nodes\`.`);
            }
        }
        else if (metricName === "k8s.node.condition_ready" ||
            (metricName.includes("node") && metricName.includes("condition"))) {
            lines.push(`One or more nodes have transitioned to a NotReady state.`);
            if (topResource.nodeName) {
                lines.push(`Node \`${topResource.nodeName}\` is reporting NotReady (value: ${topResource.metricValue}).`);
            }
            lines.push(`Common causes: kubelet process failure, node resource exhaustion (disk pressure, memory pressure, PID pressure), network connectivity issues, or underlying VM/hardware failure.`);
            lines.push(`Recommended actions: Check node conditions with \`kubectl describe node ${topResource.nodeName || "<node-name>"}\` and verify kubelet status on the node.`);
        }
        else if (metricName === "k8s.node.cpu.utilization" ||
            (metricName.includes("cpu") && metricName.includes("utilization"))) {
            lines.push(`Node CPU utilization has exceeded the configured threshold.`);
            if (topResource.nodeName) {
                lines.push(`Node \`${topResource.nodeName}\` is at **${topResource.metricValue.toFixed(1)}%** CPU utilization.`);
            }
            lines.push(`Common causes: resource-intensive workloads, insufficient resource limits on pods, noisy neighbor pods consuming excessive CPU, or insufficient cluster capacity.`);
            lines.push(`Recommended actions: Identify top CPU consumers with \`kubectl top pods --all-namespaces --sort-by=cpu\` and consider scaling the cluster or adjusting pod resource limits.`);
        }
        else if (metricName === "k8s.node.memory.usage" ||
            (metricName.includes("memory") && metricName.includes("usage"))) {
            lines.push(`Node memory utilization has exceeded the configured threshold.`);
            if (topResource.nodeName) {
                lines.push(`Node \`${topResource.nodeName}\` memory usage is at **${topResource.metricValue.toFixed(1)}%**.`);
            }
            lines.push(`Common causes: memory leaks in applications, insufficient memory limits on pods, too many pods scheduled on the node, or growing dataset sizes.`);
            lines.push(`Recommended actions: Check memory consumers with \`kubectl top pods --all-namespaces --sort-by=memory\` and review pod memory limits. Consider scaling the cluster or adding nodes with more memory.`);
        }
        else if (metricName === "k8s.deployment.unavailable_replicas" ||
            metricName.includes("unavailable")) {
            lines.push(`Deployment has unavailable replicas, indicating a mismatch between desired and available replicas.`);
            if (topResource.workloadName) {
                lines.push(`${topResource.workloadType || "Deployment"} \`${topResource.workloadName}\` has **${topResource.metricValue}** unavailable replica(s).`);
            }
            lines.push(`Common causes: failed rolling update, image pull errors (wrong image tag or missing registry credentials), pod crash loops, insufficient cluster resources to schedule new pods, or PodDisruptionBudget blocking updates.`);
            lines.push(`Recommended actions: Check deployment rollout status with \`kubectl rollout status deployment/${topResource.workloadName || "<deployment>"}\` and inspect pod events.`);
        }
        else if (metricName === "k8s.job.failed_pods" ||
            (metricName.includes("job") && metricName.includes("fail"))) {
            lines.push(`Kubernetes Job has failed pods.`);
            if (topResource.workloadName) {
                lines.push(`Job \`${topResource.workloadName}\` has **${topResource.metricValue}** failed pod(s).`);
            }
            lines.push(`Common causes: application error or non-zero exit code, resource limits exceeded (OOMKilled), misconfigured command or arguments, missing environment variables, or timeout exceeded.`);
            lines.push(`Recommended actions: Check job status with \`kubectl describe job ${topResource.workloadName || "<job-name>"}\` and review pod logs for the failed pod(s).`);
        }
        else if (metricName === "k8s.node.filesystem.usage" ||
            metricName.includes("disk") ||
            metricName.includes("filesystem")) {
            lines.push(`Node disk/filesystem usage has exceeded the configured threshold.`);
            if (topResource.nodeName) {
                lines.push(`Node \`${topResource.nodeName}\` filesystem usage is at **${topResource.metricValue.toFixed(1)}%**.`);
            }
            lines.push(`Common causes: container image layers consuming disk space, excessive logging, large emptyDir volumes, or accumulation of unused container images.`);
            lines.push(`Recommended actions: Clean up unused images with \`docker system prune\` or \`crictl rmi --prune\`, check for large log files, and review PersistentVolumeClaim usage.`);
        }
        else if (metricName === "k8s.daemonset.misscheduled_nodes" ||
            metricName.includes("daemonset")) {
            lines.push(`DaemonSet has misscheduled or unavailable nodes.`);
            if (topResource.workloadName) {
                lines.push(`DaemonSet \`${topResource.workloadName}\` has **${topResource.metricValue}** misscheduled node(s).`);
            }
            lines.push(`Common causes: node taints preventing scheduling, incorrect node selectors, or node affinity rules excluding certain nodes.`);
            lines.push(`Recommended actions: Check DaemonSet status with \`kubectl describe daemonset ${topResource.workloadName || "<daemonset>"}\` and verify node labels and taints.`);
        }
        else {
            // Generic Kubernetes context
            lines.push(`Kubernetes metric \`${metricName}\` (${breakdown.metricFriendlyName}) has breached the configured threshold.`);
            if (topResource.podName) {
                lines.push(`Most affected pod: \`${topResource.podName}\``);
            }
            if (topResource.nodeName) {
                lines.push(`Most affected node: \`${topResource.nodeName}\``);
            }
            lines.push(`Recommended actions: Investigate the affected resources using \`kubectl describe\` and \`kubectl logs\` commands.`);
        }
        return lines.join("\n");
    }
    static getMonitorDestinationString(input) {
        var _a, _b;
        if ((_a = input.probeResponse) === null || _a === void 0 ? void 0 : _a.monitorDestination) {
            return MonitorCriteriaEvaluator.stringifyValue(input.probeResponse.monitorDestination);
        }
        if ((_b = input.monitorStep.data) === null || _b === void 0 ? void 0 : _b.monitorDestination) {
            return MonitorCriteriaEvaluator.stringifyValue(input.monitorStep.data.monitorDestination);
        }
        return null;
    }
    static getMonitorPortString(input) {
        var _a, _b;
        if ((_a = input.probeResponse) === null || _a === void 0 ? void 0 : _a.monitorDestinationPort) {
            return MonitorCriteriaEvaluator.stringifyValue(input.probeResponse.monitorDestinationPort);
        }
        if ((_b = input.monitorStep.data) === null || _b === void 0 ? void 0 : _b.monitorDestinationPort) {
            return MonitorCriteriaEvaluator.stringifyValue(input.monitorStep.data.monitorDestinationPort);
        }
        return null;
    }
    static getRequestMethodString(input) {
        if (input.monitor.monitorType === MonitorType.API &&
            input.monitorStep.data) {
            return `${input.monitorStep.data.requestType}`;
        }
        return null;
    }
    static formatMilliseconds(value) {
        if (value === undefined || value === null || isNaN(value)) {
            return null;
        }
        const formatted = MonitorCriteriaMessageFormatter.formatNumber(value, {
            maximumFractionDigits: value < 100 ? 2 : value < 1000 ? 1 : 0,
        });
        return `${formatted !== null && formatted !== void 0 ? formatted : value} ms`;
    }
    static stringifyValue(value) {
        if (value === null || value === undefined) {
            return null;
        }
        try {
            // Handle primitive types directly
            if (typeof value === "string") {
                return value.trim();
            }
            if (typeof value === "number" || typeof value === "boolean") {
                return String(value);
            }
            // Handle class instances with custom toString method (like URL, IP, Hostname)
            if (value instanceof URL ||
                value instanceof IP ||
                value instanceof Hostname ||
                value instanceof Port) {
                return value.toString().trim();
            }
            /*
             * Handle JSON representations of URL, IP, Hostname, Port (e.g., { _type: "URL", value: "https://..." })
             * This can happen when the value wasn't properly deserialized from JSON
             */
            if (typeof value === "object" && value !== null && "_type" in value) {
                const typedValue = value;
                if ((typedValue._type === "URL" ||
                    typedValue._type === "IP" ||
                    typedValue._type === "Hostname" ||
                    typedValue._type === "Port") &&
                    typeof typedValue.value === "string") {
                    return typedValue.value.trim();
                }
            }
            return String(value).trim();
        }
        catch (err) {
            logger.error(err);
            return null;
        }
    }
}
//# sourceMappingURL=MonitorCriteriaEvaluator.js.map