import type { Request, Response } from "express";
import { prisma } from "../config/db.ts";
import { toIST } from "../utils/time.ts";
import asyncHandler from "express-async-handler";

export const getLeaderboard = asyncHandler(async (req: Request, res: Response) => {
    // Auth check
    const user = (req as any).user;
    const studentInstitutionId = user.studentInstitutionId;
    const institutionId = user.institutionId;

    if (!user || user.role !== "STUDENT") {
        res.status(403).json({ message: "Only students can access leaderboard" });
        return;
    }

    // Get student profile with their exam
    const studentInstitution = await prisma.studentInstitution.findFirst({
        where: { id: studentInstitutionId },
        include: {
            student: {
                select: { 
                    language: true,
                    user: { select: { firstName: true, lastName: true, details: true } } 
                }
            },
            exam: { select: { id: true } }
        }
    });

    if (!studentInstitution || !studentInstitution.examsId) {
        res.status(404).json({ message: "Student exam not found" });
        return;
    }

    const examId = studentInstitution.examsId;
    const myStudentId = studentInstitution.studentId;
    const motherTongue = studentInstitution.student?.language || studentInstitution.language;
    const mediumOfExam = studentInstitution.student?.language ? studentInstitution.language : ((studentInstitution.student?.user?.details as any)?.mediumOfExam || "English");

    // Get published tests for these exams
    const publishedPracticeTests = await prisma.practiceTest.findMany({
        where: {
            institutionId,
            examId,
            publish: true,
            OR: [
                { language: { in: [motherTongue as string, "English"] } },
                { language: mediumOfExam as string }
            ]
        },
        select: { id: true },
    });

    if (publishedPracticeTests.length === 0) {
        res.json({ leaderboard: [], myRank: null });
        return;
    }

    const practiceTestIds = publishedPracticeTests.map(t => t.id);

    const activeStudentInstitutions = await prisma.studentInstitution.findMany({
        where: { institutionId, deletedAt: null },
        select: { studentId: true },
    });
    const activeStudentIds = activeStudentInstitutions.map(s => s.studentId);

    // Fetch results for these tests
    const results = await prisma.examResult.findMany({
        where: {
            institutionId,
            testType: "PracticeTest",
            practiceTestId: { in: practiceTestIds },
            studentId: { in: activeStudentIds },
        },
        select: {
            obtainedMarks: true,
            totalMarks: true,
            createdAt: true,
            studentId: true,
            student: {
                select: {
                    user: { select: { firstName: true, lastName: true } }
                }
            }
        },
        orderBy: { createdAt: "desc" }
    });

    // Aggregate results: keep only the latest attempt per student
    const studentAggregateMap: Record<string, {
        studentId: string;
        name: string;
        totalObtainedMarks: number;
        totalMarks: number;
        latestAttemptAt: Date;
    }> = {};

    for (const r of results) {
        const sId = r.studentId;
        const testUser = r.student?.user;
        const name = testUser ? `${testUser.firstName} ${testUser.lastName}` : "Unknown Student";

        if (!studentAggregateMap[sId]) {
            studentAggregateMap[sId] = {
                studentId: sId,
                name,
                totalObtainedMarks: 0,
                totalMarks: 0,
                latestAttemptAt: r.createdAt,
            };
        }

        studentAggregateMap[sId].totalObtainedMarks += r.obtainedMarks;
        studentAggregateMap[sId].totalMarks += r.totalMarks;

        if (r.createdAt > studentAggregateMap[sId].latestAttemptAt) {
            studentAggregateMap[sId].latestAttemptAt = r.createdAt;
        }
    }

    const leaderboard = Object.values(studentAggregateMap).map(item => {
        const percentage = item.totalMarks > 0
            ? parseFloat(((item.totalObtainedMarks / item.totalMarks) * 100).toFixed(2))
            : 0;

        return {
            studentId: item.studentId,
            name: item.name,
            obtainedMarks: item.totalObtainedMarks,
            totalMarks: item.totalMarks,
            percentage,
            createdAtRaw: item.latestAttemptAt,
            createdAt: toIST(item.latestAttemptAt),
        };
    });

    // Sort for final ranking
    leaderboard.sort((a, b) => {
        if (b.percentage !== a.percentage) return b.percentage - a.percentage;
        if (b.obtainedMarks !== a.obtainedMarks) return b.obtainedMarks - a.obtainedMarks;
        return a.createdAtRaw.getTime() - b.createdAtRaw.getTime();
    });

    // Assign ranks + detect my rank
    let myRank: number | null = null;

    const ranked = leaderboard.map((item, index) => {
        const rank = index + 1;
        if (item.studentId === myStudentId) {
            myRank = rank;
        }
        return { rank, ...item, createdAtRaw: undefined };
    });

    // Final projection and filtering
    const top10 = ranked.slice(0, 10);
    res.json({
        leaderboard: top10,
        myRank
    });
});
