/**
 * Network Connectivity Monitor
 * Tracks network connectivity issues, ISP-specific problems, and connection quality
 */

const fs = require('fs');
const path = require('path');
const NetworkDiagnostics = require('./networkDiagnostics');
const config = require('../config.json');

class NetworkConnectivityMonitor {
	constructor(options = {}) {
		this.options = {
			checkInterval: options.checkInterval || 60000, // 1 minute
			historySize: options.historySize || 1000,
			enableISPDetection: options.enableISPDetection !== false,
			enableLatencyTracking: options.enableLatencyTracking !== false,
			...options
		};

		this.history = [];
		this.ispStats = new Map();
		this.errorPatterns = new Map();
		this.isMonitoring = false;
		this.monitorInterval = null;
		this.logDir = path.join(__dirname, '../logs');
		this.logFile = path.join(this.logDir, 'network-connectivity.log');

		this.ensureLogDirectory();
	}

	ensureLogDirectory() {
		if (!fs.existsSync(this.logDir)) {
			fs.mkdirSync(this.logDir, { recursive: true });
		}
	}

	log(message, data = {}) {
		const logEntry = {
			timestamp: new Date().toISOString(),
			pid: process.pid,
			message,
			...data
		};

		fs.appendFile(this.logFile, JSON.stringify(logEntry) + '\n', 'utf8', (err) => {
			if (err) console.error('Error writing network connectivity log:', err);
		});
	}

	/**
	 * Detect ISP from IP address
	 */
	detectISP(ip) {
		if (!ip || ip === 'unknown') return 'unknown';

		// Telkomsel IP ranges (common ranges)
		if (ip.startsWith('182.3.') || ip.startsWith('182.2.') || ip.startsWith('182.1.')) {
			return 'Telkomsel';
		}
		if (ip.startsWith('182.0.')) {
			return 'Telkomsel';
		}

		// Indosat IP ranges
		if (ip.startsWith('114.') || ip.startsWith('202.152.')) {
			return 'Indosat';
		}

		// XL Axiata IP ranges
		if (ip.startsWith('110.') || ip.startsWith('180.240.')) {
			return 'XL Axiata';
		}

		// Three IP ranges
		if (ip.startsWith('103.') || ip.startsWith('125.160.')) {
			return 'Three';
		}

		// Maxindo IP ranges
		if (ip.startsWith('175.103.')) {
			return 'Maxindo';
		}

		// Corporate/VPN ranges
		if (ip.startsWith('175.103.40.')) {
			return 'Corporate Network';
		}

		return 'Unknown ISP';
	}

	/**
	 * Track connectivity check
	 */
	async checkConnectivity() {
		const startTime = Date.now();
		const host = config.databaseConfigSAPPS.host;
		const port = config.databaseConfigSAPPS.port;

		try {
			const diagnostic = await NetworkDiagnostics.testTCPConnection(host, port, 10000);

			const checkResult = {
				timestamp: new Date().toISOString(),
				success: diagnostic.success,
				latency: diagnostic.duration || null,
				error: diagnostic.error || null,
				errorType: diagnostic.errorType || null,
				host,
				port,
				duration: Date.now() - startTime
			};

			// Add to history
			this.history.push(checkResult);
			if (this.history.length > this.options.historySize) {
				this.history.shift();
			}

			// Log result
			if (!diagnostic.success) {
				this.log('Connectivity check failed', checkResult);
			}

			return checkResult;
		} catch (error) {
			const checkResult = {
				timestamp: new Date().toISOString(),
				success: false,
				error: error.message,
				errorType: 'EXCEPTION',
				host,
				port,
				duration: Date.now() - startTime
			};

			this.history.push(checkResult);
			if (this.history.length > this.options.historySize) {
				this.history.shift();
			}

			this.log('Connectivity check exception', checkResult);
			return checkResult;
		}
	}

	/**
	 * Track connection attempt with ISP detection
	 */
	trackConnectionAttempt(ip, success, latency, error = null) {
		if (!this.options.enableISPDetection) return;

		const isp = this.detectISP(ip);
		const ipRange = this.getIPRange(ip);

		// Update ISP stats
		if (!this.ispStats.has(isp)) {
			this.ispStats.set(isp, {
				isp,
				totalAttempts: 0,
				successfulAttempts: 0,
				failedAttempts: 0,
				totalLatency: 0,
				avgLatency: 0,
				errorCounts: new Map(),
				firstSeen: new Date().toISOString(),
				lastSeen: new Date().toISOString(),
				ipRanges: new Set()
			});
		}

		const stats = this.ispStats.get(isp);
		stats.totalAttempts++;
		stats.lastSeen = new Date().toISOString();
		stats.ipRanges.add(ipRange);

		if (success) {
			stats.successfulAttempts++;
			if (latency) {
				stats.totalLatency += latency;
				stats.avgLatency = stats.totalLatency / stats.successfulAttempts;
			}
		} else {
			stats.failedAttempts++;
			if (error) {
				const errorType = this.classifyError(error);
				stats.errorCounts.set(errorType, (stats.errorCounts.get(errorType) || 0) + 1);
			}
		}

		// Track error patterns
		if (error) {
			const errorKey = `${isp}:${this.classifyError(error)}`;
			if (!this.errorPatterns.has(errorKey)) {
				this.errorPatterns.set(errorKey, {
					isp,
					errorType: this.classifyError(error),
					count: 0,
					firstSeen: new Date().toISOString(),
					lastSeen: new Date().toISOString()
				});
			}

			const pattern = this.errorPatterns.get(errorKey);
			pattern.count++;
			pattern.lastSeen = new Date().toISOString();
		}
	}

