// =============== CONNECTION ATTEMPTS CLEANUP SCHEDULER ===============
const cleanupScheduleState = {
	enabled: false,
	type: "daily", // daily/weekly
	time: "02:00",
	lastCleanup: null,
	table: "connection_attempts",
	log: [],
	timer: null,
};

function logCleanup(msg) {
	const ts = new Date().toISOString();
	cleanupScheduleState.log.push(`[${ts}] ${msg}`);
	if (cleanupScheduleState.log.length > 20) cleanupScheduleState.log.shift();
}

function scheduleCleanupWorker() {
	if (cleanupScheduleState.timer) clearTimeout(cleanupScheduleState.timer);
	if (!cleanupScheduleState.enabled) return;
	// Hitung ms ke waktu berikutnya
	const now = new Date();
	let next = new Date(now);
	const [hh, mm] = cleanupScheduleState.time.split(":").map(Number);
	next.setHours(hh, mm, 0, 0);
	if (next <= now) {
		// Jika sudah lewat, jadwalkan besok/pekan depan
		if (cleanupScheduleState.type === "daily") next.setDate(next.getDate() + 1);
		else if (cleanupScheduleState.type === "weekly") next.setDate(next.getDate() + 7);
	}
	const ms = next - now;
	cleanupScheduleState.timer = setTimeout(async () => {
		await runScheduledCleanup();
		scheduleCleanupWorker();
	}, ms);
	logCleanup(`Scheduled next cleanup at ${next.toLocaleString()}`);
}

async function runScheduledCleanup() {
	try {
		logCleanup(`Running scheduled cleanup for table ${cleanupScheduleState.table}`);
		// Load DB helper
		let dbSAPPS;
		try {
			dbSAPPS = require("./apps/sapps/helpers/db");
		} catch (e) {
			logCleanup("Database helper not available");
			return;
		}
		if (!dbSAPPS || !dbSAPPS.sequelize) {
			logCleanup("Database not ready");
			return;
		}
		const sequelize = dbSAPPS.sequelize;
		const table = cleanupScheduleState.table;
		// Default: hapus data lebih dari 90 hari lalu
		const before = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString();
		const limit = 10000;
		let totalDeleted = 0;
		let iterations = 0;
		let affected = 0;
		do {
			iterations++;
			const deleteSql = `DELETE FROM ${table} WHERE attempt_timestamp < :before LIMIT ${limit}`;
			try {
				const result = await sequelize.query(deleteSql, { replacements: { before } });
				if (Array.isArray(result) && result[1] && typeof result[1].affectedRows === "number") affected = result[1].affectedRows;
				else if (Array.isArray(result) && result[1] && typeof result[1].rowCount === "number") affected = result[1].rowCount;
				else if (result && typeof result.affectedRows === "number") affected = result.affectedRows;
				else affected = 0;
				totalDeleted += affected;
			} catch (e) {
				logCleanup(`Error during scheduled cleanup: ${e.message}`);
				break;
			}
		} while (affected === limit && iterations < 100);
		cleanupScheduleState.lastCleanup = new Date().toLocaleString();
		logCleanup(`Scheduled cleanup finished. Deleted: ${totalDeleted} rows in ${iterations} batch(es).`);
	} catch (e) {
		logCleanup(`Scheduled cleanup failed: ${e.message}`);
	}
}


// NOTE: app.get/app.post endpoints untuk cleanup scheduler didaftarkan setelah
// `const app = express()` di bawah, agar tidak terjadi ReferenceError.
// Inisialisasi worker juga dilakukan setelah app siap.
require("rootpath")();
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const helmet = require("helmet"); // Security headers
const compression = require("compression"); // Compress responses
const rateLimit = require("express-rate-limit"); // Rate limiting
const morgan = require("morgan"); // Logging
const path = require("path"); // Untuk resolve path folder
const errorHandler = require("middlewares/error-handler");
const healthCheck = require("middlewares/health-check");
const connectionResilience = require("middlewares/connection-resilience");
const ispAnalytics = require("middlewares/isp-analytics");
const connectionTracker = require("middlewares/connection-tracker");
const { getErrorHandler } = require("./utils/errorHandler");
const { getCrashRecovery } = require("./utils/crashRecovery");
// Menggunakan config.json (tidak pakai env/dotenv)
const config = require("./config.json");

// Basic Auth untuk dashboard monitoring (sangat ringan, tidak mengganggu sistem lain)
// Menggunakan config.json (tidak pakai env)
const DASHBOARD_USER = config.backendConfig.dashboardUser || "admin";
const DASHBOARD_PASS = config.backendConfig.dashboardPass || "password";
function dashboardAuth(req, res, next) {
	const auth = req.headers.authorization;
	if (!auth || !auth.startsWith("Basic ")) {
		res.set("WWW-Authenticate", 'Basic realm="Dashboard"');
		return res.status(401).send("Authentication required.");
	}
	const [user, pass] = Buffer.from(auth.split(" ")[1], "base64").toString().split(":");
	if (user === DASHBOARD_USER && pass === DASHBOARD_PASS) return next();
	res.set("WWW-Authenticate", 'Basic realm="Dashboard"');
	return res.status(401).send("Invalid credentials.");
}

// Pastikan dashboardConfig ada (sumber: config.json)
if (!config.dashboardConfig) config.dashboardConfig = {};

// Pastikan databaseConfigSAPPS memiliki field SSL/timezone jika belum ada
if (config.databaseConfigSAPPS.ssl === undefined) config.databaseConfigSAPPS.ssl = false;
if (config.databaseConfigSAPPS.sslRejectUnauthorized === undefined) config.databaseConfigSAPPS.sslRejectUnauthorized = false;
if (!config.databaseConfigSAPPS.timezone) config.databaseConfigSAPPS.timezone = "+07:00";

// =============== CRASH RECOVERY INITIALIZATION ===============
// Initialize crash recovery system BEFORE anything else
const crashRecovery = getCrashRecovery({
	maxRestarts: 10,
	restartWindow: 60000, // 1 minute
	restartDelay: 2000, // 2 seconds initial delay
	maxRestartDelay: 30000, // 30 seconds max delay
});

// Initialize enhanced error handler
const enhancedErrorHandler = getErrorHandler();

const app = express();

/* Proxy has been disabled per request */
app.set("trust proxy", false);

// =============== CLEANUP SCHEDULER ENDPOINTS ===============
// Didaftarkan di sini (setelah `app` dibuat) agar tidak ReferenceError

// Endpoint: GET status schedule cleanup
app.get("/admin/connection-attempts/schedule-status", dashboardAuth, (req, res) => {
	res.json({
		enabled: cleanupScheduleState.enabled,
		type: cleanupScheduleState.type,
		time: cleanupScheduleState.time,
		lastCleanup: cleanupScheduleState.lastCleanup,
		table: cleanupScheduleState.table,
		log: cleanupScheduleState.log,
	});
});

// Endpoint: POST config schedule cleanup
app.post("/admin/connection-attempts/schedule-config", express.json(), dashboardAuth, (req, res) => {
	const { enabled, type, time, table } = req.body || {};
	if (typeof enabled === "boolean") cleanupScheduleState.enabled = enabled;
	if (type === "daily" || type === "weekly") cleanupScheduleState.type = type;
	if (typeof time === "string" && /^\d{2}:\d{2}$/.test(time)) cleanupScheduleState.time = time;
	if (["connection_attempts", "connectionattempts"].includes(table)) cleanupScheduleState.table = table;
	logCleanup(
		`Schedule config updated: enabled=${cleanupScheduleState.enabled}, type=${cleanupScheduleState.type}, time=${cleanupScheduleState.time}, table=${cleanupScheduleState.table}`,
	);
	if (cleanupScheduleState.enabled) scheduleCleanupWorker();
	else if (cleanupScheduleState.timer) {
		clearTimeout(cleanupScheduleState.timer);
		cleanupScheduleState.timer = null;
	}
	res.json({ ok: true });
});

// Inisialisasi worker saat startup jika enabled
if (cleanupScheduleState.enabled) scheduleCleanupWorker();

// =============== CONNECTION TRACKING TOGGLE ===============
// State tracking enabled/disabled (runtime toggle, di-sync via file untuk multi-process/Passenger)
const TRACKING_STATE_FILE = path.join(__dirname, 'tracking-state.json');

const connectionTrackingState = {
	get enabled() {
		try {
			if (fs.existsSync(TRACKING_STATE_FILE)) {
				const state = JSON.parse(fs.readFileSync(TRACKING_STATE_FILE, 'utf8'));
				if (typeof state.enabled === 'boolean') return state.enabled;
			}
		} catch (e) {}
		return true; // Default aktif
	},
	set enabled(val) {
		try {
			let state = {};
			if (fs.existsSync(TRACKING_STATE_FILE)) {
				try { state = JSON.parse(fs.readFileSync(TRACKING_STATE_FILE, 'utf8')); } catch (e) {}
			}
			state.enabled = val;
			fs.writeFileSync(TRACKING_STATE_FILE, JSON.stringify(state), 'utf8');
			global.connectionTrackingEnabled = val; // Update langsung di proses ini
		} catch (e) {
			console.error("[ConnectionTracking] Gagal simpan state:", e.message);
		}
	}
};

// Sync global flag secara periodik (5 detik) untuk sinkronisasi antar proses
setInterval(() => {
	global.connectionTrackingEnabled = connectionTrackingState.enabled;
}, 5000);

// Init global flag saat startup
global.connectionTrackingEnabled = connectionTrackingState.enabled;

// GET status tracking (tanpa auth — read-only, tidak sensitif)
// Agar dashboard dapat menampilkan status tracking saat load tanpa perlu login dulu
app.get("/admin/connection-tracking/status", (req, res) => {
	const isEnabled = connectionTrackingState.enabled;
	res.json({
		enabled: isEnabled,
		message: isEnabled
			? "Connection tracking is ACTIVE — data sedang ditulis ke DB."
			: "Connection tracking is DISABLED — tidak ada data baru yang ditulis.",
	});
});

// POST toggle tracking
app.post("/admin/connection-tracking/toggle", express.json(), dashboardAuth, (req, res) => {
	const { enabled } = req.body || {};
	if (typeof enabled !== "boolean") return res.status(400).json({ error: "'enabled' (boolean) is required" });
	
	connectionTrackingState.enabled = enabled;
	
	console.log(`[ConnectionTracking] Tracking ${enabled ? "ENABLED" : "DISABLED"} via dashboard.`);
	res.json({ ok: true, enabled });
});

// ================= TRAFFIC COUNTER (REALTIME) =================
const trafficStats = {
	total: 0,
	perEndpoint: {},
	perMinute: {},
};
app.use((req, res, next) => {
	// Hitungan hanya untuk API (bisa disesuaikan)
	if (req.path.startsWith("/sapps/") || req.path.startsWith("/api/")) {
		trafficStats.total++;
		// Per endpoint
		trafficStats.perEndpoint[req.path] = (trafficStats.perEndpoint[req.path] || 0) + 1;
		// Per menit
		const minute = new Date().toISOString().slice(0, 16); // yyyy-mm-ddThh:mm
		trafficStats.perMinute[minute] = (trafficStats.perMinute[minute] || 0) + 1;
	}

	next();
});

// Periodic cleanup for trafficStats to prevent memory leak
setInterval(
	() => {
		// Clean perMinute (keep last 60 minutes)
		const minutes = Object.keys(trafficStats.perMinute).sort();
		if (minutes.length > 60) {
			const toRemove = minutes.slice(0, minutes.length - 60);
			toRemove.forEach((m) => delete trafficStats.perMinute[m]);
		}

		// Safety check for perEndpoint (prevent explosion from scanning or dynamic paths)
		const endpoints = Object.keys(trafficStats.perEndpoint);
		if (endpoints.length > 2000) {
			console.warn("Traffic stats warning: excessive unique endpoints. Resetting endpoint stats to prevent memory leak.");
			trafficStats.perEndpoint = {};
		}
	},
	60 * 60 * 1000,
); // Check every hour

