/**
 * Health Check module for API endpoints
 *
 * Provides:
 * - Endpoint health monitoring
 * - Latency tracking
 * - Automatic failover support
 * - Health status reporting
 */
import { EventEmitter } from 'events';
const DEFAULT_OPTIONS = {
    interval: 30000,
    timeout: 5000,
    failureThreshold: 3,
    degradedThreshold: 2000,
    maxHistory: 100,
};
/**
 * Health Check Manager
 */
export class HealthCheckManager extends EventEmitter {
    options;
    customCheckFn;
    endpoints = new Map();
    intervals = new Map();
    constructor(options = {}) {
        super();
        this.options = { ...DEFAULT_OPTIONS, ...options };
        this.customCheckFn = options.checkFn;
    }
    /**
     * Add an endpoint to monitor
     */
    addEndpoint(url) {
        if (this.endpoints.has(url))
            return;
        const health = {
            url,
            status: 'unknown',
            lastCheck: null,
            lastSuccess: null,
            latency: 0,
            failureCount: 0,
            successCount: 0,
            history: [],
        };
        this.endpoints.set(url, health);
        // Perform initial check
        this.checkEndpoint(url);
    }
    /**
     * Remove an endpoint from monitoring
     */
    removeEndpoint(url) {
        this.stopMonitoring(url);
        this.endpoints.delete(url);
    }
    /**
     * Start continuous monitoring for an endpoint
     */
    startMonitoring(url) {
        if (this.intervals.has(url))
            return;
        // Add endpoint if not exists
        if (!this.endpoints.has(url)) {
            this.addEndpoint(url);
        }
        // Set up interval
        const intervalId = setInterval(() => {
            this.checkEndpoint(url);
        }, this.options.interval);
        this.intervals.set(url, intervalId);
    }
    /**
     * Stop monitoring an endpoint
     */
    stopMonitoring(url) {
        const intervalId = this.intervals.get(url);
        if (intervalId) {
            clearInterval(intervalId);
            this.intervals.delete(url);
        }
    }
    /**
     * Perform a health check on an endpoint
     */
    async checkEndpoint(url) {
        const health = this.endpoints.get(url);
        if (!health) {
            throw new Error(`Endpoint not registered: ${url}`);
        }
        const startTime = Date.now();
        let result;
        try {
            const isHealthy = await this.performCheck(url);
            const latency = Date.now() - startTime;
            if (isHealthy) {
                health.successCount++;
                health.failureCount = 0;
                health.lastSuccess = new Date();
                health.latency = latency;
                const status = latency > this.options.degradedThreshold ? 'degraded' : 'healthy';
                result = {
                    status,
                    latency,
                    timestamp: new Date(),
                };
                health.status = status;
            }
            else {
                throw new Error('Health check returned false');
            }
        }
        catch (error) {
            const latency = Date.now() - startTime;
            health.failureCount++;
            const status = health.failureCount >= this.options.failureThreshold ? 'unhealthy' : 'degraded';
            result = {
                status,
                latency,
                timestamp: new Date(),
                error: error instanceof Error ? error.message : String(error),
            };
            health.status = status;
        }
        // Update health record
        health.lastCheck = result.timestamp;
        health.history.push(result);
        // Trim history
        if (health.history.length > this.options.maxHistory) {
            health.history = health.history.slice(-this.options.maxHistory);
        }
        // Emit events
        this.emit('check', { url, result });
        if (result.status === 'unhealthy') {
            this.emit('unhealthy', { url, result });
        }
        else if (result.status === 'healthy' && health.failureCount === 0) {
            this.emit('recovered', { url, result });
        }
        return result;
    }
    /**
     * Perform the actual health check
     */
    async performCheck(url) {
        if (this.customCheckFn) {
            return this.customCheckFn(url);
        }
        // Default: HEAD request with timeout
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.options.timeout);
        try {
            const response = await fetch(url, {
                method: 'HEAD',
                signal: controller.signal,
            });
            return response.ok;
        }
        finally {
            clearTimeout(timeoutId);
        }
    }
    /**
     * Get health status for an endpoint
     */
    getHealth(url) {
        return this.endpoints.get(url);
    }
    /**
     * Get all endpoint health statuses
     */
    getAllHealth() {
        return Array.from(this.endpoints.values());
    }
    /**
     * Get the healthiest endpoint from a list
     */
    getHealthiestEndpoint(urls) {
        let healthiest = null;
        for (const url of urls) {
            const health = this.endpoints.get(url);
            if (!health)
                continue;
            // Calculate score (lower is better)
            let score = health.latency;
            if (health.status === 'unhealthy')
                score += 100000;
            if (health.status === 'degraded')
                score += 10000;
            if (health.status === 'unknown')
                score += 5000;
            if (!healthiest || score < healthiest.score) {
                healthiest = { url, score };
            }
        }
        return healthiest?.url || null;
    }
    /**
     * Get summary statistics
     */
    getSummary() {
        const summary = { total: 0, healthy: 0, degraded: 0, unhealthy: 0, unknown: 0 };
        for (const health of this.endpoints.values()) {
            summary.total++;
            summary[health.status]++;
        }
        return summary;
    }
    /**
     * Format health status for display
     */
    formatHealth() {
        const lines = ['Endpoint Health Status:', ''];
        for (const health of this.endpoints.values()) {
            const statusIcon = health.status === 'healthy'
                ? '[OK]'
                : health.status === 'degraded'
                    ? '[WARN]'
                    : health.status === 'unhealthy'
                        ? '[FAIL]'
                        : '[?]';
            lines.push(`${statusIcon} ${health.url}`);
            lines.push(`    Status: ${health.status}`);
            lines.push(`    Latency: ${health.latency}ms`);
            lines.push(`    Success: ${health.successCount}, Failures: ${health.failureCount}`);
            if (health.lastCheck) {
                lines.push(`    Last Check: ${health.lastCheck.toISOString()}`);
            }
            lines.push('');
        }
        const summary = this.getSummary();
        lines.push(`Summary: ${summary.healthy} healthy, ${summary.degraded} degraded, ${summary.unhealthy} unhealthy`);
        return lines.join('\n');
    }
    /**
     * Stop all monitoring and cleanup
     */
    dispose() {
        for (const intervalId of this.intervals.values()) {
            clearInterval(intervalId);
        }
        this.intervals.clear();
        this.endpoints.clear();
        this.removeAllListeners();
    }
}
// Singleton instance
let healthCheckManager = null;
/**
 * Get or create the health check manager
 */
export function getHealthCheckManager(options) {
    if (!healthCheckManager) {
        healthCheckManager = new HealthCheckManager(options);
    }
    return healthCheckManager;
}
/**
 * Reset the health check manager
 */
export function resetHealthCheckManager() {
    if (healthCheckManager) {
        healthCheckManager.dispose();
        healthCheckManager = null;
    }
}
//# sourceMappingURL=health-check.js.map