/**
 * Service Health Check and Monitoring System
 * Provides per-service and aggregated health status with detailed diagnostics
 */
import { Logger } from '../config/logger.js';
import { validateUrl } from '../security/UrlValidator.js';
/**
 * Health Check Endpoint Handler
 * Each service should expose this as GET /health
 */
export const HealthCheckHandler = Object.freeze({
    /**
     * Create a new health check handler instance
     */
    create(serviceName, version, port, domain, dependencies = [], checkDatabase) {
        /**
         * Perform full health check
         */
        const performHealthCheck = async (startTime) => {
            const result = {
                service: serviceName,
                domain,
                port,
                status: 'healthy',
                timestamp: new Date().toISOString(),
                responseTime: 0,
                version,
                checks: {
                    http: true,
                    database: undefined,
                    dependencies: {},
                },
            };
            await checkDatabaseHealth(result, checkDatabase);
            await checkDependenciesHealth(result, dependencies);
            result.responseTime = Date.now() - startTime;
            return result;
        };
        return {
            /**
             * Handle health check request
             * Returns JSON with service health status
             */
            async handle(_req, res) {
                const startTime = Date.now();
                try {
                    const result = await performHealthCheck(startTime);
                    const statusCode = getStatusCode(result.status);
                    res.setStatus(statusCode).json(result);
                }
                catch (err) {
                    Logger.error('Health check handler error:', err);
                    const result = {
                        service: serviceName,
                        domain,
                        port,
                        status: 'unhealthy',
                        timestamp: new Date().toISOString(),
                        responseTime: Date.now() - startTime,
                        version,
                        checks: { http: false },
                        message: err.message,
                    };
                    res.setStatus(503).json(result);
                }
            },
        };
    },
});
/**
 * Get status code based on health status
 */
function getStatusCode(status) {
    if (status === 'healthy')
        return 200;
    if (status === 'degraded')
        return 202;
    return 503;
}
/**
 * Check single dependency service
 */
async function checkDependencyService(depService) {
    try {
        // In a real environment, we would resolve the service URL
        // For now, we assume it's on localhost with a standard port mapping or discovery
        const url = `http://localhost:7777/health?service=${depService}`;
        // SSRF Protection
        validateUrl(url);
        const depResponse = await fetch(url, {
            signal: AbortSignal.timeout(2000),
        });
        return depResponse.ok;
    }
    catch (error) {
        Logger.error(`Dependency health check failed for ${depService}`, error);
        return false;
    }
}
/**
 * Check all dependencies health
 */
async function checkDependenciesHealth(result, dependencies) {
    if (dependencies.length === 0)
        return;
    result.checks.dependencies ??= {};
    const checks = await Promise.all(dependencies.map(async (depService) => ({
        depService,
        isHealthy: await checkDependencyService(depService),
    })));
    for (const { depService, isHealthy } of checks) {
        result.checks.dependencies[depService] = isHealthy;
        if (!isHealthy) {
            result.status = 'degraded';
        }
    }
}
/**
 * Check database health
 */
async function checkDatabaseHealth(result, checkDatabase) {
    if (checkDatabase === undefined)
        return;
    try {
        result.checks.database = await checkDatabase();
        if (!result.checks.database) {
            result.status = 'degraded';
            result.message = 'Database connection failed';
        }
    }
    catch (err) {
        Logger.error('Database health check failed:', err);
        result.checks.database = false;
        result.status = 'unhealthy';
        result.message = 'Database check error';
    }
}
/**
 * Service Health Monitor
 * Monitors multiple services and provides aggregated health status
 */
export const ServiceHealthMonitor = Object.freeze({
    /**
     * Create a new service health monitor instance
     */
    create(healthCheckUrls, // serviceName -> healthCheckUrl
    intervalMs = 30000 // Check every 30 seconds
    ) {
        let checkIntervalId;
        const lastResults = new Map();
        return createMonitorObject(healthCheckUrls, intervalMs, lastResults, () => checkIntervalId, (id) => {
            checkIntervalId = id;
        });
    },
});
/**
 * Create the monitor object with methods
 */
function createMonitorObject(healthCheckUrls, intervalMs, lastResults, getIntervalId, setIntervalId) {
    const self = {
        /**
         * Start continuous health monitoring
         */
        start() {
            startMonitoring(intervalMs, async () => self.checkAll(), getIntervalId, setIntervalId);
        },
        /**
         * Stop health monitoring
         */
        stop() {
            stopMonitoring(getIntervalId, setIntervalId);
        },
        /**
         * Check all services
         */
        async checkAll() {
            const status = await runAllChecks(healthCheckUrls, lastResults);
            logHealthSummary(status);
            return status;
        },
        /**
         * Get last known status of all services
         */
        getLastStatus() {
            const results = Array.from(lastResults.values());
            return calculateAggregatedStatus(results);
        },
        /**
         * Get health status for specific service
         */
        getServiceStatus(serviceName) {
            return lastResults.get(serviceName);
        },
        /**
         * Check if all services are healthy
         */
        areAllHealthy() {
            return checkAllHealthy(lastResults, healthCheckUrls);
        },
        /**
         * Check if service is healthy
         */
        isServiceHealthy(serviceName) {
            const status = self.getServiceStatus(serviceName);
            return status?.status === 'healthy';
        },
    };
    return self;
}
/**
 * Start monitoring logic
 */
