import type { InferResultType } from "@/utils/drizzle";

type UsagePerDay = {
	day: number; // 0 (Sunday) to 6 (Saturday)
	usageMinutes: number;
};

function aggregateUsageByDay(jobs: InferResultType<"printJobs">[]): {
	usageData: UsagePerDay[];
	earliestDate: Date;
	latestDate: Date;
} {
	const usagePerDayMap = new Map<number, number>();

	let earliestDate: Date | null = null;
	let latestDate: Date | null = null;

	for (const job of jobs) {
		let remainingDuration = job.durationInMinutes;
		const currentStart = new Date(job.startAt);

		// Update earliest and latest dates
		if (!earliestDate || currentStart < earliestDate) {
			earliestDate = new Date(currentStart);
		}
		const jobEnd = new Date(currentStart);
		jobEnd.setMinutes(jobEnd.getMinutes() + job.durationInMinutes);
		if (!latestDate || jobEnd > latestDate) {
			latestDate = new Date(jobEnd);
		}

		while (remainingDuration > 0) {
			const day = currentStart.getDay();

			// Calculate minutes remaining in the current day
			const minutesRemainingInDay = (24 - currentStart.getHours()) * 60 - currentStart.getMinutes();
			const minutesToAdd = Math.min(remainingDuration, minutesRemainingInDay);

			// Update the usage for the current day
			const usageMinutes = usagePerDayMap.get(day) || 0;
			usagePerDayMap.set(day, usageMinutes + minutesToAdd);

			// Update remaining duration and move to the next day
			remainingDuration -= minutesToAdd;
			currentStart.setDate(currentStart.getDate() + 1);
			currentStart.setHours(0, 0, 0, 0); // Start at the beginning of the next day
		}
	}

	const usageData: UsagePerDay[] = Array.from({ length: 7 }, (_, day) => ({
		day,
		usageMinutes: usagePerDayMap.get(day) || 0,
	}));

	if (earliestDate === null) {
		earliestDate = new Date();
	}

	if (latestDate === null) {
		latestDate = new Date();
	}

	return { usageData, earliestDate: earliestDate, latestDate: latestDate };
}

function countWeekdays(startDate: Date, endDate: Date): number[] {
	const countPerDay = Array(7).fill(0);
	const currentDate = new Date(startDate);
	currentDate.setHours(0, 0, 0, 0); // Ensure starting at midnight
	endDate.setHours(0, 0, 0, 0); // Ensure ending at midnight

	while (currentDate <= endDate) {
		const day = currentDate.getDay();
		countPerDay[day]++;
		currentDate.setDate(currentDate.getDate() + 1);
	}
	return countPerDay;
}

export function forecastPrinterUsage(jobs: InferResultType<"printJobs">[]): number[] {
	const { usageData, earliestDate, latestDate } = aggregateUsageByDay(jobs);

	// Count the number of times each weekday occurs in the data period
	const weekdaysCount = countWeekdays(earliestDate, latestDate);

	const forecasts: number[] = [];
	for (const data of usageData) {
		const dayCount = weekdaysCount[data.day];
		let usagePrediction = data.usageMinutes / dayCount;
		if (Number.isNaN(usagePrediction)) {
			usagePrediction = 0;
		}
		forecasts.push(Math.round(usagePrediction));
	}

	return forecasts;
}
