/**
 * SMTP Server
 * Core implementation for the refactored SMTP server
 */
import * as plugins from '../../../plugins.js';
import { SmtpState } from './interfaces.js';
import { SessionManager } from './session-manager.js';
import { ConnectionManager } from './connection-manager.js';
import { CommandHandler } from './command-handler.js';
import { DataHandler } from './data-handler.js';
import { TlsHandler } from './tls-handler.js';
import { SecurityHandler } from './security-handler.js';
import { SMTP_DEFAULTS } from './constants.js';
import { mergeWithDefaults } from './utils/helpers.js';
import { SmtpLogger } from './utils/logging.js';
import { adaptiveLogger } from './utils/adaptive-logging.js';
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
/**
 * SMTP Server implementation
 * The main server class that coordinates all components
 */
export class SmtpServer {
    /**
     * Email server reference
     */
    emailServer;
    /**
     * Session manager
     */
    sessionManager;
    /**
     * Connection manager
     */
    connectionManager;
    /**
     * Command handler
     */
    commandHandler;
    /**
     * Data handler
     */
    dataHandler;
    /**
     * TLS handler
     */
    tlsHandler;
    /**
     * Security handler
     */
    securityHandler;
    /**
     * SMTP server options
     */
    options;
    /**
     * Net server instance
     */
    server = null;
    /**
     * Secure server instance
     */
    secureServer = null;
    /**
     * Whether the server is running
     */
    running = false;
    /**
     * Server recovery state
     */
    recoveryState = {
        /**
         * Whether recovery is in progress
         */
        recovering: false,
        /**
         * Number of consecutive connection failures
         */
        connectionFailures: 0,
        /**
         * Last recovery attempt timestamp
         */
        lastRecoveryAttempt: 0,
        /**
         * Recovery cooldown in milliseconds
         */
        recoveryCooldown: 5000,
        /**
         * Maximum recovery attempts before giving up
         */
        maxRecoveryAttempts: 3,
        /**
         * Current recovery attempt
         */
        currentRecoveryAttempt: 0
    };
    /**
     * Creates a new SMTP server
     * @param config - Server configuration
     */
    constructor(config) {
        this.emailServer = config.emailServer;
        this.options = mergeWithDefaults(config.options);
        // Create components - all components now receive the SMTP server instance
        this.sessionManager = config.sessionManager || new SessionManager({
            socketTimeout: this.options.socketTimeout,
            connectionTimeout: this.options.connectionTimeout,
            cleanupInterval: this.options.cleanupInterval
        });
        this.securityHandler = config.securityHandler || new SecurityHandler(this);
        this.tlsHandler = config.tlsHandler || new TlsHandler(this);
        this.dataHandler = config.dataHandler || new DataHandler(this);
        this.commandHandler = config.commandHandler || new CommandHandler(this);
        this.connectionManager = config.connectionManager || new ConnectionManager(this);
    }
    /**
     * Start the SMTP server
     * @returns Promise that resolves when server is started
     */
    async listen() {
        if (this.running) {
            throw new Error('SMTP server is already running');
        }
        try {
            // Create the server
            this.server = plugins.net.createServer((socket) => {
                // Check IP reputation before handling connection
                this.securityHandler.checkIpReputation(socket)
                    .then(allowed => {
                    if (allowed) {
                        this.connectionManager.handleNewConnection(socket);
                    }
                    else {
                        // Close connection if IP is not allowed
                        socket.destroy();
                    }
                })
                    .catch(error => {
                    SmtpLogger.error(`IP reputation check error: ${error instanceof Error ? error.message : String(error)}`, {
                        remoteAddress: socket.remoteAddress,
                        error: error instanceof Error ? error : new Error(String(error))
                    });
                    // Allow connection on error (fail open)
                    this.connectionManager.handleNewConnection(socket);
                });
            });
            // Set up error handling with recovery
            this.server.on('error', (err) => {
                SmtpLogger.error(`SMTP server error: ${err.message}`, { error: err });
                // Try to recover from specific errors
                if (this.shouldAttemptRecovery(err)) {
                    this.attemptServerRecovery('standard', err);
                }
            });
            // Start listening
            await new Promise((resolve, reject) => {
                if (!this.server) {
                    reject(new Error('Server not initialized'));
                    return;
                }
                this.server.listen(this.options.port, this.options.host, () => {
                    SmtpLogger.info(`SMTP server listening on ${this.options.host || '0.0.0.0'}:${this.options.port}`);
                    resolve();
                });
                this.server.on('error', reject);
            });
            // Start secure server if configured
            if (this.options.securePort && this.tlsHandler.isTlsEnabled()) {
                try {
                    // Import the secure server creation utility from our new module
                    // This gives us better certificate handling and error resilience
                    const { createSecureTlsServer } = await import('./secure-server.js');
                    // Create secure server with the certificates
                    // This uses a more robust approach to certificate loading and validation
                    this.secureServer = createSecureTlsServer({
                        key: this.options.key,
                        cert: this.options.cert,
                        ca: this.options.ca
                    });
                    SmtpLogger.info(`Created secure TLS server for port ${this.options.securePort}`);
                    if (this.secureServer) {
                        // Use explicit error handling for secure connections
                        this.secureServer.on('tlsClientError', (err, tlsSocket) => {
                            SmtpLogger.error(`TLS client error: ${err.message}`, {
                                error: err,
                                remoteAddress: tlsSocket.remoteAddress,
                                remotePort: tlsSocket.remotePort,
                                stack: err.stack
                            });
                            // No need to destroy, the error event will handle that
                        });
                        // Register the secure connection handler
                        this.secureServer.on('secureConnection', (socket) => {
                            SmtpLogger.info(`New secure connection from ${socket.remoteAddress}:${socket.remotePort}`, {
                                protocol: socket.getProtocol(),
                                cipher: socket.getCipher()?.name
                            });
                            // Check IP reputation before handling connection
                            this.securityHandler.checkIpReputation(socket)
                                .then(allowed => {
                                if (allowed) {
                                    // Pass the connection to the connection manager
                                    this.connectionManager.handleNewSecureConnection(socket);
                                }
                                else {
                                    // Close connection if IP is not allowed
                                    socket.destroy();
                                }
                            })
                                .catch(error => {
                                SmtpLogger.error(`IP reputation check error: ${error instanceof Error ? error.message : String(error)}`, {
                                    remoteAddress: socket.remoteAddress,
                                    error: error instanceof Error ? error : new Error(String(error)),
                                    stack: error instanceof Error ? error.stack : 'No stack trace available'
                                });
                                // Allow connection on error (fail open)
                                this.connectionManager.handleNewSecureConnection(socket);
                            });
                        });
                        // Global error handler for the secure server with recovery
                        this.secureServer.on('error', (err) => {
                            SmtpLogger.error(`SMTP secure server error: ${err.message}`, {
                                error: err,
                                stack: err.stack
                            });
                            // Try to recover from specific errors
                            if (this.shouldAttemptRecovery(err)) {
                                this.attemptServerRecovery('secure', err);
                            }
                        });
                        // Start listening on secure port
                        await new Promise((resolve, reject) => {
                            if (!this.secureServer) {
                                reject(new Error('Secure server not initialized'));
                                return;
                            }
                            this.secureServer.listen(this.options.securePort, this.options.host, () => {
                                SmtpLogger.info(`SMTP secure server listening on ${this.options.host || '0.0.0.0'}:${this.options.securePort}`);
                                resolve();
                            });
                            // Only use error event for startup issues
                            this.secureServer.once('error', reject);
                        });
                    }
                    else {
                        SmtpLogger.warn('Failed to create secure server, TLS may not be properly configured');
                    }
                }
                catch (error) {
                    SmtpLogger.error(`Error setting up secure server: ${error instanceof Error ? error.message : String(error)}`, {
                        error: error instanceof Error ? error : new Error(String(error)),
                        stack: error instanceof Error ? error.stack : 'No stack trace available'
                    });
                }
            }
            this.running = true;
        }
        catch (error) {
            SmtpLogger.error(`Failed to start SMTP server: ${error instanceof Error ? error.message : String(error)}`, {
                error: error instanceof Error ? error : new Error(String(error))
            });
            // Clean up on error
            this.close();
            throw error;
        }
    }
    /**
     * Stop the SMTP server
     * @returns Promise that resolves when server is stopped
     */
    async close() {
        if (!this.running) {
            return;
        }
        SmtpLogger.info('Stopping SMTP server');
        try {
            // Close all active connections
            this.connectionManager.closeAllConnections();
            // Clear all sessions
            this.sessionManager.clearAllSessions();
            // Clean up adaptive logger to prevent hanging timers
            adaptiveLogger.destroy();
            // Destroy all components to clean up their resources
            await this.destroy();
            // Close servers
            const closePromises = [];
            if (this.server) {
                closePromises.push(new Promise((resolve, reject) => {
                    if (!this.server) {
                        resolve();
                        return;
                    }
                    this.server.close((err) => {
                        if (err) {
                            reject(err);
                        }
                        else {
                            resolve();
                        }
                    });
                }));
            }
            if (this.secureServer) {
                closePromises.push(new Promise((resolve, reject) => {
                    if (!this.secureServer) {
                        resolve();
                        return;
                    }
                    this.secureServer.close((err) => {
                        if (err) {
                            reject(err);
                        }
                        else {
                            resolve();
                        }
                    });
                }));
            }
            // Add timeout to prevent hanging on close
            await Promise.race([
                Promise.all(closePromises),
                new Promise((resolve) => {
                    setTimeout(() => {
                        SmtpLogger.warn('Server close timed out after 3 seconds, forcing shutdown');
                        resolve();
                    }, 3000);
                })
            ]);
            this.server = null;
            this.secureServer = null;
            this.running = false;
            SmtpLogger.info('SMTP server stopped');
        }
        catch (error) {
            SmtpLogger.error(`Error stopping SMTP server: ${error instanceof Error ? error.message : String(error)}`, {
                error: error instanceof Error ? error : new Error(String(error))
            });
            throw error;
        }
    }
    /**
     * Get the session manager
     * @returns Session manager instance
     */
    getSessionManager() {
        return this.sessionManager;
    }
    /**
     * Get the connection manager
     * @returns Connection manager instance
     */
    getConnectionManager() {
        return this.connectionManager;
    }
    /**
     * Get the command handler
     * @returns Command handler instance
     */
    getCommandHandler() {
        return this.commandHandler;
    }
    /**
     * Get the data handler
     * @returns Data handler instance
     */
    getDataHandler() {
        return this.dataHandler;
    }
    /**
     * Get the TLS handler
     * @returns TLS handler instance
     */
    getTlsHandler() {
        return this.tlsHandler;
    }
    /**
     * Get the security handler
     * @returns Security handler instance
     */
    getSecurityHandler() {
        return this.securityHandler;
    }
    /**
     * Get the server options
     * @returns SMTP server options
     */
    getOptions() {
        return this.options;
    }
    /**
     * Get the email server reference
     * @returns Email server instance
     */
    getEmailServer() {
        return this.emailServer;
    }
    /**
     * Check if the server is running
     * @returns Whether the server is running
     */
    isRunning() {
        return this.running;
    }
    /**
     * Check if we should attempt to recover from an error
     * @param error - The error that occurred
     * @returns Whether recovery should be attempted
     */
    shouldAttemptRecovery(error) {
        // Skip recovery if we're already in recovery mode
        if (this.recoveryState.recovering) {
            return false;
        }
        // Check if we've reached the maximum number of recovery attempts
        if (this.recoveryState.currentRecoveryAttempt >= this.recoveryState.maxRecoveryAttempts) {
            SmtpLogger.warn('Maximum recovery attempts reached, not attempting further recovery');
            return false;
        }
        // Check if enough time has passed since the last recovery attempt
        const now = Date.now();
        if (now - this.recoveryState.lastRecoveryAttempt < this.recoveryState.recoveryCooldown) {
            SmtpLogger.warn('Recovery cooldown period not elapsed, skipping recovery attempt');
            return false;
        }
        // Recoverable errors include:
        // - EADDRINUSE: Address already in use (port conflict)
        // - ECONNRESET: Connection reset by peer
        // - EPIPE: Broken pipe
        // - ETIMEDOUT: Connection timed out
        const recoverableErrors = [
            'EADDRINUSE',
            'ECONNRESET',
            'EPIPE',
            'ETIMEDOUT',
            'ECONNABORTED',
            'EPROTO',
            'EMFILE' // Too many open files
        ];
        // Check if this is a recoverable error
        const errorCode = error.code;
        return recoverableErrors.includes(errorCode);
    }
    /**
     * Attempt to recover the server after a critical error
     * @param serverType - The type of server to recover ('standard' or 'secure')
     * @param error - The error that triggered recovery
     */
    async attemptServerRecovery(serverType, error) {
        // Set recovery flag to prevent multiple simultaneous recovery attempts
        if (this.recoveryState.recovering) {
            SmtpLogger.warn('Recovery already in progress, skipping new recovery attempt');
            return;
        }
        this.recoveryState.recovering = true;
        this.recoveryState.lastRecoveryAttempt = Date.now();
        this.recoveryState.currentRecoveryAttempt++;
        SmtpLogger.info(`Attempting server recovery for ${serverType} server after error: ${error.message}`, {
            attempt: this.recoveryState.currentRecoveryAttempt,
            maxAttempts: this.recoveryState.maxRecoveryAttempts,
            errorCode: error.code
        });
        try {
            // Determine which server to restart
            const isStandardServer = serverType === 'standard';
            // Close the affected server
            if (isStandardServer && this.server) {
                await new Promise((resolve) => {
                    if (!this.server) {
                        resolve();
                        return;
                    }
                    // First try a clean shutdown
                    this.server.close((err) => {
                        if (err) {
                            SmtpLogger.warn(`Error during server close in recovery: ${err.message}`);
                        }
                        resolve();
                    });
                    // Set a timeout to force close
                    setTimeout(() => {
                        resolve();
                    }, 3000);
                });
                this.server = null;
            }
            else if (!isStandardServer && this.secureServer) {
                await new Promise((resolve) => {
                    if (!this.secureServer) {
                        resolve();
                        return;
                    }
                    // First try a clean shutdown
                    this.secureServer.close((err) => {
                        if (err) {
                            SmtpLogger.warn(`Error during secure server close in recovery: ${err.message}`);
                        }
                        resolve();
                    });
                    // Set a timeout to force close
                    setTimeout(() => {
                        resolve();
                    }, 3000);
                });
                this.secureServer = null;
            }
            // Short delay before restarting
            await new Promise((resolve) => setTimeout(resolve, 1000));
            // Clean up any lingering connections
            this.connectionManager.closeAllConnections();
            this.sessionManager.clearAllSessions();
            // Restart the affected server
            if (isStandardServer) {
                // Create and start the standard server
                this.server = plugins.net.createServer((socket) => {
                    // Check IP reputation before handling connection
                    this.securityHandler.checkIpReputation(socket)
                        .then(allowed => {
                        if (allowed) {
                            this.connectionManager.handleNewConnection(socket);
                        }
                        else {
                            // Close connection if IP is not allowed
                            socket.destroy();
                        }
                    })
                        .catch(error => {
                        SmtpLogger.error(`IP reputation check error: ${error instanceof Error ? error.message : String(error)}`, {
                            remoteAddress: socket.remoteAddress,
                            error: error instanceof Error ? error : new Error(String(error))
                        });
                        // Allow connection on error (fail open)
                        this.connectionManager.handleNewConnection(socket);
                    });
                });
                // Set up error handling with recovery
                this.server.on('error', (err) => {
                    SmtpLogger.error(`SMTP server error after recovery: ${err.message}`, { error: err });
                    // Try to recover again if needed
                    if (this.shouldAttemptRecovery(err)) {
                        this.attemptServerRecovery('standard', err);
                    }
                });
                // Start listening again
                await new Promise((resolve, reject) => {
                    if (!this.server) {
                        reject(new Error('Server not initialized during recovery'));
                        return;
                    }
                    this.server.listen(this.options.port, this.options.host, () => {
                        SmtpLogger.info(`SMTP server recovered and listening on ${this.options.host || '0.0.0.0'}:${this.options.port}`);
                        resolve();
                    });
                    // Only use error event for startup issues during recovery
                    this.server.once('error', (err) => {
                        SmtpLogger.error(`Failed to restart server during recovery: ${err.message}`);
                        reject(err);
                    });
                });
            }
            else if (this.options.securePort && this.tlsHandler.isTlsEnabled()) {
                // Try to recreate the secure server
                try {
                    // Import the secure server creation utility
                    const { createSecureTlsServer } = await import('./secure-server.js');
                    // Create secure server with the certificates
                    this.secureServer = createSecureTlsServer({
                        key: this.options.key,
                        cert: this.options.cert,
                        ca: this.options.ca
                    });
                    if (this.secureServer) {
                        SmtpLogger.info(`Created secure TLS server for port ${this.options.securePort} during recovery`);
                        // Use explicit error handling for secure connections
                        this.secureServer.on('tlsClientError', (err, tlsSocket) => {
                            SmtpLogger.error(`TLS client error after recovery: ${err.message}`, {
                                error: err,
                                remoteAddress: tlsSocket.remoteAddress,
                                remotePort: tlsSocket.remotePort,
                                stack: err.stack
                            });
                        });
                        // Register the secure connection handler
                        this.secureServer.on('secureConnection', (socket) => {
                            // Check IP reputation before handling connection
                            this.securityHandler.checkIpReputation(socket)
                                .then(allowed => {
                                if (allowed) {
                                    // Pass the connection to the connection manager
                                    this.connectionManager.handleNewSecureConnection(socket);
                                }
                                else {
                                    // Close connection if IP is not allowed
                                    socket.destroy();
                                }
                            })
                                .catch(error => {
                                SmtpLogger.error(`IP reputation check error after recovery: ${error instanceof Error ? error.message : String(error)}`, {
                                    remoteAddress: socket.remoteAddress,
                                    error: error instanceof Error ? error : new Error(String(error))
                                });
                                // Allow connection on error (fail open)
                                this.connectionManager.handleNewSecureConnection(socket);
                            });
                        });
                        // Global error handler for the secure server with recovery
                        this.secureServer.on('error', (err) => {
                            SmtpLogger.error(`SMTP secure server error after recovery: ${err.message}`, {
                                error: err,
                                stack: err.stack
                            });
                            // Try to recover again if needed
                            if (this.shouldAttemptRecovery(err)) {
                                this.attemptServerRecovery('secure', err);
                            }
                        });
                        // Start listening on secure port again
                        await new Promise((resolve, reject) => {
                            if (!this.secureServer) {
                                reject(new Error('Secure server not initialized during recovery'));
                                return;
                            }
                            this.secureServer.listen(this.options.securePort, this.options.host, () => {
                                SmtpLogger.info(`SMTP secure server recovered and listening on ${this.options.host || '0.0.0.0'}:${this.options.securePort}`);
                                resolve();
                            });
                            // Only use error event for startup issues during recovery
                            this.secureServer.once('error', (err) => {
                                SmtpLogger.error(`Failed to restart secure server during recovery: ${err.message}`);
                                reject(err);
                            });
                        });
                    }
                    else {
                        SmtpLogger.warn('Failed to create secure server during recovery');
                    }
                }
                catch (error) {
                    SmtpLogger.error(`Error setting up secure server during recovery: ${error instanceof Error ? error.message : String(error)}`);
                }
            }
            // Recovery successful
            SmtpLogger.info('Server recovery completed successfully');
        }
        catch (recoveryError) {
            SmtpLogger.error(`Server recovery failed: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`, {
                error: recoveryError instanceof Error ? recoveryError : new Error(String(recoveryError)),
                attempt: this.recoveryState.currentRecoveryAttempt,
                maxAttempts: this.recoveryState.maxRecoveryAttempts
            });
        }
        finally {
            // Reset recovery flag
            this.recoveryState.recovering = false;
        }
    }
    /**
     * Clean up all component resources
     */
    async destroy() {
        SmtpLogger.info('Destroying SMTP server components');
        // Destroy all components in parallel
        const destroyPromises = [];
        if (this.sessionManager && typeof this.sessionManager.destroy === 'function') {
            destroyPromises.push(Promise.resolve(this.sessionManager.destroy()));
        }
        if (this.connectionManager && typeof this.connectionManager.destroy === 'function') {
            destroyPromises.push(Promise.resolve(this.connectionManager.destroy()));
        }
        if (this.commandHandler && typeof this.commandHandler.destroy === 'function') {
            destroyPromises.push(Promise.resolve(this.commandHandler.destroy()));
        }
        if (this.dataHandler && typeof this.dataHandler.destroy === 'function') {
            destroyPromises.push(Promise.resolve(this.dataHandler.destroy()));
        }
        if (this.tlsHandler && typeof this.tlsHandler.destroy === 'function') {
            destroyPromises.push(Promise.resolve(this.tlsHandler.destroy()));
        }
        if (this.securityHandler && typeof this.securityHandler.destroy === 'function') {
            destroyPromises.push(Promise.resolve(this.securityHandler.destroy()));
        }
        await Promise.all(destroyPromises);
        // Destroy the adaptive logger singleton to clean up its timer
        const { adaptiveLogger } = await import('./utils/adaptive-logging.js');
        if (adaptiveLogger && typeof adaptiveLogger.destroy === 'function') {
            adaptiveLogger.destroy();
        }
        // Clear recovery state
        this.recoveryState = {
            recovering: false,
            connectionFailures: 0,
            lastRecoveryAttempt: 0,
            recoveryCooldown: 5000,
            maxRecoveryAttempts: 3,
            currentRecoveryAttempt: 0
        };
        SmtpLogger.info('All SMTP server components destroyed');
    }
}
//# sourceMappingURL=data:application/json;base64, 