var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import MonitorProbeService from "../../Services/MonitorProbeService";
import MonitorService from "../../Services/MonitorService";
import MonitorStatusTimelineService from "../../Services/MonitorStatusTimelineService";
import logger from "../Logger";
import MonitorCriteriaEvaluator from "./MonitorCriteriaEvaluator";
import MonitorLogUtil from "./MonitorLogUtil";
import MonitorMetricUtil from "./MonitorMetricUtil";
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
import BadDataException from "../../../Types/Exception/BadDataException";
import Semaphore from "../../Infrastructure/Semaphore";
import MonitorType, { MonitorTypeHelper, } from "../../../Types/Monitor/MonitorType";
import MonitorStatusTimeline from "../../../Models/DatabaseModels/MonitorStatusTimeline";
import OneUptimeDate from "../../../Types/Date";
import TelemetryType from "../../../Types/Telemetry/TelemetryType";
import MonitorIncident from "./MonitorIncident";
import MonitorAlert from "./MonitorAlert";
import MonitorMaintenanceSuppression from "./MonitorMaintenanceSuppression";
import MonitorStatusTimelineUtil from "./MonitorStatusTimeline";
import CaptureSpan from "../Telemetry/CaptureSpan";
import ExceptionMessages from "../../../Types/Exception/ExceptionMessages";
import MonitorStatusService from "../../Services/MonitorStatusService";
import { ProbeConnectionStatus } from "../../../Models/DatabaseModels/Probe";
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
export default class MonitorResourceUtil {
    static async monitorResource(dataToProcess) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
        let response = {
            monitorId: dataToProcess.monitorId,
            criteriaMetId: undefined,
            rootCause: null,
        };
        const evaluationSummary = {
            evaluatedAt: OneUptimeDate.getCurrentDate(),
            criteriaResults: [],
            events: [],
        };
        response.evaluationSummary = evaluationSummary;
        dataToProcess.evaluationSummary = evaluationSummary;
        const monitorStatusNameCache = {};
        const getMonitorStatusName = async (statusId) => {
            var _a;
            if (!statusId) {
                return null;
            }
            const cacheKey = statusId.toString();
            if (monitorStatusNameCache[cacheKey] !== undefined) {
                return (_a = monitorStatusNameCache[cacheKey]) !== null && _a !== void 0 ? _a : null;
            }
            const monitorStatus = await MonitorStatusService.findOneBy({
                query: {
                    _id: statusId,
                },
                select: {
                    name: true,
                },
                props: {
                    isRoot: true,
                },
            });
            const statusName = (monitorStatus === null || monitorStatus === void 0 ? void 0 : monitorStatus.name) || null;
            monitorStatusNameCache[cacheKey] = statusName;
            return statusName;
        };
        logger.debug("Processing probe response");
        logger.debug("Monitor ID: " + dataToProcess.monitorId);
        logger.debug("Fetching Monitor...");
        // fetch monitor
        const monitor = await MonitorService.findOneById({
            id: dataToProcess.monitorId,
            select: {
                monitorSteps: true,
                monitorType: true,
                projectId: true,
                disableActiveMonitoring: true,
                disableActiveMonitoringBecauseOfManualIncident: true,
                disableActiveMonitoringBecauseOfScheduledMaintenanceEvent: true,
                currentMonitorStatusId: true,
                _id: true,
                name: true,
                minimumProbeAgreement: true,
            },
            props: {
                isRoot: true,
            },
        });
        logger.debug("Monitor found");
        logger.debug("Monitor ID: " + dataToProcess.monitorId);
        if (!monitor) {
            logger.debug(`${dataToProcess.monitorId.toString()} Monitor not found`);
            throw new BadDataException(ExceptionMessages.MonitorNotFound);
        }
        if (!monitor.projectId) {
            logger.debug(`${dataToProcess.monitorId.toString()} Monitor does not have a projectId`);
            throw new BadDataException("Monitor does not have a projectId");
        }
        dataToProcess.projectId = monitor.projectId;
        if (monitor.disableActiveMonitoring) {
            logger.debug(`${dataToProcess.monitorId.toString()} Monitor is disabled. Please enable it to start monitoring again.`);
            throw new BadDataException(ExceptionMessages.MonitorDisabled);
        }
        if (monitor.disableActiveMonitoringBecauseOfManualIncident) {
            logger.debug(`${dataToProcess.monitorId.toString()} Monitor is disabled because an incident which is created manually is not resolved. Please resolve the incident to start monitoring again.`);
            throw new BadDataException("Monitor is disabled because an incident which is created manually is not resolved. Please resolve the incident to start monitoring again.");
        }
        if (monitor.disableActiveMonitoringBecauseOfScheduledMaintenanceEvent) {
            logger.debug(`${dataToProcess.monitorId.toString()} Monitor is disabled because one of the scheduled maintenance event this monitor is attached to has not ended. Please end the scheduled maintenance event to start monitoring again.`);
            throw new BadDataException("Monitor is disabled because one of the scheduled maintenance event this monitor is attached to has not ended. Please end the scheduled maintenance event to start monitoring again.");
        }
        /*
         * Acquire a per-monitor lock so concurrent results for the same monitor are
         * processed serially. This MUST come after the validation above: acquiring
         * before the not-found/disabled checks meant those throw paths held — and
         * leaked — the lock. redis-semaphore keeps a refresh timer alive until
         * release() is called, so a leaked lock pinned the Redis key until pod
         * restart and forced every other worker to spin on acquire. The try/finally
         * below guarantees the lock is released on every exit path (return or
         * throw); acquireTimeout/retryInterval cap the acquire spin so a contended
         * lock fails fast instead of polling Redis for 10s.
         */
        let mutex = null;
        try {
            mutex = await Semaphore.lock({
                key: dataToProcess.monitorId.toString(),
                namespace: "MonitorResourceUtil.monitorResource",
                acquireTimeout: 2000,
                retryInterval: 100,
                acquireAttemptsLimit: 20,
            });
        }
        catch (err) {
            logger.error(err);
        }
        const releaseMutex = async () => {
            if (mutex) {
                try {
                    await Semaphore.release(mutex);
                }
                catch (err) {
                    logger.error(err);
                }
                mutex = null;
            }
        };
        try {
            let probeName = undefined;
            const monitorName = monitor.name || undefined;
            // save the last log to MonitorProbe.
            // get last log. We do this because there are many monitoring steps and we need to store those.
            logger.debug(`${dataToProcess.monitorId.toString()} - monitor type ${monitor.monitorType}`);
            if (monitor.monitorType &&
                MonitorTypeHelper.isProbableMonitor(monitor.monitorType)) {
                dataToProcess = dataToProcess;
                if (dataToProcess.probeId) {
                    const monitorProbe = await MonitorProbeService.findOneBy({
                        query: {
                            monitorId: monitor.id,
                            probeId: dataToProcess.probeId,
                        },
                        select: {
                            lastMonitoringLog: true,
                            probe: {
                                name: true,
                            },
                        },
                        props: {
                            isRoot: true,
                        },
                    });
                    if (!monitorProbe) {
                        throw new BadDataException("Probe is not assigned to this monitor");
                    }
                    probeName = ((_a = monitorProbe.probe) === null || _a === void 0 ? void 0 : _a.name) || undefined;
                    await MonitorProbeService.updateOneBy({
                        query: {
                            monitorId: monitor.id,
                            probeId: dataToProcess.probeId,
                        },
                        data: {
                            lastMonitoringLog: Object.assign(Object.assign({}, (monitorProbe.lastMonitoringLog || {})), { [dataToProcess.monitorStepId.toString()]: Object.assign(Object.assign({}, JSON.parse(JSON.stringify(dataToProcess))), { monitoredAt: OneUptimeDate.getCurrentDate() }) }),
                        },
                        props: {
                            isRoot: true,
                        },
                    });
                }
            }
            const serverMonitorResponse = monitor.monitorType === MonitorType.Server &&
                dataToProcess.requestReceivedAt
                ? dataToProcess
                : undefined;
            const incomingMonitorRequest = monitor.monitorType === MonitorType.IncomingRequest &&
                dataToProcess.incomingRequestReceivedAt &&
                !dataToProcess
                    .onlyCheckForIncomingRequestReceivedAt
                ? dataToProcess
                : undefined;
            let hasPersistedMonitorData = false;
            const persistLatestMonitorPayload = async () => {
                if (hasPersistedMonitorData) {
                    return;
                }
                if (serverMonitorResponse) {
                    logger.debug(`${dataToProcess.monitorId.toString()} - Server request received at ${serverMonitorResponse.requestReceivedAt}`);
                    logger.debug(dataToProcess);
                    /*
                     * Skip persistence when this evaluation originated from the
                     * CheckOnlineStatus cron (onlyCheckRequestReceivedAt=true). The cron
                     * re-evaluates using the already-stale value read from the DB and has
                     * no new heartbeat data to persist — writing it back would race with
                     * (and overwrite) the ingest path's fresh heartbeat update, causing
                     * the monitor to flap between Online and Offline every minute.
                     */
                    if (!serverMonitorResponse.onlyCheckRequestReceivedAt) {
                        await MonitorService.updateOneById({
                            id: monitor.id,
                            data: {
                                serverMonitorRequestReceivedAt: serverMonitorResponse.requestReceivedAt,
                                serverMonitorResponse,
                            },
                            props: {
                                isRoot: true,
                                ignoreHooks: true,
                            },
                        });
                        logger.debug(`${dataToProcess.monitorId.toString()} - Monitor Server Response Updated`);
                    }
                    else {
                        logger.debug(`${dataToProcess.monitorId.toString()} - Skipping Monitor Server Response persist (cron re-evaluation).`);
                    }
                }
                if (incomingMonitorRequest) {
                    logger.debug(`${dataToProcess.monitorId.toString()} - Incoming request received at ${incomingMonitorRequest.incomingRequestReceivedAt}`);
                    await MonitorService.updateOneById({
                        id: monitor.id,
                        data: {
                            incomingRequestMonitorHeartbeatCheckedAt: OneUptimeDate.getCurrentDate(),
                            incomingMonitorRequest: JSON.parse(JSON.stringify(incomingMonitorRequest)),
                        },
                        props: {
                            isRoot: true,
                            ignoreHooks: true,
                        },
                    });
                    logger.debug(`${dataToProcess.monitorId.toString()} - Monitor Incoming Request Updated`);
                }
                hasPersistedMonitorData = true;
            };
            logger.debug(`${dataToProcess.monitorId.toString()} - Saving monitor metrics`);
            try {
                await MonitorMetricUtil.saveMonitorMetrics({
                    monitorId: monitor.id,
                    projectId: monitor.projectId,
                    dataToProcess: dataToProcess,
                    probeName: probeName || undefined,
                    monitorName: monitorName || undefined,
                });
            }
            catch (err) {
                logger.error("Unable to save metrics");
                logger.error(err);
            }
            logger.debug(`${dataToProcess.monitorId.toString()} - Monitor metrics saved`);
            const monitorSteps = monitor.monitorSteps;
            if (!((_b = monitorSteps.data) === null || _b === void 0 ? void 0 : _b.monitorStepsInstanceArray) ||
                ((_c = monitorSteps.data) === null || _c === void 0 ? void 0 : _c.monitorStepsInstanceArray.length) === 0) {
                logger.debug(`${dataToProcess.monitorId.toString()} - No monitoring steps.`);
                await persistLatestMonitorPayload();
                MonitorLogUtil.saveMonitorLog({
                    monitorId: monitor.id,
                    projectId: monitor.projectId,
                    dataToProcess: dataToProcess,
                });
                return response;
            }
            logger.debug(`${dataToProcess.monitorId.toString()} - Auto resolving criteria instances.`);
            const criteriaInstances = monitorSteps.data.monitorStepsInstanceArray
                .map((step) => {
                var _a;
                return (_a = step.data) === null || _a === void 0 ? void 0 : _a.monitorCriteria;
            })
                .filter((criteria) => {
                return Boolean(criteria);
            })
                .map((criteria) => {
                var _a;
                return [...(((_a = criteria === null || criteria === void 0 ? void 0 : criteria.data) === null || _a === void 0 ? void 0 : _a.monitorCriteriaInstanceArray) || [])];
            })
                .flat();
            const autoResolveCriteriaInstanceIdIncidentIdsDictionary = {};
            const criteriaInstanceMap = {};
            for (const criteriaInstance of criteriaInstances) {
                criteriaInstanceMap[((_d = criteriaInstance.data) === null || _d === void 0 ? void 0 : _d.id) || ""] = criteriaInstance;
                if (((_e = criteriaInstance.data) === null || _e === void 0 ? void 0 : _e.incidents) &&
                    ((_f = criteriaInstance.data) === null || _f === void 0 ? void 0 : _f.incidents.length) > 0) {
                    for (const incidentTemplate of criteriaInstance.data.incidents) {
                        if (incidentTemplate.autoResolveIncident) {
                            if (!autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()]) {
                                autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()] = [];
                            }
                            (_g = autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()]) === null || _g === void 0 ? void 0 : _g.push(incidentTemplate.id);
                        }
                    }
                }
            }
            // alerts.
            const autoResolveCriteriaInstanceIdAlertIdsDictionary = {};
            const criteriaInstanceAlertMap = {};
            for (const criteriaInstance of criteriaInstances) {
                criteriaInstanceAlertMap[((_h = criteriaInstance.data) === null || _h === void 0 ? void 0 : _h.id) || ""] =
                    criteriaInstance;
                if (((_j = criteriaInstance.data) === null || _j === void 0 ? void 0 : _j.alerts) &&
                    ((_k = criteriaInstance.data) === null || _k === void 0 ? void 0 : _k.alerts.length) > 0) {
                    for (const alertTemplate of criteriaInstance.data.alerts) {
                        if (alertTemplate.autoResolveAlert) {
                            if (!autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()]) {
                                autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()] = [];
                            }
                            (_l = autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()]) === null || _l === void 0 ? void 0 : _l.push(alertTemplate.id);
                        }
                    }
                }
            }
            const monitorStep = monitorSteps.data.monitorStepsInstanceArray[0];
            logger.debug(`Monitor Step: ${monitorStep ? monitorStep.id : "undefined"}`);
            if (dataToProcess.monitorStepId) {
                monitorSteps.data.monitorStepsInstanceArray.find((monitorStep) => {
                    return (monitorStep.id.toString() ===
                        dataToProcess.monitorStepId.toString());
                });
                logger.debug(`Found Monitor Step ID: ${dataToProcess.monitorStepId}`);
            }
            if (!monitorStep) {
                logger.debug("No steps found, ignoring everything.");
                await persistLatestMonitorPayload();
                MonitorLogUtil.saveMonitorLog({
                    monitorId: monitor.id,
                    projectId: monitor.projectId,
                    dataToProcess: dataToProcess,
                });
                return response;
            }
            // now process the monitor step
            response.ingestedMonitorStepId = monitorStep.id;
            logger.debug(`Ingested Monitor Step ID: ${monitorStep.id}`);
            //find next monitor step after this one.
            const nextMonitorStepIndex = monitorSteps.data.monitorStepsInstanceArray.findIndex((step) => {
                return step.id.toString() === monitorStep.id.toString();
            });
            response.nextMonitorStepId =
                (_m = monitorSteps.data.monitorStepsInstanceArray[nextMonitorStepIndex + 1]) === null || _m === void 0 ? void 0 : _m.id;
            logger.debug(`Next Monitor Step ID: ${response.nextMonitorStepId}`);
            // now process probe response monitors
            logger.debug(`${dataToProcess.monitorId.toString()} - Processing monitor step...`);
            response = await MonitorCriteriaEvaluator.processMonitorStep({
                dataToProcess: dataToProcess,
                monitorStep: monitorStep,
                monitor: monitor,
                probeApiIngestResponse: response,
                evaluationSummary: evaluationSummary,
            });
            // Check probe agreement for probe-based monitors
            if (monitor.monitorType &&
                MonitorTypeHelper.isProbableMonitor(monitor.monitorType)) {
                const probeAgreementResult = await MonitorResourceUtil.checkProbeAgreement({
                    monitor: monitor,
                    monitorStep: monitorStep,
                    currentCriteriaMetId: response.criteriaMetId || null,
                    currentRootCause: response.rootCause || null,
                });
                // Add probe agreement event to evaluation summary
                evaluationSummary.events.push({
                    type: "probe-agreement",
                    title: "Probe Agreement Check",
                    message: probeAgreementResult.hasAgreement
                        ? `Probe agreement reached: ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree (${probeAgreementResult.totalActiveProbes} active probes total).`
                        : `Probe agreement not reached: ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree (${probeAgreementResult.totalActiveProbes} active probes total). Skipping status change.`,
                    at: OneUptimeDate.getCurrentDate(),
                });
                if (!probeAgreementResult.hasAgreement) {
                    logger.debug(`${dataToProcess.monitorId.toString()} - Probe agreement not met. ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree. Skipping status change.`);
                    // Release lock and return early - no status change
                    await releaseMutex();
                    await persistLatestMonitorPayload();
                    MonitorLogUtil.saveMonitorLog({
                        monitorId: monitor.id,
                        projectId: monitor.projectId,
                        dataToProcess: dataToProcess,
                    });
                    response.evaluationSummary = evaluationSummary;
                    return response;
                }
                // Use the agreed criteria result
                response.criteriaMetId = probeAgreementResult.agreedCriteriaId
                    ? probeAgreementResult.agreedCriteriaId
                    : undefined;
                response.rootCause = probeAgreementResult.agreedRootCause;
                // Add probe names in agreement to the root cause
                if (response.rootCause &&
                    probeAgreementResult.agreedProbeNames.length > 0) {
                    response.rootCause += `
**Probes in Agreement**: ${probeAgreementResult.agreedProbeNames.join(", ")}
`;
                }
            }
            if (response.criteriaMetId && response.rootCause) {
                logger.debug(`${dataToProcess.monitorId.toString()} - Criteria met: ${response.criteriaMetId}`);
                logger.debug(`${dataToProcess.monitorId.toString()} - Root cause: ${response.rootCause}`);
                let telemetryQuery = undefined;
                if (dataToProcess && dataToProcess.logQuery) {
                    telemetryQuery = {
                        telemetryQuery: dataToProcess.logQuery,
                        telemetryType: TelemetryType.Log,
                        metricViewData: null,
                    };
                    logger.debug(`${dataToProcess.monitorId.toString()} - Log query found.`);
                }
                if (dataToProcess &&
                    dataToProcess.spanQuery) {
                    telemetryQuery = {
                        telemetryQuery: dataToProcess.spanQuery,
                        telemetryType: TelemetryType.Trace,
                        metricViewData: null,
                    };
                    logger.debug(`${dataToProcess.monitorId.toString()} - Span query found.`);
                }
                if (dataToProcess &&
                    dataToProcess.metricViewConfig &&
                    dataToProcess.startAndEndDate) {
                    telemetryQuery = {
                        telemetryQuery: null,
                        telemetryType: TelemetryType.Metric,
                        metricViewData: {
                            startAndEndDate: dataToProcess.startAndEndDate ||
                                null,
                            queryConfigs: dataToProcess
                                .metricViewConfig.queryConfigs,
                            formulaConfigs: dataToProcess
                                .metricViewConfig.formulaConfigs,
                        },
                    };
                    logger.debug(`${dataToProcess.monitorId.toString()} - Span query found.`);
                }
                if (dataToProcess &&
                    dataToProcess.exceptionQuery) {
                    const exceptionResponse = dataToProcess;
                    telemetryQuery = {
                        telemetryQuery: exceptionResponse.exceptionQuery,
                        telemetryType: TelemetryType.Exception,
                        metricViewData: null,
                    };
                    logger.debug(`${dataToProcess.monitorId.toString()} - Exception query found.`);
                }
                const matchedCriteriaInstance = criteriaInstanceMap[response.criteriaMetId];
                const monitorStatusTimelineChange = await MonitorStatusTimelineUtil.updateMonitorStatusTimeline({
                    monitor: monitor,
                    rootCause: response.rootCause,
                    dataToProcess: dataToProcess,
                    criteriaInstance: matchedCriteriaInstance,
                    props: {
                        telemetryQuery: telemetryQuery,
                    },
                });
                if (monitorStatusTimelineChange) {
                    const changedStatusName = await getMonitorStatusName(((_o = matchedCriteriaInstance.data) === null || _o === void 0 ? void 0 : _o.monitorStatusId) ||
                        monitorStatusTimelineChange.monitorStatusId);
                    evaluationSummary.events.push({
                        type: "monitor-status-changed",
                        title: "Monitor status updated",
                        message: changedStatusName
                            ? `Monitor status changed to "${changedStatusName}" because criteria "${((_p = matchedCriteriaInstance.data) === null || _p === void 0 ? void 0 : _p.name) || "Unnamed criteria"}" was met.`
                            : `Monitor status changed because criteria "${((_q = matchedCriteriaInstance.data) === null || _q === void 0 ? void 0 : _q.name) || "Unnamed criteria"}" was met.`,
                        relatedCriteriaId: (_r = matchedCriteriaInstance.data) === null || _r === void 0 ? void 0 : _r.id,
                        at: OneUptimeDate.getCurrentDate(),
                    });
                }
                /*
                 * For grouped metric monitors, work out which breaching series
                 * belong to a resource that is currently inside an ongoing
                 * scheduled maintenance window. Those series are suppressed
                 * below so the monitor keeps alerting on the rest. Computed once
                 * and shared by both the incident and alert paths. Cheap on the
                 * common path: no per-series matches, or no ongoing maintenance,
                 * returns an empty set after at most one query.
                 */
                const suppressedSeriesFingerprints = await MonitorMaintenanceSuppression.getSuppressedSeriesFingerprints({
                    projectId: monitor.projectId,
                    matchesPerSeries: response.perSeriesMatches,
                });
                await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
                    monitor: monitor,
                    rootCause: response.rootCause,
                    dataToProcess: dataToProcess,
                    autoResolveCriteriaInstanceIdIncidentIdsDictionary,
                    criteriaInstance: matchedCriteriaInstance,
                    evaluationSummary: evaluationSummary,
                    props: {
                        telemetryQuery: telemetryQuery,
                    },
                    matchesPerSeries: response.perSeriesMatches,
                    suppressedSeriesFingerprints,
                });
                await MonitorAlert.criteriaMetCreateAlertsAndUpdateMonitorStatus({
                    monitor: monitor,
                    rootCause: response.rootCause,
                    dataToProcess: dataToProcess,
                    autoResolveCriteriaInstanceIdAlertIdsDictionary,
                    criteriaInstance: criteriaInstanceAlertMap[response.criteriaMetId],
                    evaluationSummary: evaluationSummary,
                    props: {
                        telemetryQuery: telemetryQuery,
                    },
                    matchesPerSeries: response.perSeriesMatches,
                    suppressedSeriesFingerprints,
                });
            }
            else if (!response.criteriaMetId &&
                monitorSteps.data.defaultMonitorStatusId &&
                ((_s = monitor.currentMonitorStatusId) === null || _s === void 0 ? void 0 : _s.toString()) !==
                    monitorSteps.data.defaultMonitorStatusId.toString()) {
                logger.debug(`${dataToProcess.monitorId.toString()} - No criteria met. Change to default status.`);
                await MonitorIncident.checkOpenIncidentsAndCloseIfResolved({
                    monitorId: monitor.id,
                    autoResolveCriteriaInstanceIdIncidentIdsDictionary,
                    rootCause: "No monitoring criteria met. Change to default status.",
                    criteriaInstance: null, // no criteria met!
                    dataToProcess: dataToProcess,
                    evaluationSummary: evaluationSummary,
                });
                await MonitorAlert.checkOpenAlertsAndCloseIfResolved({
                    monitorId: monitor.id,
                    autoResolveCriteriaInstanceIdAlertIdsDictionary,
                    rootCause: "No monitoring criteria met. Change to default status.",
                    criteriaInstance: null, // no criteria met!
                    dataToProcess: dataToProcess,
                    evaluationSummary: evaluationSummary,
                });
                // get last monitor status timeline.
                const lastMonitorStatusTimeline = await MonitorStatusTimelineService.findOneBy({
                    query: {
                        monitorId: monitor.id,
                        projectId: monitor.projectId,
                    },
                    select: {
                        _id: true,
                        monitorStatusId: true,
                    },
                    sort: {
                        startsAt: SortOrder.Descending,
                    },
                    props: {
                        isRoot: true,
                    },
                });
                if (lastMonitorStatusTimeline &&
                    lastMonitorStatusTimeline.monitorStatusId &&
                    lastMonitorStatusTimeline.monitorStatusId.toString() ===
                        monitorSteps.data.defaultMonitorStatusId.toString()) {
                    /*
                     * status is same as last status. do not create new status timeline.
                     * do nothing! status is same as last status.
                     */
                }
                else {
                    // if no criteria is met then update monitor to default state.
                    const monitorStatusTimeline = new MonitorStatusTimeline();
                    monitorStatusTimeline.monitorId = monitor.id;
                    monitorStatusTimeline.monitorStatusId =
                        monitorSteps.data.defaultMonitorStatusId;
                    monitorStatusTimeline.projectId = monitor.projectId;
                    monitorStatusTimeline.isOwnerNotified = true; // no need to notify owner as this is default status.
                    monitorStatusTimeline.statusChangeLog = JSON.parse(JSON.stringify(dataToProcess));
                    monitorStatusTimeline.rootCause =
                        "No monitoring criteria met. Change to default status. ";
                    await MonitorStatusTimelineService.create({
                        data: monitorStatusTimeline,
                        props: {
                            isRoot: true,
                        },
                    });
                    logger.debug(`${dataToProcess.monitorId.toString()} - Monitor status updated to default.`);
                    const defaultStatusName = await getMonitorStatusName(monitorSteps.data.defaultMonitorStatusId);
                    evaluationSummary.events.push({
                        type: "monitor-status-changed",
                        title: "Monitor status reverted",
                        message: defaultStatusName
                            ? `Monitor status reverted to "${defaultStatusName}" because no monitoring criteria were met.`
                            : "Monitor status reverted to its default state because no monitoring criteria were met.",
                        at: OneUptimeDate.getCurrentDate(),
                    });
                }
            }
            await releaseMutex();
            await persistLatestMonitorPayload();
            MonitorLogUtil.saveMonitorLog({
                monitorId: monitor.id,
                projectId: monitor.projectId,
                dataToProcess: dataToProcess,
            });
            return response;
        }
        finally {
            await releaseMutex();
        }
    }
    static async checkProbeAgreement(input) {
        var _a, _b, _c, _d, _e, _f, _g;
        const { monitor, monitorStep, currentCriteriaMetId, currentRootCause } = input;
        /*
         * If minimumProbeAgreement is not set, all probes must agree
         * Get all MonitorProbes for this monitor with their probe connection status
         */
        const monitorProbes = await MonitorProbeService.findBy({
            query: {
                monitorId: monitor.id,
            },
            select: {
                probeId: true,
                isEnabled: true,
                lastMonitoringLog: true,
                probe: {
                    connectionStatus: true,
                    name: true,
                },
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
            props: {
                isRoot: true,
            },
        });
        // Filter to only active probes (enabled AND connected)
        const activeProbes = monitorProbes.filter((mp) => {
            var _a;
            return (mp.isEnabled &&
                ((_a = mp.probe) === null || _a === void 0 ? void 0 : _a.connectionStatus) === ProbeConnectionStatus.Connected);
        });
        // If no active probes, treat as agreement met (nothing to compare)
        if (activeProbes.length === 0) {
            logger.debug(`${(_a = monitor.id) === null || _a === void 0 ? void 0 : _a.toString()} - No active probes found. Treating as agreement met.`);
            return {
                hasAgreement: true,
                agreementCount: 0,
                requiredCount: 0,
                totalActiveProbes: 0,
                agreedCriteriaId: currentCriteriaMetId,
                agreedRootCause: currentRootCause,
                agreedProbeNames: [],
            };
        }
        // Determine required count for agreement
        const requiredCount = (_b = monitor.minimumProbeAgreement) !== null && _b !== void 0 ? _b : activeProbes.length;
        // Effective threshold cannot exceed number of active probes
        const effectiveThreshold = Math.min(requiredCount, activeProbes.length);
        /*
         * Count how many probes agree on each criteria result
         * Key: criteriaId or "none" for no criteria met
         * Value: { count, rootCause, probeNames }
         */
        const criteriaAgreements = new Map();
        const stepId = monitorStep.id.toString();
        for (const monitorProbe of activeProbes) {
            const probeResponse = (_c = monitorProbe.lastMonitoringLog) === null || _c === void 0 ? void 0 : _c[stepId];
            if (!probeResponse) {
                // No response yet for this step from this probe - skip
                logger.debug(`${(_d = monitor.id) === null || _d === void 0 ? void 0 : _d.toString()} - Probe ${(_e = monitorProbe.probeId) === null || _e === void 0 ? void 0 : _e.toString()} has no response for step ${stepId}. Skipping.`);
                continue;
            }
            // Evaluate this probe's response against criteria
            const tempResponse = {
                monitorId: monitor.id,
                criteriaMetId: undefined,
                rootCause: null,
            };
            const tempEvaluationSummary = {
                evaluatedAt: OneUptimeDate.getCurrentDate(),
                criteriaResults: [],
                events: [],
            };
            const evaluatedResponse = await MonitorCriteriaEvaluator.processMonitorStep({
                dataToProcess: probeResponse,
                monitorStep: monitorStep,
                monitor: monitor,
                probeApiIngestResponse: tempResponse,
                evaluationSummary: tempEvaluationSummary,
            });
            // Record the result
            const criteriaKey = evaluatedResponse.criteriaMetId || "none";
            const existing = criteriaAgreements.get(criteriaKey);
            // Get probe name for this monitor probe
            const probeName = ((_f = monitorProbe.probe) === null || _f === void 0 ? void 0 : _f.name) || "Unknown Probe";
            if (existing) {
                existing.count += 1;
                existing.probeNames.push(probeName);
            }
            else {
                criteriaAgreements.set(criteriaKey, {
                    count: 1,
                    rootCause: evaluatedResponse.rootCause,
                    probeNames: [probeName],
                });
            }
        }
        // Find the criteria with the most agreement
        let maxCount = 0;
        let winningCriteriaId = null;
        let winningRootCause = null;
        let winningProbeNames = [];
        for (const [criteriaId, data] of criteriaAgreements) {
            if (data.count > maxCount) {
                maxCount = data.count;
                winningCriteriaId = criteriaId === "none" ? null : criteriaId;
                winningRootCause = data.rootCause;
                winningProbeNames = data.probeNames;
            }
        }
        // Check if the winning criteria has reached the agreement threshold
        const hasAgreement = maxCount >= effectiveThreshold;
        logger.debug(`${(_g = monitor.id) === null || _g === void 0 ? void 0 : _g.toString()} - Probe agreement check: ${maxCount}/${effectiveThreshold} probes agree on criteria "${winningCriteriaId || "none"}". Agreement ${hasAgreement ? "reached" : "not reached"}.`);
        return {
            hasAgreement,
            agreementCount: maxCount,
            requiredCount: effectiveThreshold,
            totalActiveProbes: activeProbes.length,
            agreedCriteriaId: hasAgreement ? winningCriteriaId : null,
            agreedRootCause: hasAgreement ? winningRootCause : null,
            agreedProbeNames: hasAgreement ? winningProbeNames : [],
        };
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], MonitorResourceUtil, "monitorResource", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], MonitorResourceUtil, "checkProbeAgreement", null);
//# sourceMappingURL=MonitorResource.js.map