function startMonitoring(intervalMs, checkAll, getIntervalId, setIntervalId) {
    if (getIntervalId() !== undefined) {
        Logger.warn('Health monitoring already started');
        return;
    }
    Logger.info('🏥 Starting service health monitoring');
    let inFlight = false;
    const safeCheckAll = async () => {
        if (inFlight)
            return;
        inFlight = true;
        try {
            await checkAll();
        }
        catch (err) {
            Logger.error('Service health monitoring tick failed:', err);
        }
        finally {
            inFlight = false;
        }
    };
    void safeCheckAll(); // Initial check
    const id = setInterval(() => {
        void safeCheckAll();
    }, intervalMs);
    // Node: allow process to exit; other runtimes may not support unref()
    if (isUnrefableTimer(id)) {
        id.unref();
    }
    setIntervalId(id);
}
function isUnrefableTimer(value) {
    if (typeof value !== 'object' || value === null)
        return false;
    return 'unref' in value && typeof value.unref === 'function';
}
/**
 * Stop monitoring logic
 */
function stopMonitoring(getIntervalId, setIntervalId) {
    const id = getIntervalId();
    if (id !== undefined) {
        clearInterval(id);
        setIntervalId(undefined);
        Logger.info('🛑 Health monitoring stopped');
    }
}
/**
 * Check if all services are healthy
 */
function checkAllHealthy(lastResults, healthCheckUrls) {
    const results = Array.from(lastResults.values());
    if (results.length === 0 && Object.keys(healthCheckUrls).length > 0) {
        return false;
    }
    return results.length > 0 && results.every((r) => r.status === 'healthy');
}
/**
 * Check single service
 */
async function checkService(serviceName, url) {
    const startTime = Date.now();
    try {
        const response = await fetch(url, {
            signal: AbortSignal.timeout(5000),
        });
        await response.json();
        return {
            service: serviceName,
            domain: 'unknown',
            port: 0,
            status: 'healthy',
            timestamp: new Date().toISOString(),
            responseTime: Date.now() - startTime,
            version: 'unknown',
            checks: { http: true },
        };
    }
    catch (err) {
        Logger.error(`Service health check failed for ${serviceName}:`, err);
        return {
            service: serviceName,
            domain: 'unknown',
            port: 0,
            status: 'stopped',
            timestamp: new Date().toISOString(),
            responseTime: Date.now() - startTime,
            version: 'unknown',
            checks: { http: false },
            message: `Service check failed: ${err.message}`,
        };
    }
}
/**
 * Calculate aggregated health status from results
 */
function calculateAggregatedStatus(results) {
    const healthy = results.filter((r) => r.status === 'healthy').length;
    const degraded = results.filter((r) => r.status === 'degraded').length;
    const unhealthy = results.filter((r) => r.status === 'unhealthy').length;
    return {
        timestamp: new Date().toISOString(),
        totalServices: results.length,
        healthy,
        degraded,
        unhealthy,
        services: results,
    };
}
/**
 * Log health summary
 */
function logHealthSummary(status) {
    if (status.unhealthy > 0) {
        Logger.warn(`⚠️  Health check: ${status.healthy} healthy, ${status.degraded} degraded, ${status.unhealthy} unhealthy`);
    }
    else if (status.degraded > 0) {
        Logger.warn(`⚠️  Health check: ${status.healthy} healthy, ${status.degraded} degraded`);
    }
    else {
        Logger.info(`✅ All ${status.healthy} services healthy`);
    }
}
/**
 * Health Check Aggregator Endpoint
 * Exposes aggregated health status at GET /health/services
 */
export const HealthCheckAggregator = Object.freeze({
    /**
     * Create a new health check aggregator instance
     */
    create(monitor) {
        return {
            /**
             * Handle aggregated health check request
             */
            // eslint-disable-next-line @typescript-eslint/require-await
            async handle(_req, res) {
                const status = monitor.getLastStatus();
                let statusCode;
                if (status.unhealthy > 0) {
                    statusCode = 503;
                }
                else if (status.degraded > 0) {
                    statusCode = 202;
                }
                else {
                    statusCode = 200;
                }
                res.setStatus(statusCode).json(status);
            },
        };
    },
});
/**
 * Run all service health checks
 */
async function runAllChecks(healthCheckUrls, lastResults) {
    const checks = Object.entries(healthCheckUrls).map(async ([serviceName, url]) => checkService(serviceName, url));
    const results = await Promise.all(checks);
    // Store results
    results.forEach((result) => {
        lastResults.set(result.service, result);
    });
    return calculateAggregatedStatus(results);
}
export default {
    HealthCheckHandler,
    ServiceHealthMonitor,
    HealthCheckAggregator,
};
