/**
 * Robust Network Manager
 * Comprehensive network management with auto-recovery, health monitoring, and graceful degradation
 */

const NetworkDiagnostics = require('./networkDiagnostics');
const NetworkStatusMonitor = require('./networkStatusMonitor');
const EventEmitter = require('events');

class RobustNetworkManager extends EventEmitter {
	constructor(config, options = {}) {
		super();
		this.config = config;
		this.options = {
			// Network test options
			testTimeout: options.testTimeout || 10000,
			testRetries: options.testRetries || 2,
			testRetryDelay: options.testRetryDelay || 2000,
			
			// Monitoring options
			monitoringEnabled: options.monitoringEnabled !== false,
			checkInterval: options.checkInterval || 30000,
			successThreshold: options.successThreshold || 2,
			failureThreshold: options.failureThreshold || 3,
			
			// Auto-recovery options
			autoRecoveryEnabled: options.autoRecoveryEnabled !== false,
			recoveryAttempts: options.recoveryAttempts || 5,
			recoveryDelay: options.recoveryDelay || 5000,
			
			...options
		};

		this.monitor = null;
		this.recoveryAttempts = 0;
		this.lastRecoveryAttempt = null;
		this.isRecovering = false;
	}

	/**
	 * Initialize network manager
	 */
	async initialize() {
		try {
			// Initial network test with retry strategies
			console.log('[RobustNetworkManager] Performing initial network connectivity test...');
			const initialTest = await NetworkDiagnostics.testTCPConnectionWithStrategies(
				this.config.host,
				this.config.port
			);

			if (!initialTest.success) {
				console.warn('[RobustNetworkManager] Initial network test failed, trying with retry mechanism...');
				const retryTest = await NetworkDiagnostics.testTCPConnection(
					this.config.host,
					this.config.port,
					this.options.testTimeout,
					{
						maxRetries: this.options.testRetries,
						retryDelay: this.options.testRetryDelay
					}
				);

				if (!retryTest.success) {
					const error = new Error(`Initial network connectivity test failed: ${retryTest.error || 'Unknown error'}`);
					error.networkDiagnostic = NetworkDiagnostics.getNetworkErrorInfo({
						code: retryTest.error,
						message: retryTest.message
					});
					throw error;
				}
			}

			console.log('[RobustNetworkManager] Initial network test passed');

			// Start monitoring if enabled
			if (this.options.monitoringEnabled) {
				this.startMonitoring();
			}

			return true;
		} catch (error) {
			console.error('[RobustNetworkManager] Initialization failed:', error.message);
			throw error;
		}
	}

	/**
	 * Start network monitoring
	 */
	startMonitoring() {
		if (this.monitor) {
			console.log('[RobustNetworkManager] Monitoring already started');
			return;
		}

		this.monitor = new NetworkStatusMonitor(this.config, {
			checkInterval: this.options.checkInterval,
			timeout: this.options.testTimeout,
			successThreshold: this.options.successThreshold,
			failureThreshold: this.options.failureThreshold,
			enabled: true
		});

		// Setup event handlers
		this.monitor.on('online', (data) => {
			console.log('[RobustNetworkManager] Network is ONLINE');
			this.recoveryAttempts = 0; // Reset recovery attempts on success
			this.emit('online', data);
			
			// Trigger auto-recovery if enabled
			if (this.options.autoRecoveryEnabled && data.wasOffline) {
				this.attemptAutoRecovery();
			}
		});

		this.monitor.on('offline', (data) => {
			console.error('[RobustNetworkManager] Network is OFFLINE');
			this.emit('offline', data);
		});

		this.monitor.start();
		console.log('[RobustNetworkManager] Network monitoring started');
	}

	/**
	 * Stop network monitoring
	 */
	stopMonitoring() {
		if (this.monitor) {
			this.monitor.stop();
			this.monitor = null;
			console.log('[RobustNetworkManager] Network monitoring stopped');
		}
	}