	/**
	 * Get IP range (first 3 octets)
	 */
	getIPRange(ip) {
		if (!ip || ip === 'unknown') return 'unknown';
		if (ip.includes('.')) {
			const parts = ip.split('.');
			return `${parts[0]}.${parts[1]}.${parts[2]}.*`;
		}
		return ip;
	}

	/**
	 * Classify error type
	 */
	classifyError(error) {
		if (typeof error === 'string') {
			if (error.includes('ECONNREFUSED') || error.includes('refused')) return 'CONNECTION_REFUSED';
			if (error.includes('ETIMEDOUT') || error.includes('timeout')) return 'TIMEOUT';
			if (error.includes('EHOSTUNREACH') || error.includes('unreachable')) return 'HOST_UNREACHABLE';
			if (error.includes('ENETUNREACH') || error.includes('network unreachable')) return 'NETWORK_UNREACHABLE';
			return 'UNKNOWN';
		}

		if (error && error.code) {
			return error.code;
		}

		return 'UNKNOWN';
	}

	/**
	 * Start monitoring
	 */
	startMonitoring() {
		if (this.isMonitoring) {
			console.log('[NetworkConnectivityMonitor] Already monitoring');
			return;
		}

		this.isMonitoring = true;
		this.log('Network connectivity monitoring started');

		// Initial check
		this.checkConnectivity();

		// Periodic checks
		this.monitorInterval = setInterval(() => {
			this.checkConnectivity();
		}, this.options.checkInterval);

		console.log(`[NetworkConnectivityMonitor] Monitoring started (interval: ${this.options.checkInterval}ms)`);
	}

	/**
	 * Stop monitoring
	 */
	stopMonitoring() {
		if (!this.isMonitoring) {
			return;
		}

		this.isMonitoring = false;
		if (this.monitorInterval) {
			clearInterval(this.monitorInterval);
			this.monitorInterval = null;
		}

		this.log('Network connectivity monitoring stopped');
		console.log('[NetworkConnectivityMonitor] Monitoring stopped');
	}

	/**
	 * Get monitoring status
	 */
	getStatus() {
		const recentHistory = this.history.slice(-100); // Last 100 checks
		const successCount = recentHistory.filter(h => h.success).length;
		const failureCount = recentHistory.filter(h => !h.success).length;
		const successRate = recentHistory.length > 0 ? (successCount / recentHistory.length) * 100 : 0;

		const avgLatency = recentHistory
			.filter(h => h.latency)
			.reduce((sum, h) => sum + h.latency, 0) / recentHistory.filter(h => h.latency).length || 0;

		const recentErrors = recentHistory
			.filter(h => !h.success)
			.slice(-10)
			.map(h => ({
				timestamp: h.timestamp,
				error: h.error,
				errorType: h.errorType
			}));

		return {
			isMonitoring: this.isMonitoring,
			checkInterval: this.options.checkInterval,
			totalChecks: this.history.length,
			recentChecks: recentHistory.length,
			successRate: successRate.toFixed(2) + '%',
			successCount,
			failureCount,
			avgLatency: avgLatency ? avgLatency.toFixed(2) + 'ms' : 'N/A',
			lastCheck: this.history.length > 0 ? this.history[this.history.length - 1] : null,
			recentErrors,
			ispStats: Array.from(this.ispStats.values()).map(stats => ({
				...stats,
				successRate: stats.totalAttempts > 0 
					? ((stats.successfulAttempts / stats.totalAttempts) * 100).toFixed(2) + '%'
					: '0%',
				errorCounts: Object.fromEntries(stats.errorCounts),
				ipRanges: Array.from(stats.ipRanges)
			})),
			errorPatterns: Array.from(this.errorPatterns.values())
		};
	}

	/**
	 * Get ISP statistics
	 */
	getISPStatistics() {
		return Array.from(this.ispStats.values()).map(stats => ({
			isp: stats.isp,
			totalAttempts: stats.totalAttempts,
			successfulAttempts: stats.successfulAttempts,
			failedAttempts: stats.failedAttempts,
			successRate: stats.totalAttempts > 0 
				? ((stats.successfulAttempts / stats.totalAttempts) * 100).toFixed(2) + '%'
				: '0%',
			avgLatency: stats.avgLatency ? stats.avgLatency.toFixed(2) + 'ms' : 'N/A',
			errorCounts: Object.fromEntries(stats.errorCounts),
			firstSeen: stats.firstSeen,
			lastSeen: stats.lastSeen,
			ipRanges: Array.from(stats.ipRanges)
		}));
	}

	/**
	 * Get connectivity history
	 */
	getHistory(limit = 100) {
		return this.history.slice(-limit);
	}
}

// Singleton instance
let instance = null;

function getNetworkConnectivityMonitor(options) {
	if (!instance) {
		instance = new NetworkConnectivityMonitor(options);
	}
	return instance;
}

module.exports = {
	NetworkConnectivityMonitor,
	getNetworkConnectivityMonitor
};
