/**
 * Health Check & Monitoring Middleware
 * Provides comprehensive server health status and diagnostics
 */

const os = require("os");
const { performance } = require("perf_hooks");

// Track server uptime
const serverStartTime = Date.now();

// Store request metrics
const requestMetrics = {
	total: 0,
	success: 0,
	failed: 0,
	lastReset: Date.now(),
};

// Track ISP/IP patterns
const ipAccessLog = new Map();

/**
 * Get comprehensive server health status
 */
function getHealthStatus() {
	const uptime = Date.now() - serverStartTime;
	const memoryUsage = process.memoryUsage();
	const cpuUsage = process.cpuUsage();

	return {
		status: "healthy",
		timestamp: new Date().toISOString(),
		uptime: {
			ms: uptime,
			seconds: Math.floor(uptime / 1000),
			formatted: formatUptime(uptime),
		},
		server: {
			nodeVersion: process.version,
			platform: process.platform,
			platformInfo: (() => {
				try {
					const { getPlatformInfo } = require("../utils/osCompatibility");
					return getPlatformInfo();
				} catch (e) {
					return { platform: process.platform, error: e.message };
				}
			})(),
			pid: process.pid,
		},
		memory: {
			rss: `${Math.round(memoryUsage.rss / 1024 / 1024)} MB`,
			heapUsed: `${Math.round(memoryUsage.heapUsed / 1024 / 1024)} MB`,
			heapTotal: `${Math.round(memoryUsage.heapTotal / 1024 / 1024)} MB`,
			external: `${Math.round(memoryUsage.external / 1024 / 1024)} MB`,
		},
		system: {
			totalMemory: `${Math.round(os.totalmem() / 1024 / 1024 / 1024)} GB`,
			freeMemory: `${Math.round(os.freemem() / 1024 / 1024 / 1024)} GB`,
			cpus: os.cpus().length,
			loadAverage: os.loadavg(),
		},
		requests: {
			total: requestMetrics.total,
			success: requestMetrics.success,
			failed: requestMetrics.failed,
			successRate: requestMetrics.total > 0 ? `${((requestMetrics.success / requestMetrics.total) * 100).toFixed(2)}%` : "0%",
			since: new Date(requestMetrics.lastReset).toISOString(),
		},
	};
}

/**
 * Detailed diagnostics for connection troubleshooting
 */
function getConnectionDiagnostics(req) {
	const clientIP = getClientIP(req);
	const userAgent = req.headers["user-agent"] || "unknown";
	const accessLog = ipAccessLog.get(clientIP) || { count: 0, firstSeen: Date.now(), lastSeen: Date.now() };

	return {
		client: {
			ip: clientIP,
			forwardedFor: req.headers["x-forwarded-for"] || "none",
			realIP: req.headers["x-real-ip"] || "none",
			userAgent: userAgent,
			protocol: req.protocol,
			secure: req.secure,
		},
		connection: {
			httpVersion: req.httpVersion,
			method: req.method,
			url: req.url,
			host: req.headers.host,
			origin: req.headers.origin || "none",
			referer: req.headers.referer || "none",
		},
		headers: {
			contentType: req.headers["content-type"] || "none",
			accept: req.headers.accept || "none",
			acceptEncoding: req.headers["accept-encoding"] || "none",
			acceptLanguage: req.headers["accept-language"] || "none",
			cacheControl: req.headers["cache-control"] || "none",
		},
		accessHistory: {
			totalRequests: accessLog.count,
			firstSeen: new Date(accessLog.firstSeen).toISOString(),
			lastSeen: new Date(accessLog.lastSeen).toISOString(),
		},
		server: {
			timestamp: new Date().toISOString(),
			timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
		},
	};
}

/**
 * Get client IP address (handles proxy/reverse proxy)
 */
function getClientIP(req) {
	// Proxy headers intentionally ignored; use direct connection only
	return req.ip || req.socket?.remoteAddress || req.connection?.remoteAddress || "unknown";
}

/**
 * Format uptime in human-readable format
 */
function formatUptime(ms) {
	const seconds = Math.floor(ms / 1000);
	const minutes = Math.floor(seconds / 60);
	const hours = Math.floor(minutes / 60);
	const days = Math.floor(hours / 24);

	if (days > 0) return `${days}d ${hours % 24}h ${minutes % 60}m`;
	if (hours > 0) return `${hours}h ${minutes % 60}m`;
	if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
	return `${seconds}s`;
}

/**
 * Middleware to track request metrics
 */
function trackRequestMetrics(req, res, next) {
	const clientIP = getClientIP(req);

	// Track IP access
	const accessLog = ipAccessLog.get(clientIP) || { count: 0, firstSeen: Date.now(), lastSeen: Date.now() };
	accessLog.count++;
	accessLog.lastSeen = Date.now();
	ipAccessLog.set(clientIP, accessLog);

	// Track total requests
	requestMetrics.total++;

	// Track response status
	const originalSend = res.send;
	res.send = function (data) {
		if (res.statusCode >= 200 && res.statusCode < 400) {
			requestMetrics.success++;
		} else {
			requestMetrics.failed++;
		}
		originalSend.call(this, data);
	};

	next();
}

/**
 * Reset metrics (optional, can be called periodically)
 */
function resetMetrics() {
	requestMetrics.total = 0;
	requestMetrics.success = 0;
	requestMetrics.failed = 0;
	requestMetrics.lastReset = Date.now();
	ipAccessLog.clear();
}

/**
 * Get associate status and monitoring information
 * @returns {Object} Associate status information
 */
function getAssociateStatus() {
	try {
		const dbSAPPS = require("../apps/sapps/helpers/db");
		
		if (typeof dbSAPPS.getAssociateStatus === 'function') {
			const associateStatus = dbSAPPS.getAssociateStatus();
			const verificationResult = dbSAPPS.verifyAssociations();
			
			return {
				status: associateStatus.status,
				monitoring: associateStatus,
				verification: verificationResult,
				timestamp: new Date().toISOString(),
			};
		} else {
			return {
				status: 'unknown',
				error: 'Associate monitoring functions not available',
				timestamp: new Date().toISOString(),
			};
		}
	} catch (error) {
		return {
			status: 'error',
			error: error.message,
			timestamp: new Date().toISOString(),
		};
	}
}

/**
 * Get top N IP addresses by access count
 * @param {number} limit - Number of top IPs to return (default: 10)
 * @returns {Array} Array of IP access data sorted by count (descending)
 */
function getTopAccessingIPs(limit = 10) {
	try {
		// Convert Map to Array and sort by count
		const ipArray = Array.from(ipAccessLog.entries()).map(([ip, data]) => ({
			ip_address: ip,
			access_count: data.count,
			first_seen: new Date(data.firstSeen).toISOString(),
			last_seen: new Date(data.lastSeen).toISOString(),
			first_seen_formatted: new Date(data.firstSeen).toLocaleString(),
			last_seen_formatted: new Date(data.lastSeen).toLocaleString(),
		}));

		// Sort by access count (descending) and take top N
		const topIPs = ipArray
			.sort((a, b) => b.access_count - a.access_count)
			.slice(0, parseInt(limit, 10));

		return topIPs;
	} catch (error) {
		console.error('[HealthCheck] Error getting top accessing IPs:', error);
		return [];
	}
}

module.exports = {
	getHealthStatus,
	getConnectionDiagnostics,
	getClientIP,
	trackRequestMetrics,
	resetMetrics,
	getAssociateStatus,
	getTopAccessingIPs,
};