// Endpoint untuk dashboard visual
app.get("/analytics/traffic", (req, res) => {
	res.json({
		total: trafficStats.total,
		perEndpoint: trafficStats.perEndpoint,
		perMinute: trafficStats.perMinute,
		ts: Date.now(),
	});
});
// Endpoint monitoring system info (uptime, memory, CPU, error rate, recent error log)
const os = require("os");
const fs = require("fs");
const { exec } = require("child_process");
const { promisify } = require("util");
const execAsync = promisify(exec);
const DISK_CHECK_TIMEOUT = 3000; // avoid blocking event loop in production (exec can be slow on shared hosts)
app.get("/analytics/sysinfo", async (req, res) => {
	// Uptime
	const uptime = process.uptime();
	// Memory
	const mem = process.memoryUsage();
	// CPU
	const cpus = os.cpus();
	const load = os.loadavg();
	// Node version
	const nodeVersion = process.version;
	// Error rate (dari trafficStats, error = status >= 400)
	if (!trafficStats.errorCount) trafficStats.errorCount = 0;
	// Recent error log (async read to avoid blocking event loop; was readFileSync and caused 503 under load)
	let recentErrors = [];
	try {
		const logPath = path.join(__dirname, "logs", "login-requests.log");
		const content = await fs.promises.readFile(logPath, "utf8");
		const lines = content.trim().split("\n");
		recentErrors = lines.slice(-10).map((l) => {
			try {
				return JSON.parse(l);
			} catch {
				return l;
			}
		});
	} catch (_) {}

	// Disk usage (cross-platform): async exec + timeout
	// Compatible with AlmaLinux (Linux), Windows, and other Unix-like systems
	let disk = { total: null, used: null, free: null, percent: null, raw: null };
	try {
		const { getPlatformInfo } = require("./utils/osCompatibility");
		const platformInfo = getPlatformInfo();

		if (platformInfo.isLinux || platformInfo.isDarwin) {
			// Use df -P . to get the disk usage of the current directory (where the app is installed)
			// -P ensures POSIX output (one line per filesystem)
			// Works for AlmaLinux, CentOS, RHEL, Ubuntu, Debian, macOS, etc.
			const { stdout } = await execAsync("df -P .", {
				encoding: "utf8",
				timeout: DISK_CHECK_TIMEOUT,
			});
			disk.raw = stdout;
			const lines = stdout.trim().split("\n");
			// Header is line 0, data is line 1
			// Filesystem 1024-blocks Used Available Capacity Mounted on
			if (lines[1]) {
				const parts = lines[1].split(/\s+/);
				// parts[1] = total (in 1K blocks), parts[2] = used, parts[3] = available, parts[4] = capacity percentage
				const totalBlocks = parseInt(parts[1], 10);
				const usedBlocks = parseInt(parts[2], 10);
				const freeBlocks = parseInt(parts[3], 10);

				disk.total = (totalBlocks / 1024 / 1024).toFixed(2) + " GB"; // 1K blocks -> GB
				disk.used = (usedBlocks / 1024 / 1024).toFixed(2) + " GB";
				disk.free = (freeBlocks / 1024 / 1024).toFixed(2) + " GB";
				disk.percent = parts[4]; // Already includes %
			}
		} else if (platformInfo.isWindows) {
			// Windows disk usage check
			const currentDrive = path.parse(process.cwd()).root.replace("\\", ""); // e.g., "C:"
			try {
				// Try wmic first (Windows 7/8/10/11)
				const { stdout } = await execAsync("wmic logicaldisk get size,freespace,caption", { encoding: "utf8", timeout: DISK_CHECK_TIMEOUT });
				disk.raw = stdout;
				const lines = stdout.trim().split("\n");

				// WMIC output usually: Caption  FreeSpace  Size
				// We find the line that starts with our drive letter
				const driveLine = lines.find((l) => l.trim().toUpperCase().startsWith(currentDrive.toUpperCase()));

				if (driveLine) {
					const parts = driveLine.trim().split(/\s+/);
					// Assuming order: Caption, FreeSpace, Size (alphabetical columns)
					// If the drive letter matches first part, then parts[1] is FreeSpace, parts[2] is Size
					const free = parseInt(parts[1], 10);
					const size = parseInt(parts[2], 10);

					disk.total = (size / 1024 / 1024 / 1024).toFixed(2) + " GB";
					disk.free = (free / 1024 / 1024 / 1024).toFixed(2) + " GB";
					disk.used = ((size - free) / 1024 / 1024 / 1024).toFixed(2) + " GB";
					disk.percent = size ? (((size - free) / size) * 100).toFixed(1) + "%" : null;
				}
			} catch (wmicError) {
				// Fallback for Windows 10/11 (wmic deprecated, use PowerShell)
				try {
					const { stdout } = await execAsync(
						`powershell -Command "Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Root -eq '${currentDrive}\\'} | Select-Object Used,Free | ConvertTo-Json"`,
						{ encoding: "utf8", timeout: DISK_CHECK_TIMEOUT },
					);
					const psResult = JSON.parse(stdout);
					if (psResult && psResult.Used !== undefined) {
						const used = psResult.Used;
						const free = psResult.Free;
						const total = used + free;

						disk.total = (total / 1024 / 1024 / 1024).toFixed(2) + " GB";
						disk.free = (free / 1024 / 1024 / 1024).toFixed(2) + " GB";
						disk.used = (used / 1024 / 1024 / 1024).toFixed(2) + " GB";
						disk.percent = total ? ((used / total) * 100).toFixed(1) + "%" : null;
					}
				} catch (psError) {
					throw new Error(`Windows disk check failed: ${wmicError.message}, ${psError.message}`);
				}
			}
		}
	} catch (e) {
		// fallback: gunakan os.freemem/totalmem (ini RAM, bukan disk, tapi di beberapa dashboard monitoring dicampur)
		// Kita beri label khusus jika fallback
		disk.total = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + " GB (RAM)";
		disk.free = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + " GB (RAM)";
		disk.used = ((os.totalmem() - os.freemem()) / 1024 / 1024 / 1024).toFixed(2) + " GB (RAM)";
		disk.percent = (100 - (os.freemem() / os.totalmem()) * 100).toFixed(1) + "%";
		disk.error = e.message;
		disk.fallback = true;
	}

	// Status database (readiness) - gracefully handle database unavailability
	let dbStatus = {
		ready: false,
		latencyMs: null,
		error: null,
		available: false,
	};
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		const start = Date.now();
		if (dbSAPPS.isReady && dbSAPPS.isReady()) {
			dbStatus.ready = true;
			dbStatus.available = true;
			if (dbSAPPS.sequelize && dbSAPPS.sequelize.authenticate) {
				try {
					await dbSAPPS.sequelize.authenticate();
					dbStatus.latencyMs = Date.now() - start;
				} catch (authErr) {
					dbStatus.ready = false;
					dbStatus.available = false;
					dbStatus.error = authErr.message || "Database authentication failed";
				}
			}
		} else {
			dbStatus.ready = false;
			dbStatus.available = false;
			dbStatus.error = "Database not ready";
		}
	} catch (e) {
		dbStatus.available = false;
		dbStatus.error = e.message || "Database connection unavailable";
	}

	// Rekomendasi otomatis
	const rekomendasi = [];
	if (mem.heapUsed / mem.heapTotal > 0.85) rekomendasi.push("Memory usage tinggi, pertimbangkan restart service atau optimasi kode.");
	if (disk.percent && parseFloat(disk.percent) > 85) rekomendasi.push("Disk hampir penuh, hapus file tidak perlu atau tambah kapasitas.");
	if (dbStatus.latencyMs && dbStatus.latencyMs > 1000) rekomendasi.push("Database lambat (>1s), cek koneksi DB atau query lambat.");
	if (trafficStats.errorCount / (trafficStats.total || 1) > 0.05) rekomendasi.push("Tingkat error tinggi (>5%), cek log error dan perbaiki bug.");
	if (!dbStatus.ready) rekomendasi.push("Database tidak siap, cek status DB dan koneksi.");
	if (config.backendConfig.NODE_ENV !== "production")
		rekomendasi.push("Mode DEVELOPMENT aktif via config.json/env. Set NODE_ENV=production di Application Manager untuk performa optimal.");
	if (rekomendasi.length === 0) rekomendasi.push("Semua sistem dalam kondisi normal.");

	res.json({
		uptime,
		platform: process.platform,
		platformInfo: (() => {
			try {
				const { getPlatformInfo } = require("./utils/osCompatibility");
				return getPlatformInfo();
			} catch (e) {
				return { platform: process.platform, error: e.message };
			}
		})(),
		memory: mem,
		cpu: {
			count: cpus.length,
			model: cpus[0]?.model,
			speed: cpus[0]?.speed,
			load,
		},
		nodeVersion,
		errorCount: trafficStats.errorCount,
		errorRate: trafficStats.total ? trafficStats.errorCount / trafficStats.total : 0,
		recentErrors,
		disk,
		dbStatus,
		rekomendasi,
		ts: Date.now(),
	});
});

// ================= SUPER LIGHTWEIGHT STATUS ENDPOINTS (EARLY RETURN) =================
// These endpoints are placed BEFORE any heavy middleware, DB, or logging
// They do NOT hit DB, do NOT log, and are safe for health checks, monitoring, and keepalive
const statusUptime = { started: Date.now() };
app.get("/health", (req, res) => res.status(200).send("OK"));
app.head("/health", (req, res) => res.status(200).end());
app.get("/ping", (req, res) =>
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		uptime: Math.floor((Date.now() - statusUptime.started) / 1000),
	}),
);
app.get("/health-keepalive", (req, res) =>
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		uptime: Math.floor((Date.now() - statusUptime.started) / 1000),
		keepalive: true,
	}),
);
// Diagnostics/analytics endpoints (tanpa DB, hanya info statis)
app.get("/diagnostics", (req, res) =>
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		env: config.backendConfig.NODE_ENV || "development",
		uptime: Math.floor((Date.now() - statusUptime.started) / 1000),
	}),
);
app.get("/analytics/isp", (req, res) => res.status(200).json({ ok: true, ts: Date.now(), note: "lightweight analytics endpoint" }));
app.get("/diagnostics/ping", (req, res) =>
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		env: config.backendConfig.NODE_ENV || "development",
	}),
);
app.get("/diagnostics/statuses", (req, res) =>
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		env: config.backendConfig.NODE_ENV || "development",
		uptime: Math.floor((Date.now() - statusUptime.started) / 1000),
	}),
);
// Dashboard polling interval (from config/ENV); lightweight, no DB
app.get("/api/dashboard-config", (req, res) => {
	const raw = config.dashboardConfig?.pollIntervalMs ?? 10000;
	const ms = parseInt(String(raw), 10) || 10000;
	const pollIntervalMs = Math.max(5000, Math.min(60000, ms));
	res.status(200).json({ pollIntervalMs });
});

