"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManagementServer = exports.RuntimeConfigManager = void 0;
/**
 * Runtime configuration and health check system
 * Provides dynamic configuration updates and HTTP management endpoints
 */
const http = __importStar(require("http"));
const url = __importStar(require("url"));
const logger_1 = require("./logger");
const prometheus_metrics_1 = require("./prometheus-metrics");
/**
 * Dynamic configuration manager
 */
class RuntimeConfigManager {
    constructor(initialConfig = {}) {
        this.configChangeCallbacks = new Map();
        this.config = {
            // Default configuration
            maxCacheSize: 1000,
            maxCacheMemory: 512 * 1024 * 1024, // 512MB
            circularDependencyStrategy: 'warn',
            enableTracing: true,
            logLevel: 'info',
            enableMetrics: true,
            circuitBreakerEnabled: true,
            failureThreshold: 5,
            recoveryTimeout: 30000,
            healthCheckInterval: 30000,
            enableHealthEndpoint: true,
            enableMetricsEndpoint: true,
            managementPort: 0, // 0 = disabled
            ...initialConfig,
        };
    }
    /**
     * Get current configuration
     */
    getConfig() {
        return { ...this.config };
    }
    /**
     * Update a configuration value
     */
    updateConfig(key, value) {
        const oldValue = this.config[key];
        this.config[key] = value;
        // Trigger callbacks
        const callback = this.configChangeCallbacks.get(key);
        if (callback) {
            callback(value, oldValue);
        }
        logger_1.LoggerFactory.getLogger('runtime-config').info('Configuration updated', {
            key,
            oldValue,
            newValue: value,
        });
    }
    /**
     * Update multiple configuration values
     */
    updateMultiple(updates) {
        for (const [key, value] of Object.entries(updates)) {
            this.updateConfig(key, value);
        }
    }
    /**
     * Register callback for configuration changes
     */
    onConfigChange(key, callback) {
        this.configChangeCallbacks.set(key, callback);
    }
    /**
     * Validate configuration values
     */
    validateConfig() {
        const errors = [];
        if (this.config.maxCacheSize <= 0) {
            errors.push('maxCacheSize must be greater than 0');
        }
        if (this.config.maxCacheMemory <= 0) {
            errors.push('maxCacheMemory must be greater than 0');
        }
        if (this.config.failureThreshold <= 0) {
            errors.push('failureThreshold must be greater than 0');
        }
        if (this.config.recoveryTimeout <= 0) {
            errors.push('recoveryTimeout must be greater than 0');
        }
        if (this.config.healthCheckInterval <= 0) {
            errors.push('healthCheckInterval must be greater than 0');
        }
        return {
            valid: errors.length === 0,
            errors,
        };
    }
    /**
     * Reset to default configuration
     */
    reset() {
        const defaults = new RuntimeConfigManager().getConfig();
        for (const [key, value] of Object.entries(defaults)) {
            this.updateConfig(key, value);
        }
    }
}
exports.RuntimeConfigManager = RuntimeConfigManager;
/**
 * HTTP management server for health checks and metrics
 */
