/**
 * Enhanced Error Handler
 * Comprehensive error handling untuk uncaught exceptions dan unhandled rejections
 */

const { getCrashRecovery } = require('./crashRecovery');

class EnhancedErrorHandler {
	constructor() {
		this.crashRecovery = getCrashRecovery();
		this.isShuttingDown = false;
		this.setupHandlers();
	}
	
	setupHandlers() {
		// Handle uncaught exceptions
		process.on('uncaughtException', (error) => {
			this.handleUncaughtException(error);
		});
		
		// Handle unhandled promise rejections
		process.on('unhandledRejection', (reason, promise) => {
			this.handleUnhandledRejection(reason, promise);
		});
		
		// Handle warnings
		process.on('warning', (warning) => {
			this.handleWarning(warning);
		});
		
		// Handle exit
		process.on('exit', (code) => {
			this.handleExit(code);
		});
	}
	
	handleUncaughtException(error) {
		console.error('========================================');
		console.error('UNCAUGHT EXCEPTION DETECTED');
		console.error('========================================');
		console.error('Error:', error);
		console.error('Stack:', error.stack);
		console.error('PID:', process.pid);
		console.error('Uptime:', process.uptime(), 'seconds');
		console.error('Memory:', process.memoryUsage());
		console.error('========================================');
		
		// Log crash
		this.crashRecovery.logCrash(error, {
			type: 'uncaughtException',
			pid: process.pid,
			uptime: process.uptime(),
			memory: process.memoryUsage(),
		});
		
		// Determine if we should restart
		if (!this.isShuttingDown && !this.isFatalError(error)) {
			// Attempt graceful shutdown first
			this.gracefulShutdown(() => {
				this.crashRecovery.restart('Uncaught Exception');
			});
		} else {
			// Fatal error or already shutting down
			console.error('Fatal error detected or shutdown in progress. Exiting...');
			process.exit(1);
		}
	}
	
	handleUnhandledRejection(reason, promise) {
		console.error('========================================');
		console.error('UNHANDLED PROMISE REJECTION DETECTED');
		console.error('========================================');
		console.error('Reason:', reason);
		console.error('Promise:', promise);
		console.error('PID:', process.pid);
		console.error('Uptime:', process.uptime(), 'seconds');
		console.error('========================================');
		
		// Convert reason to Error if it's not already
		const error = reason instanceof Error 
			? reason 
			: new Error(String(reason));
		
		// Log crash
		this.crashRecovery.logCrash(error, {
			type: 'unhandledRejection',
			pid: process.pid,
			uptime: process.uptime(),
			memory: process.memoryUsage(),
		});
		
		// For unhandled rejections, we might want to continue running
		// but log the error. However, if it's critical, we should restart.
		if (!this.isShuttingDown && this.isCriticalRejection(reason)) {
			this.gracefulShutdown(() => {
				this.crashRecovery.restart('Unhandled Promise Rejection');
			});
		}
		// Otherwise, just log and continue (but this is not recommended)
		// In production, we should probably restart for all unhandled rejections
	}
	
	handleWarning(warning) {
		console.warn('========================================');
		console.warn('PROCESS WARNING');
		console.warn('========================================');
		console.warn('Warning:', warning);
		console.warn('========================================');
		
		// Log warnings but don't restart
		// Some warnings are expected (e.g., deprecation warnings)
	}
	
	handleExit(code) {
		if (code !== 0 && !this.isShuttingDown) {
			console.error('========================================');
			console.error('PROCESS EXITING WITH ERROR CODE:', code);
			console.error('========================================');
		}
	}
	
	isFatalError(error) {
		// List of fatal errors that should not trigger restart
		const fatalErrors = [
			'EADDRINUSE', // Port already in use
			'EACCES', // Permission denied
			'ENOENT', // File not found (critical config files)
		];
		
		return fatalErrors.some(fatal => 
			error.code === fatal || 
			error.message.includes(fatal)
		);
	}
	
	isCriticalRejection(reason) {
		// Determine if rejection is critical enough to restart
		// Database connection errors, critical service failures, etc.
		const criticalPatterns = [
			'ECONNREFUSED',
			'ETIMEDOUT',
			'ENOTFOUND',
			'Database',
			'Connection',
		];
		
		const reasonStr = String(reason);
		return criticalPatterns.some(pattern => 
			reasonStr.includes(pattern)
		);
	}
	
	gracefulShutdown(callback) {
		if (this.isShuttingDown) {
			return;
		}
		
		this.isShuttingDown = true;
		
		console.log('Initiating graceful shutdown...');
		
		// Give time for ongoing requests to complete
		setTimeout(() => {
			if (callback) {
				callback();
			} else {
				process.exit(0);
			}
		}, 2000); // 2 seconds grace period
	}
	
	setShuttingDown(value) {
		this.isShuttingDown = value;
	}
}

// Singleton instance
let errorHandlerInstance = null;

function getErrorHandler() {
	if (!errorHandlerInstance) {
		errorHandlerInstance = new EnhancedErrorHandler();
	}
	return errorHandlerInstance;
}

module.exports = {
	EnhancedErrorHandler,
	getErrorHandler,
};
