const express = require("express");
const { getPrismaClient } = require("../config/prisma");
const moment = require("moment");
const { authenticateToken } = require("../middleware/auth");
const {
	requireViewDashboard,
	requireViewHosts,
	requireViewPackages,
	requireViewUsers,
} = require("../middleware/permissions");
const { queueManager } = require("../services/automation");

const router = express.Router();
const prisma = getPrismaClient();

// Get dashboard statistics
router.get(
	"/stats",
	authenticateToken,
	requireViewDashboard,
	async (_req, res) => {
		try {
			const now = new Date();

			// Get the agent update interval setting
			const settings = await prisma.settings.findFirst();
			const updateIntervalMinutes = settings?.update_interval || 60; // Default to 60 minutes if no setting

			// Calculate the threshold based on the actual update interval
			// Use 2x the update interval as the threshold for "errored" hosts
			const thresholdMinutes = updateIntervalMinutes * 2;
			const thresholdTime = moment(now)
				.subtract(thresholdMinutes, "minutes")
				.toDate();

			// Get all statistics in parallel for better performance
			const [
				totalHosts,
				hostsNeedingUpdates,
				totalOutdatedPackages,
				erroredHosts,
				securityUpdates,
				offlineHosts,
				totalHostGroups,
				totalUsers,
				totalRepos,
				osDistribution,
				updateTrends,
			] = await Promise.all([
				// Total hosts count (all hosts regardless of status)
				prisma.hosts.count(),

				// Hosts needing updates (distinct hosts with packages needing updates)
				prisma.hosts.count({
					where: {
						host_packages: {
							some: {
								needs_update: true,
							},
						},
					},
				}),

				// Total unique packages that need updates
				prisma.packages.count({
					where: {
						host_packages: {
							some: {
								needs_update: true,
							},
						},
					},
				}),

				// Errored hosts (not updated within threshold based on update interval)
				prisma.hosts.count({
					where: {
						status: "active",
						last_update: {
							lt: thresholdTime,
						},
					},
				}),

				// Security updates count (unique packages)
				prisma.packages.count({
					where: {
						host_packages: {
							some: {
								needs_update: true,
								is_security_update: true,
							},
						},
					},
				}),

				// Offline/Stale hosts (not updated within 3x the update interval)
				prisma.hosts.count({
					where: {
						status: "active",
						last_update: {
							lt: moment(now)
								.subtract(updateIntervalMinutes * 3, "minutes")
								.toDate(),
						},
					},
				}),

				// Total host groups count
				prisma.host_groups.count(),

				// Total users count
				prisma.users.count(),

				// Total repositories count
				prisma.repositories.count(),

				// OS distribution for pie chart
				prisma.hosts.groupBy({
					by: ["os_type"],
					where: { status: "active" },
					_count: {
						os_type: true,
					},
				}),

				// Update trends for the last 7 days
				prisma.update_history.groupBy({
					by: ["timestamp"],
					where: {
						timestamp: {
							gte: moment(now).subtract(7, "days").toDate(),
						},
					},
					_count: {
						id: true,
					},
					_sum: {
						packages_count: true,
						security_count: true,
					},
				}),
			]);

			// Format OS distribution for pie chart
			const osDistributionFormatted = osDistribution.map((item) => ({
				name: item.os_type,
				count: item._count.os_type,
			}));

			// Calculate update status distribution
			const updateStatusDistribution = [
				{ name: "Up to date", count: totalHosts - hostsNeedingUpdates },
				{ name: "Needs updates", count: hostsNeedingUpdates },
				{ name: "Errored", count: erroredHosts },
			];

			// Package update priority distribution
			const regularUpdates = Math.max(
				0,
				totalOutdatedPackages - securityUpdates,
			);
			const packageUpdateDistribution = [
				{ name: "Security", count: securityUpdates },
				{ name: "Regular", count: regularUpdates },
			];

			res.json({
				cards: {
					totalHosts,
					hostsNeedingUpdates,
					upToDateHosts: Math.max(totalHosts - hostsNeedingUpdates, 0),
					totalOutdatedPackages,
					erroredHosts,
					securityUpdates,
					offlineHosts,
					totalHostGroups,
					totalUsers,
					totalRepos,
				},
				charts: {
					osDistribution: osDistributionFormatted,
					updateStatusDistribution,
					packageUpdateDistribution,
				},
				trends: updateTrends,
				lastUpdated: now.toISOString(),
			});
		} catch (error) {
			console.error("Error fetching dashboard stats:", error);
			res.status(500).json({ error: "Failed to fetch dashboard statistics" });
		}
	},
);

