import { db } from '$lib/server/db';
import { dailyCompletions, dailyVerses, type DailyCompletion } from '$lib/server/db/schema';
import { eq, desc } from 'drizzle-orm';
import type { PageServerLoad } from './$types';
import { bibleBooks } from '$lib/types/bible';

export const load: PageServerLoad = async ({ url, locals }) => {
	// Check if user is authenticated
	if (!locals.user) {
		return {
			stats: null,
			error: null,
			user: null,
			session: null,
			requiresAuth: true
		};
	}

	const userId = locals.user.id;

	if (!userId) {
		return {
			stats: null,
			error: 'No user ID provided',
			user: locals.user,
			session: locals.session
		};
	}

	// Note: userToday is used only for the initial server-side streak estimate.
	// The client overrides this with a precise local-date calculation via /api/streak.
	const userToday = new Date().toISOString().slice(0, 10); // UTC date as safe fallback

	try {
		// Get all completions for this user
		const completions = await db
			.select()
			.from(dailyCompletions)
			.where(eq(dailyCompletions.anonymousId, userId))
			.orderBy(desc(dailyCompletions.date));

		if (completions.length === 0) {
			return {
				stats: {
					totalSolves: 0,
					avgGuesses: 0,
					gradeDistribution: {
						'S++': 0,
						'S+': 0,
						'A+': 0,
						'A': 0,
						'B+': 0,
						'B': 0,
						'C+': 0,
						'C': 0
					},
					currentStreak: 0,
					bestStreak: 0,
					recentCompletions: [],
					worstDay: null,
					bestBook: null,
					mostSeenBook: null,
					totalBooksSeenOT: 0,
					totalBooksSeenNT: 0
				},
				user: locals.user,
				session: locals.session
			};
		}

		// Calculate basic stats
		const totalSolves = completions.length;
		const totalGuesses = completions.reduce((sum: number, c: DailyCompletion) => sum + c.guessCount, 0);
		const avgGuesses = Math.round((totalGuesses / totalSolves) * 100) / 100;

		// Calculate grade distribution
		const gradeDistribution = {
			'S++': 0, // This will be calculated differently since we don't store chapter correctness
			'S+': completions.filter((c: DailyCompletion) => c.guessCount === 1).length,
			'A+': completions.filter((c: DailyCompletion) => c.guessCount === 2).length,
			'A': completions.filter((c: DailyCompletion) => c.guessCount === 3).length,
			'B+': completions.filter((c: DailyCompletion) => c.guessCount >= 4 && c.guessCount <= 6).length,
			'B': completions.filter((c: DailyCompletion) => c.guessCount >= 7 && c.guessCount <= 10).length,
			'C+': completions.filter((c: DailyCompletion) => c.guessCount >= 11 && c.guessCount <= 15).length,
			'C': completions.filter((c: DailyCompletion) => c.guessCount > 15).length
		};

		// Calculate streaks — dates are stored as the user's local date
		const sortedDates = completions
			.map((c: DailyCompletion) => c.date)
			.sort();

		let currentStreak = 0;
		let bestStreak = 0;
		let tempStreak = 1;

		if (sortedDates.length > 0) {
			// Check if current streak is active (includes today or yesterday)
			// Use the user's local date passed from the client
			const today = userToday;
			const yesterdayDate = new Date(userToday);
			yesterdayDate.setDate(yesterdayDate.getDate() - 1);
			const yesterday = yesterdayDate.toISOString().split('T')[0];
			const lastPlayedDate = sortedDates[sortedDates.length - 1];

			if (lastPlayedDate === today || lastPlayedDate === yesterday) {
				currentStreak = 1;

				// Count backwards from the most recent date
				for (let i = sortedDates.length - 2; i >= 0; i--) {
					const currentDate = new Date(sortedDates[i + 1]);
					const prevDate = new Date(sortedDates[i]);
					const daysDiff = Math.floor((currentDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24));

					if (daysDiff === 1) {
						currentStreak++;
					} else {
						break;
					}
				}
			}

			// Calculate best streak
			bestStreak = 1;
			for (let i = 1; i < sortedDates.length; i++) {
				const currentDate = new Date(sortedDates[i]);
				const prevDate = new Date(sortedDates[i - 1]);
				const daysDiff = Math.floor((currentDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24));

				if (daysDiff === 1) {
					tempStreak++;
				} else {
					bestStreak = Math.max(bestStreak, tempStreak);
					tempStreak = 1;
				}
			}
			bestStreak = Math.max(bestStreak, tempStreak);
		}

		// Get recent completions (last 7 days)
		const recentCompletions = completions
			.slice(0, 7)
			.map((c: DailyCompletion) => ({
				date: c.date,
				guessCount: c.guessCount,
				grade: getGradeFromGuesses(c.guessCount)
			}));

		// Calculate worst day (highest guess count)
		const worstDay = completions.reduce((max, c) =>
			c.guessCount > max.guessCount ? c : max,
			completions[0]
		);

		// Get all daily verses to link completions to books
		const allVerses = await db
			.select()
			.from(dailyVerses);

		// Create a map of date -> bookId
		const dateToBookId = new Map(allVerses.map(v => [v.date, v.bookId]));

		// Calculate book-specific stats
		const bookStats = new Map<string, { count: number; totalGuesses: number }>();

		for (const completion of completions) {
			const bookId = dateToBookId.get(completion.date);
			if (bookId) {
				const existing = bookStats.get(bookId) || { count: 0, totalGuesses: 0 };
				bookStats.set(bookId, {
					count: existing.count + 1,
					totalGuesses: existing.totalGuesses + completion.guessCount
				});
			}
		}

		// Find book you know the best (lowest avg guesses)
		let bestBook: { bookId: string; avgGuesses: number; count: number } | null = null;
		for (const [bookId, stats] of bookStats.entries()) {
			const avgGuesses = stats.totalGuesses / stats.count;
			if (!bestBook || avgGuesses < bestBook.avgGuesses) {
				bestBook = { bookId, avgGuesses, count: stats.count };
			}
		}

		// Find most seen book
		let mostSeenBook: { bookId: string; count: number } | null = null;
		for (const [bookId, stats] of bookStats.entries()) {
			if (!mostSeenBook || stats.count > mostSeenBook.count) {
				mostSeenBook = { bookId, count: stats.count };
			}
		}

		// Count unique books by testament
		const oldTestamentBooks = new Set<string>();
		const newTestamentBooks = new Set<string>();

		for (const [bookId, _] of bookStats.entries()) {
			const book = bibleBooks.find(b => b.id === bookId);
			if (book) {
				if (book.testament === 'old') {
					oldTestamentBooks.add(bookId);
				} else {
					newTestamentBooks.add(bookId);
				}
			}
		}

		return {
			stats: {
				totalSolves,
				avgGuesses,
				gradeDistribution,
				currentStreak,
				bestStreak,
				recentCompletions,
				worstDay: {
					date: worstDay.date,
					guessCount: worstDay.guessCount
				},
				bestBook: bestBook ? {
					bookId: bestBook.bookId,
					avgGuesses: Math.round(bestBook.avgGuesses * 100) / 100,
					count: bestBook.count
				} : null,
				mostSeenBook: mostSeenBook ? {
					bookId: mostSeenBook.bookId,
					count: mostSeenBook.count
				} : null,
				totalBooksSeenOT: oldTestamentBooks.size,
				totalBooksSeenNT: newTestamentBooks.size
			},
			user: locals.user,
			session: locals.session
		};

	} catch (error) {
		console.error('Error fetching user stats:', error);
		return {
			stats: null,
			error: 'Failed to fetch stats',
			user: locals.user,
			session: locals.session
		};
	}
};

function getGradeFromGuesses(guessCount: number): string {
	if (guessCount === 1) return "S+";
	if (guessCount === 2) return "A+";
	if (guessCount === 3) return "A";
	if (guessCount >= 4 && guessCount <= 6) return "B+";
	if (guessCount >= 7 && guessCount <= 10) return "B";
	if (guessCount >= 11 && guessCount <= 15) return "C+";
	return "C";
}