// Admin: Cleanup connection attempts (protected by Basic auth)
// FIXED: (1) dry-run count=0 falsy bug, (2) affectedRows parsing for all MySQL Sequelize drivers,
//        (3) timezone-safe date comparison (try UTC + local format), (4) TRUNCATE option
app.post("/admin/connection-attempts/cleanup", express.json(), dashboardAuth, async (req, res) => {
	try {
		const { before, batch = 10000, dryRun = false, maxIterations = 2000, truncate = false } = req.body || {};

		if (!truncate && !before) return res.status(400).json({ error: "Missing 'before' timestamp in request body" });

		let beforeDate = null;
		if (!truncate) {
			beforeDate = new Date(before);
			if (isNaN(beforeDate.getTime())) return res.status(400).json({ error: "Invalid 'before' timestamp" });
		}

		// Load DB helper
		let dbSAPPS;
		try {
			dbSAPPS = require("./apps/sapps/helpers/db");
		} catch (e) {
			return res.status(500).json({ error: "Database helper not available" });
		}
		if (!dbSAPPS || !dbSAPPS.sequelize) return res.status(503).json({ error: "Database not ready" });

		const sequelize = dbSAPPS.sequelize;
		const candidateTables = ["connectionattempts", "connection_attempts"];

		// Helper: parse affectedRows dari berbagai format Sequelize/MySQL2 driver
		function parseAffected(result) {
			if (!result) return 0;
			// Format 1: [rows, metadata] — MySQL2 Sequelize
			if (Array.isArray(result)) {
				const meta = result[1];
				if (meta && typeof meta.affectedRows === "number") return meta.affectedRows;
				if (meta && typeof meta.rowCount === "number") return meta.rowCount;
				// Format 2: [ResultSetHeader] — some MySQL2 versions return array of 1
				if (result[0] && typeof result[0].affectedRows === "number") return result[0].affectedRows;
				// Format 3: meta is a number (affectedRows directly as second element)
				if (typeof meta === "number") return meta;
			}
			// Format 4: result is the header object directly
			if (typeof result.affectedRows === "number") return result.affectedRows;
			return -1; // unknown
		}

		// Helper: buat MySQL-safe datetime string (timezone +07:00)
		// Timezone offset dalam config: "+07:00" → konversi ke WIB untuk perbandingan kolom DB
		function toMySQLDatetime(date) {
			// Format: YYYY-MM-DD HH:MM:SS (tanpa timezone) agar cocok dengan kolom DATETIME di MySQL
			const pad = (n) => String(n).padStart(2, "0");
			return (
				`${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +
				`${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
			);
		}

		// Helper: find the first existing table from candidates
		async function findTable() {
			for (const t of candidateTables) {
				try {
					await sequelize.query(`SELECT 1 FROM \`${t}\` LIMIT 1`);
					return t;
				} catch (_) {}
			}
			return null;
		}

		// ── TRUNCATE: hapus semua data tanpa kondisi (jauh lebih cepat) ──────
		if (truncate) {
			// Jika client mengirim `table` spesifik, gunakan itu (validasi dulu)
			const requestedTable = req.body && req.body.table;
			const ALLOWED_TABLES = ["connectionattempts", "connection_attempts"];

			let usedTable;
			if (requestedTable && ALLOWED_TABLES.includes(requestedTable)) {
				// Verifikasi tabel tersebut ada di database
				try {
					await sequelize.query(`SELECT 1 FROM \`${requestedTable}\` LIMIT 1`);
					usedTable = requestedTable;
				} catch (_) {
					return res.status(404).json({ error: `Tabel '${requestedTable}' tidak ditemukan di database.` });
				}
			} else {
				// Tidak ada `table` spesifik → cari tabel pertama yang ada
				usedTable = await findTable();
				if (!usedTable) return res.status(404).json({ error: "Connection attempts table not found" });
			}

			try {
				await sequelize.query(`TRUNCATE TABLE \`${usedTable}\``);
				console.log(`[Cleanup] TRUNCATE berhasil: ${usedTable}`);
				return res.json({ table: usedTable, truncated: true, ok: true, deleted: "ALL" });
			} catch (e) {
				console.error(`[Cleanup] TRUNCATE gagal pada ${usedTable}:`, e.message);
				return res.status(500).json({ error: "TRUNCATE failed: " + e.message });
			}
		}

		// ── DRY-RUN: hitung saja, jangan delete ───────────────────────────────
		if (dryRun) {
			const usedTable = await findTable();
			if (!usedTable) return res.status(404).json({ error: "Connection attempts table not found" });

			// Coba 2 format tanggal: UTC ISO dan MySQL local datetime
			const dateFormats = [beforeDate.toISOString(), toMySQLDatetime(beforeDate)];
			let countResult = 0;
			let usedFormat = null;

			for (const fmt of dateFormats) {
				try {
					const [rows] = await sequelize.query(
						`SELECT COUNT(*) as cnt FROM \`${usedTable}\` WHERE attempt_timestamp < :before`,
						{ replacements: { before: fmt }, raw: true }
					);
					const cnt = rows && rows[0] ? (Number(rows[0].cnt) || Number(rows[0].CNT) || Number(rows[0].count) || 0) : 0;
					countResult = cnt;
					usedFormat = fmt;
					break; // gunakan yang berhasil pertama
				} catch (e) {
					continue;
				}
			}

			return res.json({
				table: usedTable,
				count: countResult,
				usedDateFormat: usedFormat,
				before: beforeDate.toISOString(),
			});
		}

		// ── ACTUAL DELETE: batch loop ─────────────────────────────────────────
		const usedTable = await findTable();
		if (!usedTable) return res.status(404).json({ error: "Connection attempts table not found" });

		const limit = Math.max(100, Math.min(100000, parseInt(batch, 10) || 10000));
		let totalDeleted = 0;
		let iterations = 0;
		let lastError = null;

		// Coba UTC format dulu, jika 0 coba MySQL local format (timezone fix)
		const dateFormatsToTry = [beforeDate.toISOString(), toMySQLDatetime(beforeDate)];
		let workingFormat = null;

		// Test format mana yang menghasilkan rows (quick count test)
		for (const fmt of dateFormatsToTry) {
			try {
				const [rows] = await sequelize.query(
					`SELECT COUNT(*) as cnt FROM \`${usedTable}\` WHERE attempt_timestamp < :before`,
					{ replacements: { before: fmt }, raw: true }
				);
				const cnt = Number((rows && rows[0] && (rows[0].cnt || rows[0].CNT || rows[0].count)) || 0);
				console.log(`[Cleanup] Format test '${fmt}': ${cnt} rows would be deleted from ${usedTable}`);
				if (cnt > 0) {
					workingFormat = fmt;
					break;
				}
				// Jika cnt=0 dengan format ini, coba format berikutnya
			} catch (e) {
				continue;
			}
		}

		// Jika kedua format 0, tidak ada yang perlu dihapus — kembalikan hasil
		if (!workingFormat) {
			return res.json({
				table: usedTable,
				deleted: 0,
				iterations: 0,
				message: "No rows found to delete for the given date range. Check that 'before' date is correct.",
				before: beforeDate.toISOString(),
				triedFormats: dateFormatsToTry,
			});
		}

		console.log(`[Cleanup] Starting batch delete from ${usedTable} where attempt_timestamp < '${workingFormat}', batch=${limit}`);

		while (iterations < maxIterations) {
			iterations++;
			const deleteSql = `DELETE FROM \`${usedTable}\` WHERE attempt_timestamp < :before LIMIT ${limit}`;
			try {
				const result = await sequelize.query(deleteSql, {
					replacements: { before: workingFormat },
					type: sequelize.QueryTypes ? sequelize.QueryTypes.DELETE : undefined,
				});
				const affected = parseAffected(result);
				console.log(`[Cleanup] Batch ${iterations}: affected=${affected} (raw result type: ${typeof result[1]})`);

				if (affected === -1) {
					// format tidak dikenali — stop dan laporkan
					lastError = "Unrecognized affectedRows format from DB driver. Raw: " + JSON.stringify(result && result[1]);
					break;
				}
				if (affected === 0) break; // tidak ada lagi yang cocok
				totalDeleted += affected;
				if (affected < limit) break; // batch terakhir
			} catch (e) {
				lastError = e.message;
				console.error(`[Cleanup] Delete error at iteration ${iterations}:`, e.message);
				break;
			}
		}

		console.log(`[Cleanup] Finished: deleted=${totalDeleted}, iterations=${iterations}, table=${usedTable}`);

		return res.json({
			table: usedTable,
			deleted: totalDeleted,
			iterations,
			before: workingFormat,
			...(lastError ? { warning: lastError } : {}),
		});
	} catch (err) {
		console.error("Error in cleanup endpoint:", err);
		return res.status(500).json({ error: err.message || String(err) });
	}
});

// If you want to keep the original endpoints for deeper diagnostics, rename them (e.g. /diagnostics-full)

// =============== ISP BLOCKING MITIGATION ===============
// Track requests for ISP blocking detection
app.use(ispAnalytics.trackRequest);

// IP Connection Tracking (Enhanced)
app.use(connectionTracker);

// =============== NETWORK CONNECTIVITY MONITORING ===============
// Initialize network connectivity monitor
const { getNetworkConnectivityMonitor } = require("./utils/networkConnectivityMonitor");
const networkConnectivityMonitor = getNetworkConnectivityMonitor({
	checkInterval: 60000, // 1 minute
	historySize: 1000,
	enableISPDetection: true,
	enableLatencyTracking: true,
});

// Track connection attempts with ISP detection
app.use((req, res, next) => {
	const startTime = Date.now();
	const clientIP = req.ip || req.socket?.remoteAddress || req.connection?.remoteAddress || "unknown";

	// Track requests (for API calls that indicate backend connectivity)
	res.on("finish", () => {
		if (req.path.startsWith("/sapps/") || req.path.startsWith("/api/")) {
			const success = res.statusCode >= 200 && res.statusCode < 400;
			const responseTime = Date.now() - startTime;

			// Only track if network connectivity monitor is available
			if (global.networkConnectivityMonitor) {
				global.networkConnectivityMonitor.trackConnectionAttempt(clientIP, success, responseTime, success ? null : `HTTP ${res.statusCode}`);
			}
		}
	});

	next();
});

// Add request tracking metrics
app.use(healthCheck.trackRequestMetrics);

// Configure connection resilience
connectionResilience.configureConnectionResilience(app);

// Add retry headers
app.use(connectionResilience.addRetryHeaders);

// Optimize transmission
app.use(connectionResilience.optimizeTransmission);

// ================= SECURITY ===================
app.use(
	helmet({
		crossOriginEmbedderPolicy: false,
		crossOriginResourcePolicy: false,
		contentSecurityPolicy: false,
	}),
);

// ================ COMPRESSION ==================
app.use(
	compression({
		level: 6,
		threshold: 1024,
	}),
);

// =================== CORS ======================
// Enhanced CORS configuration to bypass ISP restrictions
// Allow localhost for development even in production (for local frontend dev)
const corsOptions = {
	origin: function (origin, callback) {
		// Allow requests with no origin (mobile apps, Postman, etc.)
		if (!origin) return callback(null, true);

		// Base production origins
		const productionOrigins = [
			config.frontendConfig.url,
			"https://sapps.sapharma.co.id",
			"https://sapps-uat.sapharma.co.id",
			"https://sapps-backup.sapharma.co.id",
		];

		// Localhost origins for development (allowed in both dev and production)
		const localhostOrigins = ["http://localhost:3000", "http://localhost:3001", "http://localhost:5173", "http://127.0.0.1:3000", "http://127.0.0.1:5173"];

		// Combine origins based on environment
		const allowedOrigins =
			config.backendConfig.NODE_ENV === "production"
				? [...productionOrigins, ...localhostOrigins] // Allow localhost in production for local dev
				: [...productionOrigins, ...localhostOrigins];

		// Normalize origin (remove trailing slash)
		const normalizedOrigin = origin.replace(/\/$/, "");

		// Check for exact match (case-sensitive for protocol/host, but allow localhost variations)
		const isAllowed = allowedOrigins.some((allowed) => {
			const normalizedAllowed = allowed.replace(/\/$/, "");
			// Exact match (case-sensitive)
			if (normalizedOrigin === normalizedAllowed) {
				return true;
			}
			// Case-insensitive match for localhost/127.0.0.1
			if (normalizedOrigin.toLowerCase().includes("localhost") || normalizedOrigin.toLowerCase().includes("127.0.0.1")) {
				return normalizedOrigin.toLowerCase() === normalizedAllowed.toLowerCase();
			}
			// StartsWith for production URLs (to handle subdomains and paths)
			return normalizedOrigin.startsWith(normalizedAllowed);
		});

		if (isAllowed) {
			callback(null, true);
		} else {
			// Log unauthorized origin for monitoring but still allow (for development flexibility)
			console.warn(`[CORS] Unauthorized origin: ${origin} - Allowed origins: ${allowedOrigins.join(", ")}`);
			callback(null, true);
		}
	},
	// Allow credentials for both development and production (needed for auth cookies)
	credentials: true,
	methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH"],
	allowedHeaders: [
		"Content-Type",
		"Authorization",
		"X-Requested-With",
		"X-Request-ID",
		"Cache-Control",
		"Pragma",
		// Concurrency and idempotency headers used by client
		"If-Match",
		"X-Record-Version",
		"X-Idempotency-Key",
	],
	exposedHeaders: ["Content-Length", "X-Request-ID", "X-Retry-After", "ETag", "X-Record-Version"],
	maxAge: 86400, // 24 hours - reduce preflight requests
	preflightContinue: false,
	optionsSuccessStatus: 200, // Use 200 instead of 204 for better compatibility
};
// Explicitly handle preflight for all routes before any other middleware
app.options("*", cors(corsOptions));
app.use(cors(corsOptions));

// ================= RATE LIMIT ===================
const limiter = rateLimit({
	windowMs: 15 * 60 * 1000,
	max: 10000000,
	standardHeaders: true,
	legacyHeaders: false,

	/* FIX ERROR:
     The 'X-Forwarded-For' header is set but trust proxy is false */
	validate: { xForwardedForHeader: false },

	/* skip static folder agar upload/download tidak dibatasi */
	skip: (req) => {
		const staticPaths = ["/avatars", "/logos", "/documents", "/images", "/videos", "/audios", "/others"];
		return staticPaths.some((p) => req.path.startsWith(p));
	},
});
app.use(limiter);

// ================= LOGGING ======================
app.use(config.backendConfig.NODE_ENV === "development" ? morgan("dev") : morgan("combined"));

// ================= BODY PARSER ==================
// FIXED: Increased limit untuk support bulk import operations
// JSON limit increased to 50mb untuk import-all-item operations
app.use(
	bodyParser.json({
		limit: "50mb", // Increased from 1mb to support bulk imports
		type: "application/json",
	}),
);
app.use(
	bodyParser.urlencoded({
		extended: true,
		limit: "50mb", // Increased from 10mb to match JSON limit
	}),
);

// ================= STATIC FILE CORS ===============
const staticCORS = (req, res, next) => {
	// Enhanced CORS for static files
	// Allow localhost in both development and production for local frontend dev
	// Use same robust matching logic as main CORS for consistency
	const allowedOrigins = [
		config.frontendConfig.url,
		"https://sapps.sapharma.co.id",
		"https://sapps-uat.sapharma.co.id",
		"https://sapps-backup.sapharma.co.id",
		"http://localhost:3000",
		"http://localhost:5173",
		"http://127.0.0.1:3000",
		"http://127.0.0.1:5173",
	];

	const origin = req.headers.origin;

	// Use same robust matching logic as main CORS
	if (origin) {
		// Normalize origin (remove trailing slash)
		const normalizedOrigin = origin.replace(/\/$/, "");

		// Check for exact match (case-sensitive for protocol/host, but allow localhost variations)
		const isAllowed = allowedOrigins.some((allowed) => {
			const normalizedAllowed = allowed.replace(/\/$/, "");
			// Exact match (case-sensitive)
			if (normalizedOrigin === normalizedAllowed) {
				return true;
			}
			// Case-insensitive match for localhost/127.0.0.1
			if (normalizedOrigin.toLowerCase().includes("localhost") || normalizedOrigin.toLowerCase().includes("127.0.0.1")) {
				return normalizedOrigin.toLowerCase() === normalizedAllowed.toLowerCase();
			}
			// StartsWith for production URLs (to handle subdomains and paths)
			return normalizedOrigin.startsWith(normalizedAllowed);
		});

		if (isAllowed) {
			res.header("Access-Control-Allow-Origin", origin);
			res.header("Access-Control-Allow-Credentials", "true");
		} else if (config.backendConfig.NODE_ENV === "development") {
			// In development, allow all origins
			res.header("Access-Control-Allow-Origin", "*");
		} else {
			// In production, log but still allow for flexibility
			console.warn(`[Static CORS] Unauthorized origin: ${origin} - Allowed origins: ${allowedOrigins.join(", ")}`);
			res.header("Access-Control-Allow-Origin", origin);
			res.header("Access-Control-Allow-Credentials", "true");
		}
	} else if (config.backendConfig.NODE_ENV === "development") {
		// No origin header (e.g., Postman, mobile apps) - allow in development
		res.header("Access-Control-Allow-Origin", "*");
	} else {
		// No origin header in production - allow but log
		res.header("Access-Control-Allow-Origin", "*");
		res.header("Access-Control-Allow-Credentials", "true");
	}

	res.header("Access-Control-Allow-Methods", "GET, OPTIONS, HEAD");
	res.header("Access-Control-Allow-Headers", "Content-Type, Range, Authorization");
	res.header("Cross-Origin-Resource-Policy", "cross-origin");
	res.header("Cross-Origin-Embedder-Policy", "unsafe-none");
	res.header("Access-Control-Expose-Headers", "Content-Length, Content-Range");
	res.header("Cache-Control", "public, max-age=31536000"); // Cache static files for 1 year
	next();
};

// Proteksi akses dashboard.html dan asset dashboard (tidak mengganggu sistem lain)
// Proteksi untuk path dengan dan tanpa prefix /public/
app.use("/dashboard.html", dashboardAuth);
app.use("/dashboard-enhanced.html", dashboardAuth);
app.use("/public/dashboard.html", dashboardAuth);
app.use("/public/dashboard-enhanced.html", dashboardAuth);
app.use("/public/dashboard", dashboardAuth); // jika ada asset tambahan

// Serve dashboard.html secara eksplisit (fallback jika express.static tidak match)
app.get("/dashboard.html", (req, res, next) => {
	const fs = require("fs");
	const dashboardPath = path.join(__dirname, "public", "dashboard.html");
	if (fs.existsSync(dashboardPath)) {
		return res.sendFile(dashboardPath);
	}
	next(); // Fallback ke express.static
});

app.get("/dashboard-enhanced.html", (req, res, next) => {
	const fs = require("fs");
	const dashboardPath = path.join(__dirname, "public", "dashboard-enhanced.html");
	if (fs.existsSync(dashboardPath)) {
		return res.sendFile(dashboardPath);
	}
	next(); // Fallback ke express.static
});

const publicDataPath = path.join(__dirname, "public/data");

app.use("/avatars", staticCORS, express.static(path.join(publicDataPath, "avatars")));
app.use("/logos", staticCORS, express.static(path.join(publicDataPath, "logos")));
app.use("/documents", staticCORS, express.static(path.join(publicDataPath, "documents")));
app.use("/images", staticCORS, express.static(path.join(publicDataPath, "images")));
app.use("/videos", staticCORS, express.static(path.join(publicDataPath, "videos")));
app.use("/audios", staticCORS, express.static(path.join(publicDataPath, "audios")));
app.use("/others", staticCORS, express.static(path.join(publicDataPath, "others")));

app.use(express.static(path.join(__dirname, "public")));

// ================= API CACHE CONTROL =================
// Ensure API responses are never cached by browsers or proxies
app.use(async (req, res, next) => {
	const isApi = req.path.startsWith("/sapps/") || req.path.startsWith("/api/");
	if (isApi && req.method !== "OPTIONS") {
		res.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
		res.set("Pragma", "no-cache");
		res.set("Expires", "0");
		res.set("Surrogate-Control", "no-store");
		// Prevent cached responses tied to auth from leaking
		res.set("Vary", "Authorization");
	}
	next();
});

// =============== HEALTH CHECK & DIAGNOSTICS ===============
// Basic health check endpoint
app.get("/health", (req, res) => {
	res.status(200).json(healthCheck.getHealthStatus());
});

// Endpoint khusus untuk cPanel keep-alive (ping setiap 5 menit)
// Bisa dipanggil oleh cron job cPanel: curl -s https://sapps.sapharma.co.id/health-keepalive
app.get("/health-keepalive", (req, res) => {
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		uptime: process.uptime(),
		keepalive: true,
	});
});