// Get hosts with their update status - OPTIMIZED
router.get("/hosts", authenticateToken, requireViewHosts, async (_req, res) => {
	try {
		// Get settings once (outside the loop)
		const settings = await prisma.settings.findFirst();
		const updateIntervalMinutes = settings?.update_interval || 60;
		const thresholdMinutes = updateIntervalMinutes * 2;

		// Fetch hosts with groups
		const hosts = await prisma.hosts.findMany({
			select: {
				id: true,
				machine_id: true,
				friendly_name: true,
				hostname: true,
				ip: true,
				os_type: true,
				os_version: true,
				last_update: true,
				status: true,
				agent_version: true,
				auto_update: true,
				notes: true,
				api_id: true,
				host_group_memberships: {
					include: {
						host_groups: {
							select: {
								id: true,
								name: true,
								color: true,
							},
						},
					},
				},
			},
			orderBy: { last_update: "desc" },
		});

		// OPTIMIZATION: Get all package counts in 2 batch queries instead of N*2 queries
		const hostIds = hosts.map((h) => h.id);

		const [updateCounts, totalCounts] = await Promise.all([
			// Get update counts for all hosts at once
			prisma.host_packages.groupBy({
				by: ["host_id"],
				where: {
					host_id: { in: hostIds },
					needs_update: true,
				},
				_count: { id: true },
			}),
			// Get total counts for all hosts at once
			prisma.host_packages.groupBy({
				by: ["host_id"],
				where: {
					host_id: { in: hostIds },
				},
				_count: { id: true },
			}),
		]);

		// Create lookup maps for O(1) access
		const updateCountMap = new Map(
			updateCounts.map((item) => [item.host_id, item._count.id]),
		);
		const totalCountMap = new Map(
			totalCounts.map((item) => [item.host_id, item._count.id]),
		);

		// Process hosts with counts from maps (no more DB queries!)
		const hostsWithUpdateInfo = hosts.map((host) => {
			const updatesCount = updateCountMap.get(host.id) || 0;
			const totalPackagesCount = totalCountMap.get(host.id) || 0;

			// Calculate effective status based on reporting interval
			const isStale = moment(host.last_update).isBefore(
				moment().subtract(thresholdMinutes, "minutes"),
			);
			let effectiveStatus = host.status;

			// Override status if host hasn't reported within threshold
			if (isStale && host.status === "active") {
				effectiveStatus = "inactive";
			}

			return {
				...host,
				updatesCount,
				totalPackagesCount,
				isStale,
				effectiveStatus,
			};
		});

		res.json(hostsWithUpdateInfo);
	} catch (error) {
		console.error("Error fetching hosts:", error);
		res.status(500).json({ error: "Failed to fetch hosts" });
	}
});

// Get packages that need updates across all hosts
router.get(
	"/packages",
	authenticateToken,
	requireViewPackages,
	async (_req, res) => {
		try {
			const packages = await prisma.packages.findMany({
				where: {
					host_packages: {
						some: {
							needs_update: true,
						},
					},
				},
				select: {
					id: true,
					name: true,
					description: true,
					category: true,
					latest_version: true,
					host_packages: {
						where: { needs_update: true },
						select: {
							current_version: true,
							available_version: true,
							is_security_update: true,
							hosts: {
								select: {
									id: true,
									friendly_name: true,
									os_type: true,
								},
							},
						},
					},
				},
				orderBy: {
					name: "asc",
				},
			});

			const packagesWithHostInfo = packages.map((pkg) => ({
				id: pkg.id,
				name: pkg.name,
				description: pkg.description,
				category: pkg.category,
				latestVersion: pkg.latest_version,
				affectedHostsCount: pkg.host_packages.length,
				isSecurityUpdate: pkg.host_packages.some((hp) => hp.is_security_update),
				affectedHosts: pkg.host_packages.map((hp) => ({
					hostId: hp.hosts.id,
					friendlyName: hp.hosts.friendly_name,
					osType: hp.hosts.os_type,
					currentVersion: hp.current_version,
					availableVersion: hp.available_version,
					isSecurityUpdate: hp.is_security_update,
				})),
			}));

			res.json(packagesWithHostInfo);
		} catch (error) {
			console.error("Error fetching packages:", error);
			res.status(500).json({ error: "Failed to fetch packages" });
		}
	},
);