	/**
	 * Attempt auto-recovery when network comes back online
	 */
	async attemptAutoRecovery() {
		if (this.isRecovering) {
			console.log('[RobustNetworkManager] Auto-recovery already in progress');
			return;
		}

		if (this.recoveryAttempts >= this.options.recoveryAttempts) {
			console.warn(`[RobustNetworkManager] Max recovery attempts (${this.options.recoveryAttempts}) reached`);
			return;
		}

		this.isRecovering = true;
		this.recoveryAttempts++;
		this.lastRecoveryAttempt = new Date().toISOString();

		console.log(`[RobustNetworkManager] Attempting auto-recovery (attempt ${this.recoveryAttempts}/${this.options.recoveryAttempts})...`);

		try {
			// Wait a bit before attempting recovery
			await new Promise(resolve => setTimeout(resolve, this.options.recoveryDelay));

			// Trigger database reconnection
			const dbSAPPS = require("../apps/sapps/helpers/db");
			if (dbSAPPS.dbConnectionManager) {
				// Check if connection manager is closed before attempting recovery
				if (dbSAPPS.dbConnectionManager.isConnectionManagerClosed && dbSAPPS.dbConnectionManager.isConnectionManagerClosed()) {
					console.warn('[RobustNetworkManager] Cannot recover: Connection manager is closed');
					this.isRecovering = false;
					return;
				}

				if (dbSAPPS.dbConnectionManager.reconnect) {
					await dbSAPPS.dbConnectionManager.reconnect();
					console.log('[RobustNetworkManager] Auto-recovery successful');
					this.emit('recovery-success', {
						attempt: this.recoveryAttempts,
						timestamp: this.lastRecoveryAttempt
					});
				} else {
					console.warn('[RobustNetworkManager] Database connection manager reconnect method not available');
				}
			} else {
				console.warn('[RobustNetworkManager] Database connection manager not available for recovery');
			}
		} catch (error) {
			console.error(`[RobustNetworkManager] Auto-recovery failed (attempt ${this.recoveryAttempts}):`, error.message);
			this.emit('recovery-failure', {
				attempt: this.recoveryAttempts,
				error: error.message,
				timestamp: this.lastRecoveryAttempt
			});

			// Retry if not at max attempts
			if (this.recoveryAttempts < this.options.recoveryAttempts) {
				setTimeout(() => {
					this.isRecovering = false;
					this.attemptAutoRecovery();
				}, this.options.recoveryDelay * 2); // Exponential backoff
			} else {
				this.isRecovering = false;
			}
		} finally {
			if (this.recoveryAttempts < this.options.recoveryAttempts) {
				this.isRecovering = false;
			}
		}
	}

	/**
	 * Get comprehensive network status
	 */
	getStatus() {
		const status = {
			monitoring: {
				enabled: this.options.monitoringEnabled,
				active: !!this.monitor && this.monitor.isMonitoring
			},
			autoRecovery: {
				enabled: this.options.autoRecoveryEnabled,
				attempts: this.recoveryAttempts,
				maxAttempts: this.options.recoveryAttempts,
				lastAttempt: this.lastRecoveryAttempt,
				isRecovering: this.isRecovering
			}
		};

		if (this.monitor) {
			status.monitor = this.monitor.getStatus();
			status.diagnostic = this.monitor.getDiagnostic();
		}

		return status;
	}

	/**
	 * Force network check
	 */
	async forceCheck() {
		if (this.monitor) {
			return await this.monitor.forceCheck();
		}

		// Fallback to direct test
		return await NetworkDiagnostics.testTCPConnection(
			this.config.host,
			this.config.port,
			this.options.testTimeout
		);
	}

	/**
	 * Get network diagnostic information
	 */
	getDiagnostic() {
		if (this.monitor) {
			return this.monitor.getDiagnostic();
		}
		return null;
	}

	/**
	 * Check if network is online
	 */
	isOnline() {
		return this.monitor ? this.monitor.isOnline() : null;
	}

	/**
	 * Check if network is offline
	 */
	isOffline() {
		return this.monitor ? this.monitor.isOffline() : null;
	}
}

module.exports = RobustNetworkManager;