// Contoh cron job cPanel (setiap 5 menit):
// */5 * * * * curl -s https://sapps.sapharma.co.id/health-keepalive > /dev/null

// Fast health check for connectivity probes
app.head("/health", (req, res) => {
	res.status(200).end();
});

// Detailed diagnostics endpoint
app.get("/diagnostics", (req, res) => {
	res.status(200).json(healthCheck.getConnectionDiagnostics(req));
});

// ISP analytics endpoint
app.get("/analytics/isp", (req, res) => {
	res.status(200).json({
		summary: ispAnalytics.getAnalyticsSummary(),
		patterns: ispAnalytics.getISPPatternReport(),
	});
});

// Network connectivity monitoring endpoint
app.get("/analytics/network-connectivity", (req, res) => {
	try {
		if (!global.networkConnectivityMonitor) {
			return res.status(503).json({
				success: false,
				message: "Network connectivity monitor not initialized",
				timestamp: new Date().toISOString(),
			});
		}

		const status = global.networkConnectivityMonitor.getStatus();
		const ispStats = global.networkConnectivityMonitor.getISPStatistics();
		const history = global.networkConnectivityMonitor.getHistory(100);

		res.status(200).json({
			success: true,
			status,
			ispStatistics: ispStats,
			recentHistory: history,
			timestamp: new Date().toISOString(),
		});
	} catch (err) {
		res.status(500).json({
			success: false,
			error: "Failed to get network connectivity status",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Quick diagnostics ping
app.get("/diagnostics/ping", (req, res) => {
	res.status(200).json({
		ok: true,
		ts: Date.now(),
		env: config.backendConfig.NODE_ENV || "development",
	});
});

// Surface DB env (safe) and readiness snapshot
app.get("/diagnostics/statuses", (req, res) => {
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		const sapps = dbSAPPS.getInitializationStatus ? dbSAPPS.getInitializationStatus() : undefined;
		res.status(200).json({
			ok: true,
			sapps,
			uptime: process.uptime(),
			dbEnv: {
				host: config.databaseConfigSAPPS.host,
				port: config.databaseConfigSAPPS.port,
				name: config.databaseConfigSAPPS.database,
				user: config.databaseConfigSAPPS.user ? "**set**" : undefined,
				ssl: config.databaseConfigSAPPS.ssl,
				sslRejectUnauthorized: config.databaseConfigSAPPS.sslRejectUnauthorized,
				timezone: config.databaseConfigSAPPS.timezone,
			},
		});
	} catch (err) {
		res.status(500).json({ ok: false, error: err.message });
	}
});

// Live DB connection diagnostic
app.get("/diagnostics/db-test", async (req, res) => {
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		const statusBefore = dbSAPPS.getInitializationStatus ? dbSAPPS.getInitializationStatus() : undefined;
		if (dbSAPPS.sequelize && dbSAPPS.sequelize.authenticate) {
			await dbSAPPS.sequelize.authenticate();
			return res.status(200).json({ ok: true, mode: "existing", status: statusBefore });
		}
		if (
			dbSAPPS.dbConnectionManager &&
			dbSAPPS.dbConnectionManager.ensureConnection &&
			(!dbSAPPS.dbConnectionManager.isConnectionManagerClosed || !dbSAPPS.dbConnectionManager.isConnectionManagerClosed())
		) {
			const conn = await dbSAPPS.dbConnectionManager.ensureConnection();
			await conn.authenticate();
			return res.status(200).json({ ok: true, mode: "ensured" });
		}
		return res.status(200).json({
			ok: true,
			note: "Connection manager not available; background retry active",
		});
	} catch (err) {
		res.status(500).json({ ok: false, error: err.message, code: err.code });
	}
});

// Deploy verification: 200 hanya jika DB siap. Untuk pengecekan pasca-deploy.
app.get("/ready", async (req, res) => {
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		const ok = dbSAPPS.isReady && dbSAPPS.isReady();
		if (ok) {
			return res.status(200).json({ ready: true, ts: Date.now() });
		}
		res.status(503).json({ ready: false, ts: Date.now() });
	} catch (err) {
		res.status(503).json({ ready: false, error: err.message, ts: Date.now() });
	}
});

// Crash recovery stats endpoint (for monitoring)
app.get("/analytics/crashes", (req, res) => {
	try {
		const stats = crashRecovery.getCrashStats();
		res.status(200).json({
			status: "ok",
			crashStats: stats,
			timestamp: new Date().toISOString(),
		});
	} catch (err) {
		res.status(500).json({
			error: "Failed to get crash stats",
			message: err.message,
		});
	}
});

// Client log forwarding endpoint (async write agar tidak blocking event loop)
app.post("/analytics/client-logs", (req, res) => {
	try {
		const fs = require("fs");
		const path = require("path");
		const logDir = path.join(__dirname, "logs");
		const logFile = path.join(logDir, "client-logs.log");
		if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
		const payload = {
			timestamp: new Date().toISOString(),
			ip: healthCheck.getClientIP(req),
			body: req.body,
		};
		fs.appendFile(logFile, JSON.stringify(payload) + "\n", "utf8", (e) => {
			if (e) console.error("[client-logs] write error:", e.message);
		});
		res.status(200).json({ status: "ok" });
	} catch (err) {
		res.status(500).json({ status: "error", message: err.message });
	}
});

// Database connection stats endpoint (for monitoring)
app.get("/analytics/database", (req, res) => {
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		if (dbSAPPS.dbConnectionManager) {
			const stats = dbSAPPS.dbConnectionManager.getConnectionStats();
			const initStatus = dbSAPPS.getInitializationStatus ? dbSAPPS.getInitializationStatus() : null;

			// Enhanced stats with initialization status
			const enhancedStats = {
				...stats,
				isReady: dbSAPPS.isReady && dbSAPPS.isReady() ? true : false,
				isInitialized: initStatus?.isInitialized || false,
				hasError: !!initStatus?.initializationError,
				error: initStatus?.initializationError
					? {
							message: initStatus.initializationError.message,
							code: initStatus.initializationError.code,
						}
					: null,
			};

			res.status(200).json({
				status: "ok",
				databaseStats: enhancedStats,
				timestamp: new Date().toISOString(),
			});
		} else {
			res.status(503).json({
				status: "error",
				message: "Database connection manager not initialized",
				timestamp: new Date().toISOString(),
			});
		}
	} catch (err) {
		res.status(500).json({
			error: "Failed to get database stats",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Circuit breaker stats endpoint (for monitoring)
app.get("/analytics/circuit-breaker", (req, res) => {
	try {
		const { getCircuitBreakerManager } = require("./utils/circuitBreaker");
		const circuitBreakerManager = getCircuitBreakerManager();
		const stats = circuitBreakerManager.getAllStats();

		res.status(200).json({
			status: "ok",
			circuitBreakerStats: stats,
			timestamp: new Date().toISOString(),
		});
	} catch (err) {
		res.status(500).json({
			status: "error",
			error: "Failed to get circuit breaker stats",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Associate monitoring endpoint (for monitoring model associations)
app.get("/analytics/associations", (req, res) => {
	try {
		const associateStatus = healthCheck.getAssociateStatus();
		const statusCode = associateStatus.status === "healthy" ? 200 : associateStatus.status === "degraded" ? 207 : 503; // 207 = Multi-Status

		res.status(statusCode).json({
			status: associateStatus.status,
			associations: associateStatus.monitoring || associateStatus,
			verification: associateStatus.verification,
			timestamp: new Date().toISOString(),
		});
	} catch (err) {
		res.status(500).json({
			status: "error",
			error: "Failed to get associate status",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Database initialization status endpoint (for debugging in cPanel)
app.get("/analytics/initialization", (req, res) => {
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		if (typeof dbSAPPS.getInitializationStatus === "function") {
			const initStatus = dbSAPPS.getInitializationStatus();
			const statusCode = initStatus.isReady ? 200 : initStatus.isInitialized ? 207 : 503;

			res.status(statusCode).json({
				status: initStatus.isReady ? "ready" : initStatus.isInitialized ? "degraded" : "not_ready",
				initialization: initStatus,
				timestamp: new Date().toISOString(),
			});
		} else {
			res.status(503).json({
				status: "error",
				message: "Initialization status function not available",
				timestamp: new Date().toISOString(),
			});
		}
	} catch (err) {
		res.status(500).json({
			status: "error",
			error: "Failed to get initialization status",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Enhanced database status endpoint for frontend (more user-friendly)
// Enhanced database status endpoint for frontend (more user-friendly)
app.get("/sapps/api/database-status", (req, res) => {
	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");
		const initStatus = dbSAPPS.getInitializationStatus ? dbSAPPS.getInitializationStatus() : null;
		const isReady = dbSAPPS.isReady && dbSAPPS.isReady();

		// Get network diagnostic info if there's an error
		let networkDiagnostic = null;
		if (initStatus?.initializationError) {
			const NetworkDiagnostics = require("./utils/networkDiagnostics");
			networkDiagnostic = NetworkDiagnostics.getNetworkErrorInfo(initStatus.initializationError);
		}

		// Get network monitor status if available
		let networkMonitorStatus = null;
		if (global.networkManager) {
			const managerStatus = global.networkManager.getStatus();
			networkMonitorStatus = {
				...managerStatus.monitor,
				diagnostic: managerStatus.diagnostic,
				autoRecovery: managerStatus.autoRecovery,
			};
		} else if (global.networkMonitor) {
			const monitorStatus = global.networkMonitor.getStatus();
			networkMonitorStatus = {
				isOnline: monitorStatus.isOnline,
				isOffline: monitorStatus.isOffline,
				lastCheck: monitorStatus.lastCheck,
				lastSuccess: monitorStatus.lastSuccess,
				lastFailure: monitorStatus.lastFailure,
				consecutiveFailures: monitorStatus.consecutiveFailures,
				currentError: monitorStatus.currentError,
				diagnostic: global.networkMonitor.getDiagnostic(),
			};
		}

		res.status(200).json({
			success: true,
			isReady,
			isInitialized: initStatus?.isInitialized || false,
			hasError: !!initStatus?.initializationError,
			error: initStatus?.initializationError
				? {
						message: initStatus.initializationError.message,
						code: initStatus.initializationError.code,
					}
				: null,
			networkDiagnostic,
			networkMonitor: networkMonitorStatus,
		});
	} catch (err) {
		res.status(500).json({
			success: false,
			error: err.message,
		});
	}
});

// Network diagnostic endpoint
app.get("/diagnostics/network", async (req, res) => {
	try {
		const NetworkDiagnostics = require("./utils/networkDiagnostics");
		const host = config.databaseConfigSAPPS.host;
		const port = config.databaseConfigSAPPS.port;

		const diagnostic = await NetworkDiagnostics.diagnose(host, port, 10000);

		// Include network monitor status if available
		if (global.networkManager) {
			diagnostic.managerStatus = global.networkManager.getStatus();
		} else if (global.networkMonitor) {
			diagnostic.monitorStatus = global.networkMonitor.getStatus();
		}

		const statusCode = diagnostic.success ? 200 : 503;
		res.status(statusCode).json(diagnostic);
	} catch (err) {
		res.status(500).json({
			success: false,
			error: "Failed to run network diagnostic",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Network status endpoint (from monitor)
app.get("/diagnostics/network-status", (req, res) => {
	try {
		if (!global.networkManager && !global.networkMonitor) {
			return res.status(503).json({
				success: false,
				message: "Network manager/monitor not initialized",
				timestamp: new Date().toISOString(),
			});
		}

		let status, diagnostic;
		if (global.networkManager) {
			const managerStatus = global.networkManager.getStatus();
			status = managerStatus.monitor || managerStatus;
			diagnostic = managerStatus.diagnostic;
		} else {
			status = global.networkMonitor.getStatus();
			diagnostic = global.networkMonitor.getDiagnostic();
		}

		res.status(200).json({
			success: true,
			status: {
				isOnline: status.isOnline,
				isOffline: status.isOffline,
				status: status.isOnline === true ? "online" : status.isOnline === false ? "offline" : "unknown",
				lastCheck: status.lastCheck,
				lastSuccess: status.lastSuccess,
				lastFailure: status.lastFailure,
				consecutiveSuccesses: status.consecutiveSuccesses,
				consecutiveFailures: status.consecutiveFailures,
				totalChecks: status.totalChecks,
				totalSuccesses: status.totalSuccesses,
				totalFailures: status.totalFailures,
				isMonitoring: status.isMonitoring,
			},
			diagnostic: diagnostic || null,
			currentError: status.currentError,
			timestamp: new Date().toISOString(),
		});
	} catch (err) {
		res.status(500).json({
			success: false,
			error: "Failed to get network status",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Force network check endpoint
app.post("/diagnostics/network-check", async (req, res) => {
	try {
		if (!global.networkManager && !global.networkMonitor) {
			return res.status(503).json({
				success: false,
				message: "Network manager/monitor not initialized",
				timestamp: new Date().toISOString(),
			});
		}

		const result = global.networkManager ? await global.networkManager.forceCheck() : await global.networkMonitor.forceCheck();

		res.status(result.success ? 200 : 503).json({
			success: result.success,
			result,
			timestamp: new Date().toISOString(),
		});
	} catch (err) {
		res.status(500).json({
			success: false,
			error: "Failed to force network check",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Comprehensive system health check endpoint (combines all diagnostics)
app.get("/diagnostics/system-health", async (req, res) => {
	try {
		const health = {
			timestamp: new Date().toISOString(),
			server: {
				status: "running",
				uptime: Math.floor(process.uptime()),
				environment: config.backendConfig.NODE_ENV || "development",
				port: START_PORT,
			},
			database: null,
			network: null,
			overall: {
				status: "unknown",
				healthy: false,
				issues: [],
			},
		};

		// Database status
		try {
			const dbSAPPS = require("./apps/sapps/helpers/db");
			const initStatus = dbSAPPS.getInitializationStatus ? dbSAPPS.getInitializationStatus() : null;
			const isReady = dbSAPPS.isReady && dbSAPPS.isReady();

			let networkDiagnostic = null;
			if (initStatus?.initializationError) {
				const NetworkDiagnostics = require("./utils/networkDiagnostics");
				networkDiagnostic = NetworkDiagnostics.getNetworkErrorInfo(initStatus.initializationError);
			}

			health.database = {
				status: isReady ? "ready" : initStatus?.isInitialized ? "initializing" : "not_ready",
				isReady,
				isInitialized: initStatus?.isInitialized || false,
				hasError: !!initStatus?.initializationError,
				error: initStatus?.initializationError
					? {
							message: initStatus.initializationError.message,
							code: initStatus.initializationError.code,
							networkDiagnostic,
						}
					: null,
			};

			if (!isReady) {
				health.overall.issues.push({
					component: "database",
					severity: "critical",
					message: networkDiagnostic?.userMessage || initStatus?.initializationError?.message || "Database is not ready",
					suggestion: networkDiagnostic?.suggestion || "Please check database configuration and network connectivity",
				});
			}
		} catch (dbError) {
			health.database = {
				status: "error",
				error: dbError.message,
			};
			health.overall.issues.push({
				component: "database",
				severity: "critical",
				message: `Database check failed: ${dbError.message}`,
			});
		}

		// Network status
		try {
			if (global.networkManager) {
				const managerStatus = global.networkManager.getStatus();
				const monitorStatus = managerStatus.monitor || managerStatus;
				const diagnostic = managerStatus.diagnostic;

				health.network = {
					status: monitorStatus.isOnline === true ? "online" : monitorStatus.isOnline === false ? "offline" : "unknown",
					isOnline: monitorStatus.isOnline,
					isOffline: monitorStatus.isOffline,
					lastCheck: monitorStatus.lastCheck,
					lastSuccess: monitorStatus.lastSuccess,
					lastFailure: monitorStatus.lastFailure,
					consecutiveSuccesses: monitorStatus.consecutiveSuccesses,
					consecutiveFailures: monitorStatus.consecutiveFailures,
					totalChecks: monitorStatus.totalChecks,
					totalSuccesses: monitorStatus.totalSuccesses,
					totalFailures: monitorStatus.totalFailures,
					currentError: monitorStatus.currentError,
					diagnostic: diagnostic || null,
					isMonitoring: monitorStatus.isMonitoring,
					autoRecovery: managerStatus.autoRecovery || null,
				};

				if (monitorStatus.isOnline === false) {
					health.overall.issues.push({
						component: "network",
						severity: "critical",
						message: diagnostic?.userMessage || "Network connectivity to database server is offline",
						suggestion: diagnostic?.suggestion || "Please check network connectivity and firewall rules",
					});
				} else if (monitorStatus.isOnline === null) {
					health.overall.issues.push({
						component: "network",
						severity: "warning",
						message: "Network status is unknown",
						suggestion: "Network monitoring is still initializing",
					});
				}
			} else if (global.networkMonitor) {
				// Fallback to old monitor if available
				const monitorStatus = global.networkMonitor.getStatus();
				const diagnostic = global.networkMonitor.getDiagnostic();

				health.network = {
					status: monitorStatus.isOnline === true ? "online" : monitorStatus.isOnline === false ? "offline" : "unknown",
					isOnline: monitorStatus.isOnline,
					isOffline: monitorStatus.isOffline,
					lastCheck: monitorStatus.lastCheck,
					diagnostic: diagnostic || null,
					isMonitoring: monitorStatus.isMonitoring,
				};
			} else {
				health.network = {
					status: "not_monitored",
					message: "Network manager/monitor not initialized",
				};
				health.overall.issues.push({
					component: "network",
					severity: "warning",
					message: "Network monitoring is not active",
				});
			}
		} catch (netError) {
			health.network = {
				status: "error",
				error: netError.message,
			};
			health.overall.issues.push({
				component: "network",
				severity: "warning",
				message: `Network check failed: ${netError.message}`,
			});
		}

		// Determine overall health
		const hasCriticalIssues = health.overall.issues.some((issue) => issue.severity === "critical");
		const hasWarnings = health.overall.issues.some((issue) => issue.severity === "warning");

		if (hasCriticalIssues) {
			health.overall.status = "unhealthy";
			health.overall.healthy = false;
		} else if (hasWarnings) {
			health.overall.status = "degraded";
			health.overall.healthy = false;
		} else {
			health.overall.status = "healthy";
			health.overall.healthy = true;
		}

		const statusCode = health.overall.healthy ? 200 : hasCriticalIssues ? 503 : 207; // 207 = Multi-Status
		res.status(statusCode).json(health);
	} catch (err) {
		res.status(500).json({
			success: false,
			error: "Failed to get system health",
			message: err.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// Simple ping endpoint for connectivity tests
app.get("/ping", (req, res) => {
	res.status(200).json({
		status: "ok",
		message: "pong",
		timestamp: new Date().toISOString(),
		yourIP: healthCheck.getClientIP(req),
	});
});

// =============== DATABASE READINESS CHECK ===============
// Middleware to ensure database is initialized before processing requests
// IMPROVED: More graceful handling with better error messages and status information
app.use(async (req, res, next) => {
	// Skip health check endpoints and statuses (uses in-memory cache)
	if (
		req.path === "/health" ||
		req.path === "/ping" ||
		req.path === "/diagnostics" ||
		req.path.startsWith("/analytics") ||
		req.path === "/sapps/api/statuses" ||
		req.path === "/sapps/doc-types/option"
	) {
		return next();
	}

	// Always allow CORS preflight to pass
	if (req.method === "OPTIONS") {
		return next();
	}

	const startTime = Date.now();
	let timeoutTriggered = false;
	const TIMEOUT_MS = 8000; // Reduced to 8 seconds for faster failure detection
	const timer = setTimeout(() => {
		timeoutTriggered = true;
		console.error(`[Readiness Check][TIMEOUT] Request to ${req.method} ${req.path} waiting DB readiness > ${TIMEOUT_MS}ms`);
	}, TIMEOUT_MS);

	try {
		const dbSAPPS = require("./apps/sapps/helpers/db");

		// Get initialization status for better error messages
		const initStatus = dbSAPPS.getInitializationStatus ? dbSAPPS.getInitializationStatus() : null;
		const isReady = dbSAPPS.isReady && dbSAPPS.isReady();

		// If not ready, try a quick on-demand connection before failing
		if (!isReady) {
			try {
				if (
					dbSAPPS.dbConnectionManager &&
					dbSAPPS.dbConnectionManager.ensureConnection &&
					(!dbSAPPS.dbConnectionManager.isConnectionManagerClosed || !dbSAPPS.dbConnectionManager.isConnectionManagerClosed())
				) {
					// Attempt a fast ensure (up to ~5s inside helper)
					await dbSAPPS.dbConnectionManager.ensureConnection();
					// Re-check after ensureConnection
					if (dbSAPPS.isReady && dbSAPPS.isReady()) {
						clearTimeout(timer);
						if (!timeoutTriggered) {
							console.log(`[Readiness Check][${req.method} ${req.path}] PASSED (after ensureConnection) in ${Date.now() - startTime}ms`);
							return next();
						}
					}
				}
			} catch (e) {
				// Ignore errors if connection manager is closed
				if (e.message && !e.message.includes("closed")) {
					console.warn(`[Readiness Check] ensureConnection failed: ${e.message}`);
				}
			}

			// Still not ready after retry
			if (!dbSAPPS.isReady || !dbSAPPS.isReady()) {
				console.warn(`[Readiness Check] Database not ready for request: ${req.method} ${req.path}`);
				clearTimeout(timer);

				// Provide detailed error information
				const errorInfo = {
					success: false,
					message: "Database is not ready. Please check database connection and try again.",
					status: "database_not_ready",
					timestamp: new Date().toISOString(),
					details: {
						isInitialized: initStatus?.isInitialized || false,
						isReady: false,
						hasError: !!initStatus?.initializationError,
						errorMessage: initStatus?.initializationError?.message || null,
						suggestion: initStatus?.initializationError
							? "Database connection failed. Please check database configuration and network connectivity."
							: "Database is still initializing. This may take a few moments.",
					},
				};

				// Add retry-after header to help frontend with backoff
				res.set("Retry-After", "5"); // Suggest retry after 5 seconds
				res.set("X-Database-Status", "not_ready");

				return res.status(503).json(errorInfo);
			}
		}

		// Database is ready, proceed
		clearTimeout(timer);
		if (!timeoutTriggered) {
			// Only log if it took significant time (>100ms)
			if (Date.now() - startTime > 100) {
				console.log(`[Readiness Check][${req.method} ${req.path}] PASSED in ${Date.now() - startTime}ms`);
			}
			next();
		} else {
			// Timeout was triggered but DB became ready - return timeout error
			res.set("Retry-After", String(config.backendConfig?.retryAfterSeconds ?? 30));
			return res.status(503).json({
				success: false,
				message: "Database readiness check timeout. Please check DB connection.",
				status: "timeout",
				timestamp: new Date().toISOString(),
			});
		}
	} catch (error) {
		clearTimeout(timer);
		console.error("[Readiness Check] Error checking database readiness:", error);

		// Provide detailed error information
		const errorInfo = {
			success: false,
			message: "Database initialization error. Please contact administrator.",
			status: "database_error",
			error: error.message,
			timestamp: new Date().toISOString(),
			details: {
				errorType: error.name || "UnknownError",
				errorMessage: error.message,
				suggestion: "Please check database configuration, network connectivity, and server logs.",
			},
		};

		res.set("Retry-After", String(config.backendConfig?.retryAfterSeconds ?? 30));
		res.set("X-Database-Status", "error");
		return res.status(503).json(errorInfo);
	}
});

// IP Analytics Routes (setelah database readiness agar hanya dijalankan saat DB siap)
const ipAnalyticsRoutes = require("./apps/analytics/ip-analytics.routes");
app.use("/api/ip-analytics", ipAnalyticsRoutes);

// Top 10 IP Access Endpoint (lightweight, no DB required)
app.get("/analytics/top-accessing-ips", (req, res) => {
	try {
		const limit = parseInt(req.query.limit) || 10;
		const topIPs = healthCheck.getTopAccessingIPs(limit);

		res.status(200).json({
			success: true,
			data: topIPs,
			count: topIPs.length,
			timestamp: new Date().toISOString(),
		});
	} catch (error) {
		console.error("[Analytics] Error getting top accessing IPs:", error);
		res.status(500).json({
			success: false,
			error: "Failed to get top accessing IPs",
			message: error.message,
			timestamp: new Date().toISOString(),
		});
	}
});

// =============== API ROUTES ===============
// app.use("/api/whatsapp", require("./apps/sapps/services/Whatsapps/whatsapps.controller"));

// main
app.use("/sapps/api", require("./apps/sapps/masters/statuses/statuses.controller"));

// Logging login request untuk analisis timeout

// Logging login request hanya jika terjadi error (status >= 400)
app.use("/sapps/users/login", async (req, res, next) => {
	// Tangkap response status setelah response selesai
	const fs = require("fs");
	const path = require("path");
	const logDir = path.join(__dirname, "logs");
	const loginLogFile = path.join(logDir, "login-requests.log");
	if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
	// Simpan data request untuk log error
	const payload = {
		timestamp: new Date().toISOString(),
		ip: healthCheck.getClientIP ? healthCheck.getClientIP(req) : req.ip,
		body: req.body,
		headers: req.headers,
		path: req.path,
		method: req.method,
	};
	// Hook response finish (async write agar tidak blocking)
	res.on("finish", () => {
		if (res.statusCode >= 400) {
			fs.appendFile(loginLogFile, JSON.stringify({ ...payload, statusCode: res.statusCode }) + "\n", "utf8", (e) => {
				if (e) console.error("[login-requests] write error:", e.message);
			});
		}
	});
	next();
});

// sapps
app.use("/sapps/users", require("./apps/sapps/masters/users/users.controller"));
app.use("/sapps/user-activities", require("./apps/sapps/masters/userActivites/userActivities.controller"));
app.use("/sapps/employees", require("./apps/sapps/masters/employees/employees.controller"));
app.use("/sapps/companies", require("./apps/sapps/masters/companies/companies.controller"));
app.use("/sapps/menus", require("./apps/sapps/masters/menus/menus.controller"));
app.use("/sapps/roles", require("./apps/sapps/masters/roles/roles.controller"));
app.use("/sapps/doc-types", require("./apps/sapps/masters/docTypes/docTypes.controller"));
app.use("/sapps/permissions", require("./apps/sapps/masters/permissions/permissions.controller"));
app.use("/sapps/projects", require("./apps/sapps/masters/projects/projects.controller"));
app.use("/sapps/images", require("./apps/sapps/masters/images/images.contoller"));
app.use("/sapps/documents", require("./apps/sapps/masters/documents/documents.controller"));
app.use("/sapps/videos", require("./apps/sapps/masters/videos/videos.controller"));
app.use("/sapps/emails", require("./apps/sapps/masters/emails/emails.controller"));
app.use("/sapps/email-accounts", require("./apps/sapps/masters/emailAccounts/emailAccounts.controller"));
app.use("/sapps/history-emails", require("./apps/sapps/histories/emails/historyEmail.controller"));
app.use("/sapps/developments", require("./apps/sapps/masters/developments/developments.controller"));
app.use("/sapps/apikeys", require("./apps/sapps/masters/apikeys/apikeys.controller"));
app.use("/sapps/erps", require("./apps/sapps/masters/erps/erps.controller"));
app.use("/sapps/provinces", require("./apps/sapps/masters/provinces/provinces.controller"));
app.use("/sapps/cities", require("./apps/sapps/masters/cities/cities.controller"));
app.use("/sapps/years", require("./apps/sapps/masters/years/years.controller"));
app.use("/sapps/periods", require("./apps/sapps/masters/periods/periods.controller"));
app.use("/sapps/client-types", require("./apps/sapps/masters/clientTypes/clientTypes.controller"));
app.use("/sapps/client-subtypes", require("./apps/sapps/masters/clientSubtypes/clientSubtypes.controller"));
app.use("/sapps/specialists", require("./apps/sapps/masters/specialists/specialists.controller"));
app.use("/sapps/faculties", require("./apps/sapps/masters/faculties/faculties.controller"));
app.use("/sapps/majors", require("./apps/sapps/masters/majors/majors.controller"));
app.use("/sapps/sub-specialists", require("./apps/sapps/masters/subSpecialists/subSpecialists.controller"));
app.use("/sapps/outlet-types", require("./apps/sapps/masters/outletTypes/outletTypes.controller"));
app.use("/sapps/institutions", require("./apps/sapps/masters/institutions/institutions.controller"));
app.use("/sapps/clients", require("./apps/sapps/masters/clients/clients.controller"));
app.use("/sapps/territory-versions", require("./apps/sapps/masters/territoryVersions/territoryVersions.controller"));
app.use("/sapps/territories", require("./apps/sapps/masters/territories/territories.controller"));
app.use("/sapps/outlets", require("./apps/sapps/masters/outlets/outlets.controller"));
app.use("/sapps/departments", require("./apps/sapps/masters/departments/departments.controller"));
app.use("/sapps/designations", require("./apps/sapps/masters/designations/designations.controller"));
app.use("/sapps/holiday-groups", require("./apps/sapps/masters/holidayGroups/holidayGroups.controller"));
app.use("/sapps/holidays", require("./apps/sapps/masters/holidays/holidays.controller"));
app.use("/sapps/item-groups", require("./apps/sapps/masters/itemGroups/itemGroups.controller"));
app.use("/sapps/items", require("./apps/sapps/masters/items/items.controller"));
app.use("/sapps/item-competitors", require("./apps/sapps/masters/itemCompetitors/itemCompetitors.controller"));
app.use("/sapps/question-banks", require("./apps/sapps/masters/questionBanks/questionBanks.controller"));
app.use("/sapps/delivery-trips", require("./apps/sapps/masters/deliveryTrips/deliveryTrips.controller"));
app.use("/sapps/expeditions", require("./apps/sapps/masters/expeditions/expeditions.controller"));
app.use("/sapps/dms-legals", require("./apps/sapps/masters/dmsLegals/dmsLegals.controller"));
app.use("/sapps/courses", require("./apps/sapps/masters/courses/courses.controller"));
app.use("/sapps/questions", require("./apps/sapps/masters/questions/questions.controller"));
app.use("/sapps/project-managements", require("./apps/sapps/masters/projectManagements/projectManagements.controller"));
app.use("/sapps/vehicles", require("./apps/sapps/masters/vehicles/vehicles.controller"));
app.use("/sapps/workplan-monitors", require("./apps/sapps/masters/workplanMonitors/workplanMonitors.controller"));
app.use("/sapps/project-management", require("./apps/sapps/masters/projectManagements/projectManagements.controller"));
app.use("/sapps/call-plans", require("./apps/sapps/masters/callPlans/callPlans.controller"));
app.use("/sapps/moms", require("./apps/sapps/masters/moms/moms.controller"));
app.use("/sapps/designation-versions", require("./apps/sapps/masters/designationVersions/designationVersions.controller"));
app.use("/sapps/workflow-settings", require("./apps/sapps/masters/workflowSettings/workflowSettings.controller"));
app.use("/sapps/workflows", require("./apps/sapps/masters/workflows/workflows.controller"));
app.use("/sapps/notifications", require("./apps/sapps/masters/notifications/notifications.controller"));
app.use("/sapps/employee-checkins", require("./apps/sapps/masters/employeeCheckins/employeeCheckins.controller"));
app.use("/sapps/attendances", require("./apps/sapps/masters/attendances/attendances.controller"));
app.use("/sapps/attendance-requests", require("./apps/sapps/masters/attendanceRequests/attendanceRequests.controller"));
app.use("/sapps/leave-types", require("./apps/sapps/masters/leaveTypes/leaveTypes.controller"));
app.use("/sapps/leave-applications", require("./apps/sapps/masters/leaveApplications/leaveApplications.controller"));
app.use("/sapps/shift-types", require("./apps/sapps/masters/shiftTypes/shiftTypes.controller"));
app.use("/sapps/distributors", require("./apps/sapps/masters/distributors/distributors.controller"));
app.use("/sapps/customer-lists", require("./apps/sapps/masters/customerLists/customerLists.controller"));
app.use("/sapps/udks", require("./apps/sapps/masters/udks/udks.controller"));
app.use("/sapps/applicants", require("./apps/sapps/masters/applicants/applicants.controller"));
app.use("/sapps/employee-requests", require("./apps/sapps/masters/employeeRequests/employeeRequests.controller"));
app.use("/sapps/absence-clarifications", require("./apps/sapps/masters/absenceClarifications/absenceClarifications.controller"));
app.use("/sapps/sales-distributors", require("./apps/sapps/masters/salesDistributors/salesDistributor.controller"));
app.use("/sapps/permit-eases", require("./apps/sapps/masters/permitEases/permitEases.controller"));
app.use("/sapps/leave-allocations", require("./apps/sapps/masters/leaveAllocations/leaveAllocations.controller"));
app.use("/sapps/client-collabs", require("./apps/sapps/masters/clientCollabs/clientCollabs.controller"));
app.use("/sapps/surveys", require("./apps/sapps/masters/surveys/surveys.controller"));
app.use("/sapps/account-entries", require("./apps/sapps/masters/accountEntries/accountEntries.controller"));
app.use("/sapps/receipt-of-goods", require("./apps/sapps/masters/receiptOfGoods/receiptOfGoods.controller"));
app.use("/sapps/udps", require("./apps/sapps/masters/udps/udps.controller"));
app.use("/sapps/sales-targets", require("./apps/sapps/masters/salesTargets/salesTargets.controller"));
app.use("/sapps/internal-audits", require("./apps/sapps/masters/internalAudits/internalAudits.controller"));
app.use("/sapps/work-instructions", require("./apps/sapps/masters/workInstructions/workInstructions.controller"));
app.use("/sapps/job-postings", require("./apps/sapps/masters/jobPostings/jobPostings.controller"));
app.use("/sapps/nc-products", require("./apps/sapps/masters/ncProducts/ncProducts.controller"));
app.use("/sapps/product-returns", require("./apps/sapps/masters/productReturns/productReturns.controller"));
app.use("/sapps/comments", require("./apps/sapps/masters/comments/comments.controller"));
app.use("/sapps/sales-plans", require("./apps/sapps/masters/salesPlans/salesPlans.controller"));
app.use("/sapps/invoice-receipts", require("./apps/sapps/masters/invoiceReceipts/invoiceReceipts.controller"));
app.use("/sapps/fses", require("./apps/sapps/masters/fses/fses.controller"));
app.use("/sapps/gimmick-req-types", require("./apps/sapps/masters/gimmickReqTypes/gimmickReqTypes.controller"));
app.use("/sapps/employee-statuses", require("./apps/sapps/masters/employeeStatuses/employeeStatuses.controller"));
app.use("/sapps/job-roles", require("./apps/sapps/masters/jobRoles/jobRoles.controller"));
app.use("/sapps/genders", require("./apps/sapps/masters/genders/genders.controller"));
app.use("/sapps/religions", require("./apps/sapps/masters/religions/religions.controller"));
app.use("/sapps/bank-accounts", require("./apps/sapps/masters/bankAccounts/bankAccounts.controller"));
app.use("/sapps/marital-statuses", require("./apps/sapps/masters/maritalStatuses/maritalStatuses.controller"));
app.use("/sapps/gimmick-requests", require("./apps/sapps/masters/gimmickRequests/gimmickRequests.controller"));
app.use("/sapps/banks", require("./apps/sapps/masters/banks/banks.controller"));
app.use("/sapps/employee-grades", require("./apps/sapps/masters/employeeGrades/employeeGrades.controller"));
app.use("/sapps/employee-groups", require("./apps/sapps/masters/employeeGroups/employeeGroups.controller"));
app.use("/sapps/email-groups", require("./apps/sapps/masters/emailGroups/emailGroups.controller"));
app.use("/sapps/employment-types", require("./apps/sapps/masters/employmentTypes/employmentTypes.controller"));
app.use("/sapps/branches", require("./apps/sapps/masters/branches/branches.controller"));
app.use("/sapps/leave-periods", require("./apps/sapps/masters/leavePeriods/leavePeriods.controller"));
app.use("/sapps/holiday-lists", require("./apps/sapps/masters/holidayLists/holidayLists.controller"));
app.use("/sapps/reg-categories", require("./apps/sapps/masters/regulatoryCategories/regulatoryCategories.controller"));
app.use("/sapps/document-groups", require("./apps/sapps/masters/documentGroups/documentGroups.controller"));
app.use("/sapps/travel-cost-rates", require("./apps/sapps/masters/travelCostRates/travelCostRates.controller"));
app.use("/sapps/dms-regulatories", require("./apps/sapps/masters/dmsRegulatories/dmsRegulatories.controller"));
app.use("/sapps/tracking-locations", require("./apps/sapps/masters/trackingLocations/trackingLocations.controller"));
app.use("/sapps/uc-settings", require("./apps/sapps/masters/ucSettings/ucSettings.controller"));
app.use("/sapps/blood-groups", require("./apps/sapps/masters/bloodGroups/bloodGroups.controller"));
app.use("/sapps/asset-categories", require("./apps/sapps/masters/assetCategories/assetCategories.controller"));
app.use("/sapps/asset-manufacturers", require("./apps/sapps/masters/assetManufacturers/assetManufacturers.controller"));
app.use("/sapps/asset-status-labels", require("./apps/sapps/masters/assetStatusLabels/assetStatusLabels.controller"));
app.use("/sapps/asset-locations", require("./apps/sapps/masters/assetLocations/assetLocations.controller"));
app.use("/sapps/asset-models", require("./apps/sapps/masters/assetModels/assetModels.controller"));
app.use("/sapps/education-levels", require("./apps/sapps/masters/educationLevels/educationLevels.controller"));
app.use("/sapps/placement-areas", require("./apps/sapps/masters/placementAreas/placementAreas.controller"));
app.use("/sapps/zones", require("./apps/sapps/masters/zones/zones.controller"));
app.use("/sapps/nationalities", require("./apps/sapps/masters/nationalities/nationalities.controller"));
app.use("/sapps/occupations", require("./apps/sapps/masters/occupations/occupations.controller"));
app.use("/sapps/up-countries", require("./apps/sapps/masters/upCountries/upCountries.controller"));
app.use("/sapps/uc-cost-realizations", require("./apps/sapps/masters/ucCostRealizations/ucCostRealizations.controller"));
app.use("/sapps/release-notes", require("./apps/sapps/masters/releaseNotes/releaseNotes.controller"));
app.use("/sapps/good-receipts", require("./apps/sapps/masters/goodReceipts/goodReceipts.controller")); // yang mana yang dipakai
app.use("/sapps/goods-receipts", require("./apps/sapps/masters/goodsReceipts/goodsReceipts.controller")); // yang mana yang dipakai
app.use("/sapps/budgetings", require("./apps/sapps/masters/budgetings/budgetings.controller"));
app.use("/sapps/procurement-trackers", require("./apps/sapps/masters/procurementTrackers/procurementTrackers.controller"));
app.use("/sapps/work-activities", require("./apps/sapps/masters/workActivities/workActivities.controller"));
app.use("/sapps/work-activity-plans", require("./apps/sapps/masters/workActivityPlans/workActivityPlans.controller"));
app.use("/sapps/accounts", require("./apps/sapps/masters/accounts/accounts.controller"));
app.use("/sapps/budget-categories", require("./apps/sapps/masters/budgetCategories/budgetCategories.controller"));
app.use("/sapps/budget-classifications", require("./apps/sapps/masters/budgetClassifications/budgetClassifications.controller"));
app.use("/sapps/budget-departments", require("./apps/sapps/masters/budgetDepartments/budgetDepartments.controller"));
app.use("/sapps/budget-items", require("./apps/sapps/masters/budgetItems/budgetItems.controller"));
app.use("/sapps/budget-accounts", require("./apps/sapps/masters/budgetAccounts/budgetAccounts.controller"));
app.use("/sapps/budget-unit-of-measurements", require("./apps/sapps/masters/budgetUnitOfMeasurements/budgetUnitOfMeasurements.controller"));
app.use("/sapps/budget-plans", require("./apps/sapps/masters/budgetPlans/budgetPlans.controller"));
app.use("/sapps/budget-approvers", require("./apps/sapps/masters/budgetApprovers/budgetApprovers.controller"));
app.use("/sapps/saldo-awal-clients", require("./apps/sapps/masters/saldoAwalClients/saldoAwalClients.controller"));
app.use("/sapps/tenders", require("./apps/sapps/masters/tenders/tenders.controller"));
app.use("/sapps/tender-quotations", require("./apps/sapps/masters/tenders/tenderQuotations.controller"));
app.use("/sapps/tender-categories", require("./apps/sapps/masters/tenderCategories/tenderCategories.controller"));
app.use("/sapps/tender-vendors", require("./apps/sapps/masters/tenderVendors/tenderVendors.controller"));
app.use("/sapps/tender-payment-terms", require("./apps/sapps/masters/tenderPaymentTerms/tenderPaymentTerms.controller"));

// dashboard
app.use("/sapps/dashboards-employee-checkins", require("./apps/sapps/dashboards/employeeCheckins/employeeCheckins.controller"));
app.use("/sapps/dashboards-accountings", require("./apps/sapps/dashboards/accountings/accountings.controller"));
app.use("/sapps/dashboards-leave-allocations", require("./apps/sapps/dashboards/leaveAllocations/leaveAllocations.controller"));
app.use("/sapps/dashboards-leave-applications", require("./apps/sapps/dashboards/leaveApplications/leaveApplications.controller"));
app.use("/sapps/dashboards-attendance-requests", require("./apps/sapps/dashboards/attendanceRequests/attendanceRequests.controller"));
app.use("/sapps/dashboards-absence-clarifications", require("./apps/sapps/dashboards/absenceClarifications/absenceClarifications.controller"));
app.use("/sapps/dashboards-permit-eases", require("./apps/sapps/dashboards/permitEases/permitEases.controller"));
app.use("/sapps/dashboards-work-activities", require("./apps/sapps/dashboards/workActivities/workActivities.controller"));
app.use("/sapps/dashboards-work-activity-plans", require("./apps/sapps/dashboards/workActivityPlans/workActivityPlans.controller"));

// junction
app.use("/sapps/role-menu", require("./apps/sapps/junctions/roleMenus/roleMenus.controller"));
app.use("/sapps/user-company", require("./apps/sapps/junctions/userCompanies/userCompanies.controller"));
app.use("/sapps/employee-territory", require("./apps/sapps/junctions/employeeTerritories/employeeTerritories.controller"));
app.use("/sapps/territory-itemgroup", require("./apps/sapps/junctions/territoryItemGroups/territoryItemGroups.controller"));
app.use("/sapps/outlet-client", require("./apps/sapps/junctions/outletClients/outletClients.controller"));
app.use("/sapps/territory-outlet", require("./apps/sapps/junctions/territoryOutlet/territoryOutlets.controller"));
app.use("/sapps/master-lists", require("./apps/sapps/junctions/masterLists/masterLists.controller"));

// =============== NON-BLOCKING STARTUP ARCHITECTURE ===============
// Global system state
global.isSystemReady = false;
global.startupStatus = {
	step: "starting",
	details: "Server process started",
	timestamp: new Date().toISOString(),
};

// Serve static files (dashboard.html, dll) SEBELUM startup middleware
// Agar file statis bisa diakses meskipun system belum ready
// express.static sudah dipasang di line 545, tapi pastikan tidak diblokir
// Dashboard auth sudah dipasang di line 530-534

// Middleware to handle requests during startup
// Returns 503 Service Unavailable until system is ready
app.use((req, res, next) => {
	// Always allow these paths even during startup
	if (
		global.isSystemReady ||
		req.path === "/health" ||
		req.path === "/diagnostics/system-health" ||
		req.path === "/dashboard.html" ||
		req.path === "/dashboard-enhanced.html" ||
		req.path.startsWith("/public") ||
		req.path.startsWith("/assets") ||
		req.path.startsWith("/avatars") ||
		req.path.startsWith("/logos") ||
		req.path.startsWith("/documents") ||
		req.path.startsWith("/images") ||
		req.path.startsWith("/videos") ||
		req.path.startsWith("/audios") ||
		req.path.startsWith("/others") ||
		req.method === "OPTIONS"
	) {
		return next();
	}

	// Degraded mode handling
	if (global.isDegradedMode) {
		// In degraded mode, we might want to warn but allow traffic
		// Or specific endpoints might need to check DB status themselves
		return next();
	}

	res.status(503).json({
		success: false,
		message: "System is initializing",
		status: global.startupStatus,
		retryAfter: 5, // seconds
	});
});

// Reports
app.use("/sapps/reports", require("./apps/sapps/reports/reports.controller"));

// Imports
app.use("/sapps/imports", require("./apps/sapps/imports/imports.controller"));

// Additional
app.use("/sapps/additional-designations", require("./apps/sapps/additionals/designations/designations.controller"));
app.use("/sapps/additional-territories", require("./apps/sapps/additionals/territories/territories.controller"));

// Integration
// ERPNext
app.use("/sapps/integrations/erpnext/accounts", require("./apps/sapps/integrations/erpnext/accounts/accounts.controller"));
app.use("/sapps/integrations/erpnext/items", require("./apps/sapps/integrations/erpnext/items/items.controller"));
app.use("/sapps/integrations/erpnext/departments", require("./apps/sapps/integrations/erpnext/departments/departments.controller"));

// =============== ERROR HANDLER ====================
// Handle connection errors first
app.use(connectionResilience.handleConnectionErrors);

// Global error handler
app.use(errorHandler);

// =============== SERVER STARTUP ====================
let serverInstance = null;
// Menggunakan config.json (tidak pakai env)
const START_PORT = config.backendConfig.port ? parseInt(config.backendConfig.port, 10) : 3000;

const LISTEN_RETRY_DELAY_MS = 750;
const MAX_LISTEN_RETRIES = 20;

function startServer(port, attempt = 0) {
	// 1. LISTEN IMMEDIATELY
	// This satisfies Passenger/cPanel timeout requirements
	serverInstance = app.listen(port, () => {
		console.log("========================================");
		console.log(`[FAST START] Server listening on port ${port}`);
		console.log(`[FAST START] PID: ${process.pid}`);
		console.log(`[FAST START] Environment: ${config.backendConfig.NODE_ENV || "development"}`);
		console.log("========================================");

		// 2. TRIGGER BACKGROUND INITIALIZATION
		initializeSystem().catch((err) => {
			console.error("CRITICAL: Background initialization failed:", err);
			// We do NOT exit, we stay in degraded state to allow debugging via /health
		});
	});

	// Handle server errors
	serverInstance.on("error", (err) => {
		if (err && err.code === "EADDRINUSE" && attempt < MAX_LISTEN_RETRIES) {
			console.warn(`[FAST START] Port ${port} is in use (attempt ${attempt + 1}/${MAX_LISTEN_RETRIES}). Retrying in ${LISTEN_RETRY_DELAY_MS}ms...`);
			try {
				serverInstance.close();
			} catch (_) {}
			setTimeout(() => startServer(port, attempt + 1), LISTEN_RETRY_DELAY_MS);
			return;
		}

		console.error("Server fatal error:", err);
		crashRecovery.logCrash(err, { type: "serverStartError" });
		process.exit(1);
	});
}

startServer(START_PORT);

// Background Initialization Logic
async function initializeSystem() {
	console.log("[INIT] Starting background initialization sequence...");
	global.startupStatus = {
		step: "network_check",
		details: "Testing database connectivity",
		timestamp: new Date().toISOString(),
	};

	// Initialize PKWT Notification Scheduler
	try {
		const pkwtScheduler = require("./apps/sapps/masters/notifications/pkwtNotificationScheduler");
		// Start scheduler with default settings (daily check, 30 days before expiration)
		// You can configure this via environment variables or config.json if needed
		const schedulerConfig = {
			intervalHours: 24, // Run daily
			daysBeforeExpiration: 30, // Notify 30 days before expiration
			modified_by: 1, // System user
			owner: 1, // System owner
			runImmediately: false, // Don't run immediately on startup, wait for first scheduled run
		};
		pkwtScheduler.startScheduler(schedulerConfig);
		console.log("[INIT] PKWT Notification Scheduler initialized");
	} catch (error) {
		console.error("[INIT] Failed to initialize PKWT Notification Scheduler:", error);
		// Don't block startup if scheduler fails
	}

	// Initialize Birthday Notification Scheduler
	try {
		const birthdayScheduler = require("./apps/sapps/masters/notifications/birthdayNotificationScheduler");
		const schedulerConfig = {
			intervalHours: 24, // Run daily
			modified_by: 1, // System user
			owner: 1, // System owner
			runImmediately: false, // Don't run immediately on startup
		};
		birthdayScheduler.startScheduler(schedulerConfig);
		console.log("[INIT] Birthday Notification Scheduler initialized");
	} catch (error) {
		console.error("[INIT] Failed to initialize Birthday Notification Scheduler:", error);
		// Don't block startup if scheduler fails
	}

	try {
		// --- A. NETWORK CHECK (Async, Non-blocking for server) ---
		const NetworkDiagnostics = require("./utils/networkDiagnostics");

		// Use retry mechanism but don't block forever
		let networkTest = await NetworkDiagnostics.testTCPConnectionWithStrategies(config.databaseConfigSAPPS.host, config.databaseConfigSAPPS.port);

		if (!networkTest.success) {
			console.warn("[INIT WARN] Fast network test failed, trying robust retry...");
			networkTest = await NetworkDiagnostics.testTCPConnection(config.databaseConfigSAPPS.host, config.databaseConfigSAPPS.port, 10000, {
				maxRetries: 2,
				retryDelay: 2000,
			});
		}

		if (!networkTest.success) {
			console.error("[INIT ERROR] Network connectivity failed. System entering DEGRADED mode.");
			global.isDegradedMode = true;
			global.startupStatus = {
				step: "failed",
				details: "Network connectivity failed",
				error: networkTest.error,
			};
			// We continue to try DB init, it might handle itself or fail gracefully
		} else {
			console.log("[INIT] Network connectivity: PASSED");
		}

		// --- B. ROBUST NETWORK MANAGER ---
		console.log("[INIT] Initializing Robust Network Manager...");
		const RobustNetworkManager = require("./utils/robustNetworkManager");
		const networkManager = new RobustNetworkManager(config.databaseConfigSAPPS, {
			testTimeout: 5000,
			monitoringEnabled: true,
		});

		// Initialize without awaiting lightly if possible, but here we await to ensure globals are set
		await networkManager.initialize();
		global.networkManager = networkManager;
		global.networkMonitor = networkManager.monitor;

		// --- C. DATABASE INITIALIZATION ---
		console.log("[INIT] Waiting for Database initialization...");
		global.startupStatus = {
			step: "db_init",
			details: "Initializing database models",
			timestamp: new Date().toISOString(),
		};

		const dbSAPPS = require("./apps/sapps/helpers/db");
		// Wait for the DB to be ready (it started loading when required)
		// Give it huge timeout for production
		const initTimeout = 180000; // 3 minutes

		try {
			await dbSAPPS.waitForInitialization(initTimeout);

			if (dbSAPPS.isReady && dbSAPPS.isReady()) {
				console.log("[INIT] Database Ready.");
			} else {
				throw new Error("DB reported not ready after wait");
			}
		} catch (dbErr) {
			console.error("[INIT ERROR] Database initialization failed:", dbErr.message);
			global.isDegradedMode = true;
			global.startupStatus = {
				step: "degraded",
				details: "Database failed to initialize",
				error: dbErr.message,
			};
			// Even if DB fails, we set system ready (but degraded) so requests can hit controllers
			// and potentially get specific DB errors instead of generic 503
		}

		// --- D. CRASH RECOVERY RESET ---
		console.log("[INIT] System initialization complete.");
		if (process.uptime() > 60) {
			crashRecovery.resetRestartCount();
		}

		// --- E. FINALIZE ---
		global.isSystemReady = true;
		global.startupStatus = {
			step: "ready",
			details: "System running",
			timestamp: new Date().toISOString(),
		};

		console.log("========================================");
		console.log("SYSTEM FULLY OPERATIONAL 🚀");
		console.log("========================================");
	} catch (err) {
		console.error("[INIT CRITICAL] Unexpected error during background init:", err);
		global.startupStatus = {
			step: "crashed",
			details: "Init process crashed",
			error: err.message,
		};
	}
}

// Cleanup on exit
function gracefulShutdown(signal) {
	console.log(`Received ${signal}. Closing...`);
	if (serverInstance) serverInstance.close(() => process.exit(0));
	else process.exit(0);
}
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