// Get detailed host information
router.get(
	"/hosts/:hostId",
	authenticateToken,
	requireViewHosts,
	async (req, res) => {
		try {
			const { hostId } = req.params;

			const limit = parseInt(req.query.limit, 10) || 10;
			const offset = parseInt(req.query.offset, 10) || 0;

			const [host, totalHistoryCount] = await Promise.all([
				prisma.hosts.findUnique({
					where: { id: hostId },
					include: {
						host_group_memberships: {
							include: {
								host_groups: {
									select: {
										id: true,
										name: true,
										color: true,
									},
								},
							},
						},
						host_packages: {
							include: {
								packages: true,
							},
							orderBy: {
								needs_update: "desc",
							},
						},
						update_history: {
							orderBy: {
								timestamp: "desc",
							},
							take: limit,
							skip: offset,
						},
					},
				}),
				prisma.update_history.count({
					where: { host_id: hostId },
				}),
			]);

			if (!host) {
				return res.status(404).json({ error: "Host not found" });
			}

			const hostWithStats = {
				...host,
				stats: {
					total_packages: host.host_packages.length,
					outdated_packages: host.host_packages.filter((hp) => hp.needs_update)
						.length,
					security_updates: host.host_packages.filter(
						(hp) => hp.needs_update && hp.is_security_update,
					).length,
				},
				pagination: {
					total: totalHistoryCount,
					limit,
					offset,
					hasMore: offset + limit < totalHistoryCount,
				},
			};

			res.json(hostWithStats);
		} catch (error) {
			console.error("Error fetching host details:", error);
			res.status(500).json({ error: "Failed to fetch host details" });
		}
	},
);

// Get agent queue status for a specific host
router.get(
	"/hosts/:hostId/queue",
	authenticateToken,
	requireViewHosts,
	async (req, res) => {
		try {
			const { hostId } = req.params;
			const { limit = 20 } = req.query;

			// Get the host to find its API ID
			const host = await prisma.hosts.findUnique({
				where: { id: hostId },
				select: { api_id: true, friendly_name: true },
			});

			if (!host) {
				return res.status(404).json({ error: "Host not found" });
			}

			// Get queue jobs for this host
			const queueData = await queueManager.getHostJobs(
				host.api_id,
				parseInt(limit, 10),
			);

			res.json({
				success: true,
				data: {
					hostId,
					apiId: host.api_id,
					friendlyName: host.friendly_name,
					...queueData,
				},
			});
		} catch (error) {
			console.error("Error fetching host queue status:", error);
			res.status(500).json({
				success: false,
				error: "Failed to fetch host queue status",
			});
		}
	},
);

// Get recent users ordered by last_login desc
router.get(
	"/recent-users",
	authenticateToken,
	requireViewUsers,
	async (_req, res) => {
		try {
			const users = await prisma.users.findMany({
				where: {
					last_login: {
						not: null,
					},
				},
				select: {
					id: true,
					username: true,
					email: true,
					role: true,
					last_login: true,
					created_at: true,
				},
				orderBy: [{ last_login: "desc" }, { created_at: "desc" }],
				take: 5,
			});

			res.json(users);
		} catch (error) {
			console.error("Error fetching recent users:", error);
			res.status(500).json({ error: "Failed to fetch recent users" });
		}
	},
);

// Get recent hosts that have sent data (ordered by last_update desc)
router.get(
	"/recent-collection",
	authenticateToken,
	requireViewHosts,
	async (_req, res) => {
		try {
			const hosts = await prisma.hosts.findMany({
				select: {
					id: true,
					friendly_name: true,
					hostname: true,
					last_update: true,
					status: true,
				},
				orderBy: {
					last_update: "desc",
				},
				take: 5,
			});

			res.json(hosts);
		} catch (error) {
			console.error("Error fetching recent collection:", error);
			res.status(500).json({ error: "Failed to fetch recent collection" });
		}
	},
);