class ManagementServer {
    constructor(metrics, circuitBreakers, configManager) {
        this.logger = logger_1.LoggerFactory.getLogger('management-server');
        this.activeConnections = new Set();
        this.isShuttingDown = false;
        this.SHUTDOWN_TIMEOUT_MS = 30000; // 30 seconds
        this.prometheusExporter = new prometheus_metrics_1.PrometheusExporter();
        this.metrics = metrics;
        this.circuitBreakers = circuitBreakers;
        this.configManager = configManager;
    }
    /**
     * Start the management server
     */
    start(port) {
        return new Promise((resolve, reject) => {
            this.server = http.createServer(this.handleRequest.bind(this));
            // Track socket connections
            this.server.on('connection', socket => {
                this.activeConnections.add(socket);
                socket.on('close', () => {
                    this.activeConnections.delete(socket);
                });
            });
            this.server.on('error', error => {
                this.logger.error('Management server error', error);
                reject(error);
            });
            this.server.listen(port, () => {
                const address = this.server.address();
                const actualPort = typeof address === 'object' && address ? address.port : port;
                this.logger.info('Management server started', { port: actualPort });
                resolve(actualPort);
            });
        });
    }
    /**
     * Stop the management server with graceful shutdown
     */
    async stop() {
        if (!this.server || this.isShuttingDown) {
            return;
        }
        this.logger.info('Initiating graceful shutdown', {
            activeConnections: this.activeConnections.size,
        });
        // Mark as shutting down to reject new requests
        this.isShuttingDown = true;
        // Stop accepting new connections
        await new Promise((resolve, reject) => {
            this.server.close(err => {
                if (err) {
                    this.logger.warn('Error closing server', { error: err.message });
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
        // Wait for active connections to drain with timeout
        const startTime = Date.now();
        const drainPromise = this.drainConnections();
        const timeoutPromise = new Promise(resolve => {
            setTimeout(() => {
                if (this.activeConnections.size > 0) {
                    this.logger.warn('Shutdown timeout reached, forcing connection closure', {
                        remainingConnections: this.activeConnections.size,
                        elapsed: Date.now() - startTime,
                    });
                    this.forceCloseConnections();
                }
                resolve();
            }, this.SHUTDOWN_TIMEOUT_MS);
        });
        try {
            await Promise.race([drainPromise, timeoutPromise]);
            this.logger.info('Management server stopped gracefully', {
                elapsed: Date.now() - startTime,
            });
        }
        catch (error) {
            this.logger.error('Error during shutdown', error);
            // Force close any remaining connections
            this.forceCloseConnections();
            throw error;
        }
    }
    /**
     * Wait for all active connections to finish
     */
    async drainConnections() {
        const checkInterval = 100; // Check every 100ms
        while (this.activeConnections.size > 0) {
            await new Promise(resolve => setTimeout(resolve, checkInterval));
        }
    }
    /**
     * Force close all active connections
     */
    forceCloseConnections() {
        for (const connection of this.activeConnections) {
            try {
                connection.destroy();
            }
            catch (error) {
                this.logger.warn('Error destroying connection', {
                    error: error.message,
                });
            }
        }
        this.activeConnections.clear();
    }
    async handleRequest(req, res) {
        // Reject new requests during shutdown
        if (this.isShuttingDown) {
            this.sendErrorResponse(res, 503, 'Server is shutting down');
            return;
        }
        const parsedUrl = url.parse(req.url || '', true);
        const pathname = parsedUrl.pathname || '';
        const method = req.method || 'GET';
        this.logger.debug('Management request', { method, pathname });
        try {
            // CORS headers
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
            res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
            if (method === 'OPTIONS') {
                res.writeHead(200);
                res.end();
                return;
            }
            switch (pathname) {
                case '/health':
                    await this.handleHealthCheck(req, res);
                    break;
                case '/health/ready':
                    await this.handleReadinessCheck(req, res);
                    break;
                case '/metrics':
                    await this.handleMetrics(req, res);
                    break;
                case '/metrics/prometheus':
                    await this.handlePrometheusMetrics(req, res);
                    break;
                case '/config':
                    await this.handleConfig(req, res);
                    break;
                case '/circuit-breakers':
                    await this.handleCircuitBreakers(req, res);
                    break;
                case '/admin/reset':
                    await this.handleReset(req, res);
                    break;
                default:
                    await this.handleNotFound(req, res);
            }
        }
        catch (error) {
            this.logger.error('Request handling error', error);
            this.sendErrorResponse(res, 500, 'Internal Server Error');
        }
    }
    async handleHealthCheck(req, res) {
        const config = this.configManager.getConfig();
        const health = await this.metrics.performHealthChecks(0, config.maxCacheMemory);
        const response = {
            status: health.status,
            timestamp: new Date().toISOString(),
            uptime: health.uptime,
            version: health.version,
            checks: health.checks.map(check => ({
                name: check.name,
                status: check.status,
                message: check.message,
                duration: check.duration,
            })),
            details: {
                circuitBreakers: this.circuitBreakers.getAllStatus(),
            },
        };
        let statusCode;
        if (health.status === 'healthy' || health.status === 'degraded') {
            statusCode = 200;
        }
        else {
            statusCode = 503;
        }
        this.sendJsonResponse(res, statusCode, response);
    }
    async handleReadinessCheck(req, res) {
        // Readiness check - simplified version of health check
        const overallHealth = this.circuitBreakers.getOverallHealth();
        const ready = overallHealth.healthy;
        const response = {
            ready,
            timestamp: new Date().toISOString(),
            circuitBreakers: {
                total: overallHealth.totalBreakers,
                healthy: overallHealth.healthyBreakers,
                open: overallHealth.openBreakers,
            },
        };
        this.sendJsonResponse(res, ready ? 200 : 503, response);
    }
    async handleMetrics(req, res) {
        const config = this.configManager.getConfig();
        const stats = this.metrics.getStats(0, 0, config.maxCacheMemory);
        this.sendJsonResponse(res, 200, stats);
    }
    async handlePrometheusMetrics(req, res) {
        const config = this.configManager.getConfig();
        const stats = this.metrics.getStats(0, 0, config.maxCacheMemory);
        const prometheusText = this.prometheusExporter.exportMetrics(stats, this.circuitBreakers);
        res.writeHead(200, {
            'Content-Type': 'text/plain; version=0.0.4',
            'Content-Length': Buffer.byteLength(prometheusText),
        });
        res.end(prometheusText);
    }
    async handleConfig(req, res) {
        const method = req.method || 'GET';
        if (method === 'GET') {
            const config = this.configManager.getConfig();
            this.sendJsonResponse(res, 200, config);
        }
        else if (method === 'PUT' || method === 'POST') {
            const body = await this.readRequestBody(req);
            try {
                const updates = JSON.parse(body);
                this.configManager.updateMultiple(updates);
                const validation = this.configManager.validateConfig();
                if (!validation.valid) {
                    this.sendJsonResponse(res, 400, {
                        error: 'Invalid configuration',
                        details: validation.errors,
                    });
                    return;
                }
                this.sendJsonResponse(res, 200, {
                    message: 'Configuration updated',
                    config: this.configManager.getConfig(),
                });
            }
            catch (error) {
                this.sendJsonResponse(res, 400, {
                    error: 'Invalid JSON',
                    message: error.message,
                });
            }
        }
        else {
            this.sendErrorResponse(res, 405, 'Method Not Allowed');
        }
    }
    async handleCircuitBreakers(req, res) {
        const method = req.method || 'GET';
        if (method === 'GET') {
            const status = this.circuitBreakers.getAllStatus();
            const overall = this.circuitBreakers.getOverallHealth();
            this.sendJsonResponse(res, 200, {
                overall,
                breakers: status,
            });
        }
        else if (method === 'POST') {
            const body = await this.readRequestBody(req);
            try {
                const action = JSON.parse(body);
                if (action.type === 'reset') {
                    if (action.moduleId) {
                        const breaker = this.circuitBreakers.getBreaker(action.moduleId);
                        breaker.reset();
                    }
                    else {
                        this.circuitBreakers.resetAll();
                    }
                    this.sendJsonResponse(res, 200, { message: 'Circuit breakers reset' });
                }
                else {
                    this.sendJsonResponse(res, 400, { error: 'Unknown action type' });
                }
            }
            catch (error) {
                this.sendJsonResponse(res, 400, {
                    error: 'Invalid JSON',
                    message: error.message,
                });
            }
        }
        else {
            this.sendErrorResponse(res, 405, 'Method Not Allowed');
        }
    }
    async handleReset(req, res) {
        if (req.method !== 'POST') {
            this.sendErrorResponse(res, 405, 'Method Not Allowed');
            return;
        }
        // Reset all systems
        this.metrics.reset();
        this.circuitBreakers.resetAll();
        this.sendJsonResponse(res, 200, {
            message: 'All systems reset',
            timestamp: new Date().toISOString(),
        });
    }
    async handleNotFound(req, res) {
        this.sendErrorResponse(res, 404, 'Not Found');
    }
    sendJsonResponse(res, statusCode, data) {
        res.writeHead(statusCode, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(data, null, 2));
    }
    sendErrorResponse(res, statusCode, message) {
        res.writeHead(statusCode, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: message, timestamp: new Date().toISOString() }));
    }
    readRequestBody(req) {
        return new Promise((resolve, reject) => {
            let body = '';
            req.on('data', chunk => {
                body += chunk.toString();
            });
            req.on('end', () => {
                resolve(body);
            });
            req.on('error', reject);
        });
    }
}
exports.ManagementServer = ManagementServer;
//# sourceMappingURL=runtime-config.js.map