"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProcessHealthMonitor = void 0;
const HealthCheckStrategy_1 = require("./HealthCheckStrategy");
const Parser_1 = require("./Parser");
const String_1 = require("./String");
const Task_1 = require("./Task");
/**
 * Manages health checking logic for processes.
 * Provides centralized health assessment and monitoring capabilities.
 */
class ProcessHealthMonitor {
    options;
    emitter;
    #healthCheckStates = new Map();
    healthStrategy;
    constructor(options, emitter, healthStrategy) {
        this.options = options;
        this.emitter = emitter;
        this.healthStrategy = healthStrategy ?? new HealthCheckStrategy_1.CompositeHealthCheckStrategy();
    }
    /**
     * Initialize health monitoring for a process
     */
    initializeProcess(pid) {
        this.#healthCheckStates.set(pid, {
            lastHealthCheck: Date.now(),
            healthCheckFailures: 0,
            lastJobFailed: false,
        });
    }
    /**
     * Clean up health monitoring for a process
     */
    cleanupProcess(pid) {
        this.#healthCheckStates.delete(pid);
    }
    /**
     * Record that a job failed for a process
     */
    recordJobFailure(pid) {
        const state = this.#healthCheckStates.get(pid);
        if (state != null) {
            state.lastJobFailed = true;
        }
    }
    /**
     * Record that a job succeeded for a process
     */
    recordJobSuccess(pid) {
        const state = this.#healthCheckStates.get(pid);
        if (state != null) {
            state.lastJobFailed = false;
        }
    }
    /**
     * Assess the health of a process and return why it's not healthy, or null if healthy
     */
    assessHealth(process, overrideReason) {
        if (overrideReason != null)
            return overrideReason;
        const state = this.#healthCheckStates.get(process.pid);
        if (state != null && state.healthCheckFailures > 0) {
            return "unhealthy";
        }
        return this.healthStrategy.assess(process, this.options);
    }
    /**
     * Check if a process is healthy
     */
    isHealthy(process, overrideReason) {
        return this.assessHealth(process, overrideReason) == null;
    }
    /**
     * Assess why a process is not ready (combines health and business)
     */
    assessReadiness(process, overrideReason) {
        return !process.idle ? "busy" : this.assessHealth(process, overrideReason);
    }
    /**
     * Check if a process is ready to handle tasks
     */
    isReady(process, overrideReason) {
        return this.assessReadiness(process, overrideReason) == null;
    }
    /**
     * Run a health check on a process if needed
     */
    maybeRunHealthCheck(process) {
        const hcc = this.options.healthCheckCommand;
        // if there's no health check command, no-op.
        if (hcc == null || (0, String_1.blank)(hcc))
            return;
        // if the prior health check failed, .ready will be false
        if (!this.isReady(process))
            return;
        const state = this.#healthCheckStates.get(process.pid);
        if (state == null)
            return;
        if (state.lastJobFailed ||
            (this.options.healthCheckIntervalMillis > 0 &&
                Date.now() - state.lastHealthCheck >
                    this.options.healthCheckIntervalMillis)) {
            state.lastHealthCheck = Date.now();
            const t = new Task_1.Task(hcc, Parser_1.SimpleParser);
            t.promise
                .catch((err) => {
                this.emitter.emit("healthCheckError", err instanceof Error ? err : new Error(String(err)), 
                // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
                process);
                state.healthCheckFailures++;
                // BatchCluster will see we're unhealthy and reap us later
            })
                .finally(() => {
                state.lastHealthCheck = Date.now();
            });
            // Execute the health check task on the process
            if (process.execTask(t)) {
                return t;
            }
        }
        return;
    }
    /**
     * Get health statistics for monitoring
     */
    getHealthStats() {
        let totalFailures = 0;
        let processesWithFailures = 0;
        for (const state of this.#healthCheckStates.values()) {
            totalFailures += state.healthCheckFailures;
            if (state.healthCheckFailures > 0) {
                processesWithFailures++;
            }
        }
        return {
            monitoredProcesses: this.#healthCheckStates.size,
            totalHealthCheckFailures: totalFailures,
            processesWithFailures,
        };
    }
    /**
     * Reset health check failures for a process (useful for recovery scenarios)
     */
    resetHealthCheckFailures(pid) {
        const state = this.#healthCheckStates.get(pid);
        if (state != null) {
            state.healthCheckFailures = 0;
        }
    }
    /**
     * Get health check state for a specific process
     */
    getProcessHealthState(pid) {
        return this.#healthCheckStates.get(pid);
    }
}
exports.ProcessHealthMonitor = ProcessHealthMonitor;
//# sourceMappingURL=ProcessHealthMonitor.js.map