// Get package trends over time
router.get(
	"/package-trends",
	authenticateToken,
	requireViewHosts,
	async (req, res) => {
		try {
			const { days = 30, hostId } = req.query;
			const daysInt = parseInt(days, 10);

			// Calculate date range
			const endDate = new Date();
			const startDate = new Date();
			startDate.setDate(endDate.getDate() - daysInt);

			// Build where clause
			const whereClause = {
				timestamp: {
					gte: startDate,
					lte: endDate,
				},
			};

			// Add host filter if specified
			if (hostId && hostId !== "all" && hostId !== "undefined") {
				whereClause.host_id = hostId;
			}

			// Get all update history records in the date range
			const trendsData = await prisma.update_history.findMany({
				where: whereClause,
				select: {
					timestamp: true,
					packages_count: true,
					security_count: true,
					total_packages: true,
					host_id: true,
					status: true,
				},
				orderBy: {
					timestamp: "asc",
				},
			});

			// Enhanced data validation and processing
			const processedData = trendsData
				.filter((record) => {
					// Enhanced validation
					return (
						record.total_packages !== null &&
						record.total_packages >= 0 &&
						record.packages_count >= 0 &&
						record.security_count >= 0 &&
						record.security_count <= record.packages_count && // Security can't exceed outdated
						record.status === "success"
					); // Only include successful reports
				})
				.map((record) => {
					const date = new Date(record.timestamp);
					let timeKey;

					if (daysInt <= 1) {
						// For hourly view, group by hour only (not minutes)
						timeKey = date.toISOString().substring(0, 13); // YYYY-MM-DDTHH
					} else {
						// For daily view, group by day
						timeKey = date.toISOString().split("T")[0]; // YYYY-MM-DD
					}

					return {
						timeKey,
						total_packages: record.total_packages,
						packages_count: record.packages_count || 0,
						security_count: record.security_count || 0,
						host_id: record.host_id,
						timestamp: record.timestamp,
					};
				});

			// Determine if we need aggregation based on host filter
			const needsAggregation =
				!hostId || hostId === "all" || hostId === "undefined";

			let aggregatedArray;

			if (needsAggregation) {
				// For "All Hosts" mode, we need to calculate the actual total packages differently
				// Instead of aggregating historical data (which is per-host), we'll use the current total
				// and show that as a flat line, since total packages don't change much over time

				// Get the current total packages count (unique packages across all hosts)
				const currentTotalPackages = await prisma.packages.count({
					where: {
						host_packages: {
							some: {}, // At least one host has this package
						},
					},
				});

				// Aggregate data by timeKey when looking at "All Hosts" or no specific host
				const aggregatedData = processedData.reduce((acc, item) => {
					if (!acc[item.timeKey]) {
						acc[item.timeKey] = {
							timeKey: item.timeKey,
							total_packages: currentTotalPackages, // Use current total packages
							packages_count: 0,
							security_count: 0,
							record_count: 0,
							host_ids: new Set(),
							min_timestamp: item.timestamp,
							max_timestamp: item.timestamp,
						};
					}

					// For outdated and security packages: SUM (these represent counts across hosts)
					acc[item.timeKey].packages_count += item.packages_count;
					acc[item.timeKey].security_count += item.security_count;

					acc[item.timeKey].record_count += 1;
					acc[item.timeKey].host_ids.add(item.host_id);

					// Track timestamp range
					if (item.timestamp < acc[item.timeKey].min_timestamp) {
						acc[item.timeKey].min_timestamp = item.timestamp;
					}
					if (item.timestamp > acc[item.timeKey].max_timestamp) {
						acc[item.timeKey].max_timestamp = item.timestamp;
					}

					return acc;
				}, {});

				// Convert to array and add metadata
				aggregatedArray = Object.values(aggregatedData)
					.map((item) => ({
						...item,
						host_count: item.host_ids.size,
						host_ids: Array.from(item.host_ids),
					}))
					.sort((a, b) => a.timeKey.localeCompare(b.timeKey));
			} else {
				// For specific host, show individual data points without aggregation
				// But still group by timeKey to handle multiple reports from same host in same time period
				const hostAggregatedData = processedData.reduce((acc, item) => {
					if (!acc[item.timeKey]) {
						acc[item.timeKey] = {
							timeKey: item.timeKey,
							total_packages: 0,
							packages_count: 0,
							security_count: 0,
							record_count: 0,
							host_ids: new Set([item.host_id]),
							min_timestamp: item.timestamp,
							max_timestamp: item.timestamp,
						};
					}

					// For same host, take the latest values (not sum)
					// This handles cases where a host reports multiple times in the same time period
					if (item.timestamp > acc[item.timeKey].max_timestamp) {
						acc[item.timeKey].total_packages = item.total_packages;
						acc[item.timeKey].packages_count = item.packages_count;
						acc[item.timeKey].security_count = item.security_count;
						acc[item.timeKey].max_timestamp = item.timestamp;
					}

					acc[item.timeKey].record_count += 1;

					return acc;
				}, {});

				// Convert to array
				aggregatedArray = Object.values(hostAggregatedData)
					.map((item) => ({
						...item,
						host_count: item.host_ids.size,
						host_ids: Array.from(item.host_ids),
					}))
					.sort((a, b) => a.timeKey.localeCompare(b.timeKey));
			}

			// Handle sparse data by filling missing time periods
			const fillMissingPeriods = (data, daysInt) => {
				const filledData = [];
				const startDate = new Date();
				startDate.setDate(startDate.getDate() - daysInt);

				const dataMap = new Map(data.map((item) => [item.timeKey, item]));

				const endDate = new Date();
				const currentDate = new Date(startDate);

				// Find the last known values for interpolation
				let lastKnownValues = null;
				if (data.length > 0) {
					lastKnownValues = {
						total_packages: data[0].total_packages,
						packages_count: data[0].packages_count,
						security_count: data[0].security_count,
					};
				}

				while (currentDate <= endDate) {
					let timeKey;
					if (daysInt <= 1) {
						timeKey = currentDate.toISOString().substring(0, 13); // Hourly
						currentDate.setHours(currentDate.getHours() + 1);
					} else {
						timeKey = currentDate.toISOString().split("T")[0]; // Daily
						currentDate.setDate(currentDate.getDate() + 1);
					}

					if (dataMap.has(timeKey)) {
						const item = dataMap.get(timeKey);
						filledData.push(item);
						// Update last known values
						lastKnownValues = {
							total_packages: item.total_packages,
							packages_count: item.packages_count,
							security_count: item.security_count,
						};
					} else {
						// For missing periods, use the last known values (interpolation)
						// This creates a continuous line instead of gaps
						filledData.push({
							timeKey,
							total_packages: lastKnownValues?.total_packages || 0,
							packages_count: lastKnownValues?.packages_count || 0,
							security_count: lastKnownValues?.security_count || 0,
							record_count: 0,
							host_count: 0,
							host_ids: [],
							min_timestamp: null,
							max_timestamp: null,
							isInterpolated: true, // Mark as interpolated for debugging
						});
					}
				}

				return filledData;
			};

			const finalProcessedData = fillMissingPeriods(aggregatedArray, daysInt);

			// Get hosts list for dropdown
			const hostsList = await prisma.hosts.findMany({
				select: {
					id: true,
					friendly_name: true,
					hostname: true,
					last_update: true,
					status: true,
				},
				orderBy: {
					friendly_name: "asc",
				},
			});

			// Get current package state for offline fallback
			let currentPackageState = null;
			if (hostId && hostId !== "all" && hostId !== "undefined") {
				// Get current package counts for specific host
				const currentState = await prisma.host_packages.aggregate({
					where: {
						host_id: hostId,
					},
					_count: {
						id: true,
					},
				});

				// Get counts for boolean fields separately
				const outdatedCount = await prisma.host_packages.count({
					where: {
						host_id: hostId,
						needs_update: true,
					},
				});

				const securityCount = await prisma.host_packages.count({
					where: {
						host_id: hostId,
						is_security_update: true,
					},
				});

				currentPackageState = {
					total_packages: currentState._count.id,
					packages_count: outdatedCount,
					security_count: securityCount,
				};
			} else {
				// Get current package counts for all hosts
				// Total packages = count of unique packages installed on at least one host
				const totalPackagesCount = await prisma.packages.count({
					where: {
						host_packages: {
							some: {}, // At least one host has this package
						},
					},
				});

				// Get counts for boolean fields separately
				const outdatedCount = await prisma.host_packages.count({
					where: {
						needs_update: true,
					},
				});

				const securityCount = await prisma.host_packages.count({
					where: {
						is_security_update: true,
					},
				});

				currentPackageState = {
					total_packages: totalPackagesCount,
					packages_count: outdatedCount,
					security_count: securityCount,
				};
			}

			// Format data for chart
			const chartData = {
				labels: [],
				datasets: [
					{
						label: needsAggregation
							? "Total Packages (All Hosts)"
							: "Total Packages",
						data: [],
						borderColor: "#3B82F6", // Blue
						backgroundColor: "rgba(59, 130, 246, 0.1)",
						tension: 0.4,
						hidden: true, // Hidden by default
						spanGaps: true, // Connect lines across missing data
						pointRadius: 3,
						pointHoverRadius: 5,
					},
					{
						label: needsAggregation
							? "Total Outdated Packages"
							: "Outdated Packages",
						data: [],
						borderColor: "#F59E0B", // Orange
						backgroundColor: "rgba(245, 158, 11, 0.1)",
						tension: 0.4,
						spanGaps: true, // Connect lines across missing data
						pointRadius: 3,
						pointHoverRadius: 5,
					},
					{
						label: needsAggregation
							? "Total Security Packages"
							: "Security Packages",
						data: [],
						borderColor: "#EF4444", // Red
						backgroundColor: "rgba(239, 68, 68, 0.1)",
						tension: 0.4,
						spanGaps: true, // Connect lines across missing data
						pointRadius: 3,
						pointHoverRadius: 5,
					},
				],
			};

			// Process aggregated data
			finalProcessedData.forEach((item) => {
				chartData.labels.push(item.timeKey);
				chartData.datasets[0].data.push(item.total_packages);
				chartData.datasets[1].data.push(item.packages_count);
				chartData.datasets[2].data.push(item.security_count);
			});

			// Calculate data quality metrics
			const dataQuality = {
				totalRecords: trendsData.length,
				validRecords: processedData.length,
				aggregatedPoints: aggregatedArray.length,
				filledPoints: finalProcessedData.length,
				recordsWithNullTotal: trendsData.filter(
					(r) => r.total_packages === null,
				).length,
				recordsWithInvalidData: trendsData.length - processedData.length,
				successfulReports: trendsData.filter((r) => r.status === "success")
					.length,
				failedReports: trendsData.filter((r) => r.status === "error").length,
			};

			res.json({
				chartData,
				hosts: hostsList,
				period: daysInt,
				hostId: hostId || "all",
				currentPackageState,
				dataQuality,
				aggregationInfo: {
					hasData: aggregatedArray.length > 0,
					hasGaps: finalProcessedData.some((item) => item.record_count === 0),
					lastDataPoint:
						aggregatedArray.length > 0
							? aggregatedArray[aggregatedArray.length - 1]
							: null,
					aggregationMode: needsAggregation
						? "sum_across_hosts"
						: "individual_host_data",
					explanation: needsAggregation
						? "Data is summed across all hosts for each time period"
						: "Data shows individual host values without cross-host aggregation",
				},
			});
		} catch (error) {
			console.error("Error fetching package trends:", error);
			res.status(500).json({ error: "Failed to fetch package trends" });
		}
	},
);

// Diagnostic endpoint to investigate package spikes
router.get(
	"/package-spike-analysis",
	authenticateToken,
	requireViewHosts,
	async (req, res) => {
		try {
			const { date, time, hours = 2 } = req.query;

			if (!date || !time) {
				return res.status(400).json({
					error:
						"Date and time parameters are required. Format: date=2025-10-17&time=18:00",
				});
			}

			// Parse the specific date and time
			const targetDateTime = new Date(`${date}T${time}:00`);
			const startTime = new Date(targetDateTime);
			startTime.setHours(startTime.getHours() - parseInt(hours, 10));
			const endTime = new Date(targetDateTime);
			endTime.setHours(endTime.getHours() + parseInt(hours, 10));

			console.log(
				`Analyzing package spike around ${targetDateTime.toISOString()}`,
			);
			console.log(
				`Time range: ${startTime.toISOString()} to ${endTime.toISOString()}`,
			);

			// Get all update history records in the time window
			const spikeData = await prisma.update_history.findMany({
				where: {
					timestamp: {
						gte: startTime,
						lte: endTime,
					},
				},
				select: {
					id: true,
					host_id: true,
					timestamp: true,
					packages_count: true,
					security_count: true,
					total_packages: true,
					status: true,
					error_message: true,
					execution_time: true,
					payload_size_kb: true,
					hosts: {
						select: {
							friendly_name: true,
							hostname: true,
							os_type: true,
							os_version: true,
						},
					},
				},
				orderBy: {
					timestamp: "asc",
				},
			});

			// Analyze the data
			const analysis = {
				timeWindow: {
					start: startTime.toISOString(),
					end: endTime.toISOString(),
					target: targetDateTime.toISOString(),
				},
				totalRecords: spikeData.length,
				successfulReports: spikeData.filter((r) => r.status === "success")
					.length,
				failedReports: spikeData.filter((r) => r.status === "error").length,
				uniqueHosts: [...new Set(spikeData.map((r) => r.host_id))].length,
				hosts: {},
				timeline: [],
				summary: {
					maxPackagesCount: 0,
					maxSecurityCount: 0,
					maxTotalPackages: 0,
					avgPackagesCount: 0,
					avgSecurityCount: 0,
					avgTotalPackages: 0,
				},
			};

			// Group by host and analyze each host's behavior
			spikeData.forEach((record) => {
				const hostId = record.host_id;
				if (!analysis.hosts[hostId]) {
					analysis.hosts[hostId] = {
						hostInfo: record.hosts,
						records: [],
						summary: {
							totalReports: 0,
							successfulReports: 0,
							failedReports: 0,
							maxPackagesCount: 0,
							maxSecurityCount: 0,
							maxTotalPackages: 0,
							avgPackagesCount: 0,
							avgSecurityCount: 0,
							avgTotalPackages: 0,
						},
					};
				}

				analysis.hosts[hostId].records.push({
					timestamp: record.timestamp,
					packages_count: record.packages_count,
					security_count: record.security_count,
					total_packages: record.total_packages,
					status: record.status,
					error_message: record.error_message,
					execution_time: record.execution_time,
					payload_size_kb: record.payload_size_kb,
				});

				analysis.hosts[hostId].summary.totalReports++;
				if (record.status === "success") {
					analysis.hosts[hostId].summary.successfulReports++;
					analysis.hosts[hostId].summary.maxPackagesCount = Math.max(
						analysis.hosts[hostId].summary.maxPackagesCount,
						record.packages_count,
					);
					analysis.hosts[hostId].summary.maxSecurityCount = Math.max(
						analysis.hosts[hostId].summary.maxSecurityCount,
						record.security_count,
					);
					analysis.hosts[hostId].summary.maxTotalPackages = Math.max(
						analysis.hosts[hostId].summary.maxTotalPackages,
						record.total_packages || 0,
					);
				} else {
					analysis.hosts[hostId].summary.failedReports++;
				}
			});

			// Calculate averages for each host
			Object.keys(analysis.hosts).forEach((hostId) => {
				const host = analysis.hosts[hostId];
				const successfulRecords = host.records.filter(
					(r) => r.status === "success",
				);

				if (successfulRecords.length > 0) {
					host.summary.avgPackagesCount = Math.round(
						successfulRecords.reduce((sum, r) => sum + r.packages_count, 0) /
							successfulRecords.length,
					);
					host.summary.avgSecurityCount = Math.round(
						successfulRecords.reduce((sum, r) => sum + r.security_count, 0) /
							successfulRecords.length,
					);
					host.summary.avgTotalPackages = Math.round(
						successfulRecords.reduce(
							(sum, r) => sum + (r.total_packages || 0),
							0,
						) / successfulRecords.length,
					);
				}
			});

			// Create timeline with hourly/daily aggregation
			const timelineMap = new Map();
			spikeData.forEach((record) => {
				const timeKey = record.timestamp.toISOString().substring(0, 13); // Hourly
				if (!timelineMap.has(timeKey)) {
					timelineMap.set(timeKey, {
						timestamp: timeKey,
						totalReports: 0,
						successfulReports: 0,
						failedReports: 0,
						totalPackagesCount: 0,
						totalSecurityCount: 0,
						totalTotalPackages: 0,
						uniqueHosts: new Set(),
					});
				}

				const timelineEntry = timelineMap.get(timeKey);
				timelineEntry.totalReports++;
				timelineEntry.uniqueHosts.add(record.host_id);

				if (record.status === "success") {
					timelineEntry.successfulReports++;
					timelineEntry.totalPackagesCount += record.packages_count;
					timelineEntry.totalSecurityCount += record.security_count;
					timelineEntry.totalTotalPackages += record.total_packages || 0;
				} else {
					timelineEntry.failedReports++;
				}
			});

			// Convert timeline map to array
			analysis.timeline = Array.from(timelineMap.values())
				.map((entry) => ({
					...entry,
					uniqueHosts: entry.uniqueHosts.size,
				}))
				.sort((a, b) => a.timestamp.localeCompare(b.timestamp));

			// Calculate overall summary
			const successfulRecords = spikeData.filter((r) => r.status === "success");
			if (successfulRecords.length > 0) {
				analysis.summary.maxPackagesCount = Math.max(
					...successfulRecords.map((r) => r.packages_count),
				);
				analysis.summary.maxSecurityCount = Math.max(
					...successfulRecords.map((r) => r.security_count),
				);
				analysis.summary.maxTotalPackages = Math.max(
					...successfulRecords.map((r) => r.total_packages || 0),
				);
				analysis.summary.avgPackagesCount = Math.round(
					successfulRecords.reduce((sum, r) => sum + r.packages_count, 0) /
						successfulRecords.length,
				);
				analysis.summary.avgSecurityCount = Math.round(
					successfulRecords.reduce((sum, r) => sum + r.security_count, 0) /
						successfulRecords.length,
				);
				analysis.summary.avgTotalPackages = Math.round(
					successfulRecords.reduce(
						(sum, r) => sum + (r.total_packages || 0),
						0,
					) / successfulRecords.length,
				);
			}

			// Identify potential causes of the spike
			const potentialCauses = [];

			// Check for hosts with unusually high package counts
			Object.keys(analysis.hosts).forEach((hostId) => {
				const host = analysis.hosts[hostId];
				if (
					host.summary.maxPackagesCount >
					analysis.summary.avgPackagesCount * 2
				) {
					potentialCauses.push({
						type: "high_package_count",
						hostId,
						hostName: host.hostInfo.friendly_name || host.hostInfo.hostname,
						value: host.summary.maxPackagesCount,
						avg: analysis.summary.avgPackagesCount,
					});
				}
			});

			// Check for multiple hosts reporting at the same time (this explains the 500 vs 59 discrepancy)
			const concurrentReports = analysis.timeline.filter(
				(entry) => entry.uniqueHosts > 1,
			);
			if (concurrentReports.length > 0) {
				potentialCauses.push({
					type: "concurrent_reports",
					description:
						"Multiple hosts reported simultaneously - this explains why chart shows higher numbers than individual host reports",
					count: concurrentReports.length,
					details: concurrentReports.map((entry) => ({
						timestamp: entry.timestamp,
						totalPackagesCount: entry.totalPackagesCount,
						uniqueHosts: entry.uniqueHosts,
						avgPerHost: Math.round(
							entry.totalPackagesCount / entry.uniqueHosts,
						),
					})),
					explanation:
						"The chart sums package counts across all hosts. If multiple hosts report at the same time, the chart shows the total sum, not individual host counts.",
				});
			}

			// Check for failed reports that might indicate system issues
			if (analysis.failedReports > 0) {
				potentialCauses.push({
					type: "failed_reports",
					count: analysis.failedReports,
					percentage: Math.round(
						(analysis.failedReports / analysis.totalRecords) * 100,
					),
				});
			}

			// Add aggregation explanation
			const aggregationExplanation = {
				type: "aggregation_explanation",
				description: "Chart Aggregation Logic",
				details: {
					howItWorks:
						"The package trends chart sums package counts across all hosts for each time period",
					individualHosts:
						"Each host reports its own package count (e.g., 59 packages)",
					chartDisplay:
						"Chart shows the sum of all hosts' package counts (e.g., 59 + other hosts = 500)",
					timeGrouping:
						"Multiple hosts reporting in the same hour/day are aggregated together",
				},
				example: {
					host1: "Host A reports 59 outdated packages",
					host2: "Host B reports 120 outdated packages",
					host3: "Host C reports 321 outdated packages",
					chartShows: "Chart displays 500 total packages (59+120+321)",
				},
			};
			potentialCauses.push(aggregationExplanation);

			// Add specific host breakdown if a host ID is provided
			let specificHostAnalysis = null;
			if (req.query.hostId) {
				const hostId = req.query.hostId;
				const hostData = analysis.hosts[hostId];
				if (hostData) {
					specificHostAnalysis = {
						hostId,
						hostInfo: hostData.hostInfo,
						summary: hostData.summary,
						records: hostData.records,
						explanation: `This host reported ${hostData.summary.maxPackagesCount} outdated packages, but the chart shows ${analysis.summary.maxPackagesCount} because it sums across all hosts that reported at the same time.`,
					};
				}
			}

			res.json({
				analysis,
				potentialCauses,
				specificHostAnalysis,
				recommendations: [
					"Check if any hosts had major package updates around this time",
					"Verify if any new hosts were added to the system",
					"Check for system maintenance or updates that might have triggered package checks",
					"Review any automation or scheduled tasks that run around 6pm",
					"Check if any repositories were updated or new packages were released",
					"Remember: Chart shows SUM of all hosts' package counts, not individual host counts",
				],
			});
		} catch (error) {
			console.error("Error analyzing package spike:", error);
			res.status(500).json({ error: "Failed to analyze package spike" });
		}
	},
);

module.exports = router;
