import type { Request, Response } from "express";
import asyncHandler from "express-async-handler";
import { prisma } from "../config/db.ts";
import { getHost } from "../utils/utils.ts";
import { toIST } from "../utils/time.ts";
import pdfPrinter from "pdfkit-table";
import PDFDocument from "pdfkit";
import fs from "fs";
import path from "path";
import dayjs from "dayjs";

export const submitExam = asyncHandler(async (req: Request, res: Response) => {
    const user = req.user;
    const userId = user.id;
    const institutionId = user.institutionId
    const studentInstitutionId = user.studentInstitutionId
    let { testId, testType, answers } = req.body;

    // Default to PracticeTest if not provided
    if (!testType) {
        testType = "PracticeTest";
    }

    // Validate inputs
    if (!testId || !answers) {
        res.status(400).json({ message: "testId & answers are required" });
        return;
    }

    let test: any = null;
    let questions: any[] = [];

    if (testType === "OldQuestionPaper") {
        test = await prisma.oldQuestionPaper.findUnique({
            where: { id: testId, institutionId: institutionId },
        });
        if (test) {
            const sourceId = test.referenceSourceId || test.id;
            const sourceTest = await prisma.oldQuestionPaper.findUnique({
                where: { id: sourceId },
                include: {
                    questions: {
                        orderBy: {
                            createdAt: 'asc'
                        }
                    }
                }
            });
            if (sourceTest) test.questions = sourceTest.questions;
        }
    } else {
        test = await prisma.practiceTest.findUnique({
            where: { id: testId, institutionId: institutionId },
        });
        if (test) {
            const ptWithQuestions = await prisma.practiceTest.findUnique({
                where: { id: testId },
                include: {
                    questions: {
                        orderBy: {
                            createdAt: 'asc'
                        }
                    }
                }
            });
            if (ptWithQuestions) test.questions = ptWithQuestions.questions;
        }
    }

    if (!test) {
        res.status(404).json({ message: "Test not found" });
        return;
    }

    if (testType === "PracticeTest" && test.testId) {
        let testRecord = await prisma.test.findUnique({ where: { id: test.testId } });
        let sourceTestId = test.testId;
        if (testRecord && testRecord.referenceSourceId) {
            sourceTestId = testRecord.referenceSourceId;
        }
        questions = await prisma.questionBank.findMany({
            where: {
                testQuestions: { some: { testId: sourceTestId } }
            },
            orderBy: {
                createdAt: 'asc'
            }
        });
    } else {
        questions = test.questions || [];
    }

    // Evaluation Variables
    let totalMarks = 0;
    let obtainedMarks = 0;
    let correctCount = 0;
    let wrongCount = 0;

    const validationDetails: any[] = [];

    questions.forEach((q: any) => {
        const questionId = q.id;
        const selectedAnswerIndex = answers[questionId];
        const correctIndex = q.correctAnswer;
        const mark = q.marks || 1;

        totalMarks += mark;

        const isCorrect = selectedAnswerIndex != null && selectedAnswerIndex == correctIndex;

        if (isCorrect) {
            correctCount++;
            obtainedMarks += mark;
        } else {
            wrongCount++;
        }

        const optionsArray = Array.isArray(q.options) ? q.options : [];

        validationDetails.push({
            questionId,
            questionText: q.questionText,
            questionImage: q.questionImage ? `${getHost()}${q.questionImage}` : "",
            options: optionsArray.map((option: any) => ({
                option: String(option.option) ?? "",
                optionImage: option.optionImage ? `${getHost()}${option.optionImage}` : "",
            })),
            correctAnswerIndex: correctIndex,
            difficulty: q.difficulty,
            selectedAnswerIndex: selectedAnswerIndex ?? null,
            isCorrect: isCorrect,
            explanation: q.explanation ?? "",
            explanationImage: q.explanationImage ? `${getHost()}${q.explanationImage}` : ""
        });
    });

    // Find student ID from User ID to link relation
    const studentInstitution = await prisma.studentInstitution.findFirst({
        where: { id: studentInstitutionId }
    });

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

    const computedPercentage = totalMarks > 0 ? (obtainedMarks / totalMarks) * 100 : 0;

    // Save Exam Result
    const resultData: any = {
        studentId: studentInstitution.studentId,
        testType,
        totalQuestions: questions.length,
        totalMarks,
        obtainedMarks,
        percentage: parseFloat(computedPercentage.toFixed(2)),
        answers: validationDetails.map((q: any) => ({
            questionId: q.questionId,
            selectedAnswerIndex: q.selectedAnswerIndex,
            isCorrect: q.isCorrect
        })),
        institutionId: institutionId,
        studentInstitutionId: studentInstitutionId
    };

    if (testType === "OldQuestionPaper") {
        resultData.oldQuestionPaperId = testId;
    } else {
        resultData.practiceTestId = testId;
    }

    await prisma.examResult.create({
        data: resultData
    });

    if (testType === "PracticeTest" && test.marks !== totalMarks) {
        await prisma.practiceTest.update({
            where: { id: testId },
            data: { marks: totalMarks }
        });
    }

    // Return Response
    res.status(200).json({
        message: "Test submitted successfully",
        result: {
            totalMarks,
            obtainedMarks,
            correctCount,
            wrongCount,
            percentage: computedPercentage.toFixed(2),
            validationDetails
        }
    });
});

export const getExamResults = asyncHandler(async (req: Request, res: Response) => {
    const user = req.user;
    const search = (req.query.search as string)?.trim();

    const examId = req.query.examId as string;
    const examType = req.query.examType as string;
    const subjectId = req.query.subjectId as string;
    const testTitle = (req.query.testTitle as string)?.trim();
    const startDate = req.query.startDate as string;
    const endDate = req.query.endDate as string;

    const sortBy = (req.query.sortBy as string) || "date";
    const sortOrder = (req.query.sortOrder as string)?.toLowerCase() === "asc" ? "asc" : "desc";

    const page = parseInt((req.query.page as string) || "1");
    const limit = parseInt((req.query.limit as string) || "10");
    const skip = (page - 1) * limit;

    let studentSearchCondition: any = undefined;
    if (search) {
        const searchWords = search.split(/\s+/).filter(Boolean);
        studentSearchCondition = {
            user: {
                OR: [
                    { firstName: { contains: search } },
                    { lastName: { contains: search } },
                    { email: { contains: search } },
                    ...(searchWords.length > 1 ? [{
                        AND: searchWords.map(word => ({
                            OR: [
                                { firstName: { contains: word } },
                                { lastName: { contains: word } }
                            ]
                        }))
                    }] : [])
                ]
            }
        };
    }

    let dateCondition: any = undefined;
    if (startDate || endDate) {
        dateCondition = {};
        if (startDate) dateCondition.gte = new Date(startDate);
        if (endDate) {
            const endOfDay = new Date(endDate);
            endOfDay.setHours(23, 59, 59, 999);
            dateCondition.lte = endOfDay;
        }
    }

    let examResultsRaw: any[] = [];
    let mockResultsRaw: any[] = [];

    let examWhereClause: any = { institutionId: user.institutionId };
    if (studentSearchCondition) {
        examWhereClause.studentInstitution = { student: studentSearchCondition };
    }
    if (dateCondition) examWhereClause.createdAt = dateCondition;

    if (examType === "PRACTICE") examWhereClause.practiceTestId = { not: null };
    if (examType === "OQP") examWhereClause.oldQuestionPaperId = { not: null };

    if (subjectId) {
        examWhereClause.practiceTest = {
            test: {
                subjectId: subjectId
            }
        };
    }

    const examOrConditions: any[] = [];
    if (examId) {
        examOrConditions.push({ practiceTest: { examId } }, { oldQuestionPaper: { examId } });
    }

    // Test Title Filter
    if (testTitle) {
        examOrConditions.push(
            { practiceTest: { title: { contains: testTitle } } }, 
            { oldQuestionPaper: { title: { contains: testTitle } } }
        );
    }
    if (examOrConditions.length > 0) examWhereClause.OR = examOrConditions;

    let mockWhereClause: any = { studentInstitution: { institutionId: user.institutionId } };
    if (studentSearchCondition) {
        mockWhereClause.studentInstitution.student = studentSearchCondition;
    }
    if (dateCondition) mockWhereClause.createdAt = dateCondition;

    if (examId) mockWhereClause.mockTest = { examId };
    if (testTitle) {
        mockWhereClause.mockTest = {
            ...mockWhereClause.mockTest,
            title: { contains: testTitle }
        };
    }

    const shouldFetchExams = !examType || examType === "PRACTICE" || examType === "OQP";
    const shouldFetchMocks = (!examType || examType === "MOCK") && !subjectId;

    await Promise.all([
        shouldFetchExams ? (async () => {
            examResultsRaw = await prisma.examResult.findMany({
                where: examWhereClause,
                include: {
                    studentInstitution: {
                        select: {
                            student: { include: { user: { select: { firstName: true, lastName: true, email: true } } } }
                        }
                    },
                    practiceTest: { 
                        select: { 
                            title: true, 
                            exam: true,
                            test: {
                                select: {
                                    subject: { select: { id: true, subjectName: true } }
                                }
                            }
                        } 
                    },
                    oldQuestionPaper: { select: { title: true, exam: true } }
                }
            });
        })() : Promise.resolve(),

        shouldFetchMocks ? (async () => {
            mockResultsRaw = await prisma.mockTestResult.findMany({
                where: mockWhereClause,
                include: {
                    studentInstitution: {
                        select: {
                            student: { include: { user: { select: { firstName: true, lastName: true, email: true } } } }
                        }
                    },
                    mockTest: { select: { title: true, exam: true, questionCount: true } }
                }
            });
        })() : Promise.resolve()
    ]);

    const formattedExams = examResultsRaw.map(res => {
        const student = res.studentInstitution?.student;
        const totalMarks = res.totalMarks || 0;
        const obtainedMarks = res.obtainedMarks || 0;
        const percentage = totalMarks > 0 ? parseFloat(((obtainedMarks / totalMarks) * 100).toFixed(2)) : 0;

        return {
            id: res.id,
            testTitle: res.practiceTest?.title || res.oldQuestionPaper?.title || "Deleted Test",
            studentName: student?.user ? `${student.user.firstName} ${student.user.lastName}`.trim() : "Deleted Student",
            email: student?.user?.email || "",
            examName: res.practiceTest?.exam?.examName || res.oldQuestionPaper?.exam?.examName || "Deleted Exam",
            subjectName: res.practiceTest?.test?.subject?.subjectName || null, 
            examType: res.practiceTest ? "PRACTICE" : "OQP",
            createdAt: res.createdAt,
            totalQuestions: res.totalQuestions,
            obtainedMarks,
            totalMarks,
            percentage
        };
    });

    const formattedMocks = mockResultsRaw.map(res => {
        const student = res.studentInstitution?.student;
        const totalMarks = res.totalMarks || 0;
        const obtainedMarks = res.obtainedMarks || 0;
        const percentage = totalMarks > 0 ? parseFloat(((obtainedMarks / totalMarks) * 100).toFixed(2)) : 0;

        return {
            id: res.id,
            testTitle: res.mockTest?.title || "Deleted Test",
            studentName: student?.user ? `${student.user.firstName} ${student.user.lastName}`.trim() : "Deleted Student",
            email: student?.user?.email || "",
            examName: res.mockTest?.exam?.examName || "Deleted Exam",
            subjectName: null, 
            examType: "MOCK",
            createdAt: res.createdAt,
            totalQuestions: res.mockTest?.questionCount || 0,
            obtainedMarks,
            totalMarks,
            percentage
        };
    });

    const combinedResults = [...formattedExams, ...formattedMocks];

    // IN-MEMORY SORTING LOGIC
    combinedResults.sort((a, b) => {
        let valueA: any;
        let valueB: any;

        switch (sortBy) {
            case "title":
                valueA = a.testTitle.toLowerCase();
                valueB = b.testTitle.toLowerCase();
                return sortOrder === "asc" ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA);
            case "score":
            case "percentage":
                valueA = a.percentage;
                valueB = b.percentage;
                break;
                break;
            case "date":
            default:
                valueA = a.createdAt.getTime();
                valueB = b.createdAt.getTime();
                break;
        }

        if (valueA < valueB) return sortOrder === "asc" ? -1 : 1;
        if (valueA > valueB) return sortOrder === "asc" ? 1 : -1;
        return 0;
    });

    const total = combinedResults.length;

    const paginatedResults = combinedResults.slice(skip, skip + limit).map(res => ({
        ...res,
        createdAt: toIST(res.createdAt),
        percentage: `${res.percentage.toFixed(2)}%` 
    }));

    res.status(200).json({
        message: "Test results fetched successfully",
        data: paginatedResults,
        meta: {
            total,
            page,
            limit,
            totalPages: Math.ceil(total / limit)
        }
    });
});

export const getLastTest = asyncHandler(async (req: Request, res: Response) => {
    const user = (req as any).user;
    const studentInstitutionId = user.studentInstitutionId;
    const { type: reqType } = req.query;

    const testType = reqType === "PYQ" ? "PYQ" : "PRACTICE";

    const responseData: any = {
        testTitle: "",
        createdAt: "",
        obtainedMarks: 0,
        totalMarks: 0,
        correctCount: 0,
        wrongCount: 0,
        skippedCount: 0,
        percentage: "0.00",
        validationDetails: [],
    };

    const student = await prisma.student.findFirst({
        where: {
            userId: user.id,
        },
    });

    if (!student) {
        res.status(404).json({
            message: "Student not found",
        });
        return;
    }

    const studentInstitution = studentInstitutionId
        ? await prisma.studentInstitution.findUnique({
            where: { id: studentInstitutionId },
            select: { examsId: true, language: true, student: { select: { language: true } } },
        })
        : null;

    const motherTongue = studentInstitution?.student?.language || studentInstitution?.language || "";
    const filterLanguage = studentInstitution?.student?.language ? (studentInstitution?.language || "English") : "English";

    const enrolledExamId = studentInstitution?.examsId ?? null;

    let questions: any[] = [];
    let testAnswers: any[] = [];
    let testLanguage: string | undefined | null = null;
    let markPerQuestion = 1;

    if (testType === "PYQ") {
        const pyqFilter: any = {};
        if (enrolledExamId) pyqFilter.examId = enrolledExamId;

        if (filterLanguage || motherTongue) {
            const langOr: any[] = [];
            if (filterLanguage) {
                langOr.push({ language: filterLanguage });
                langOr.push({ referenceSource: { language: filterLanguage } });
            }
            if (motherTongue && motherTongue !== filterLanguage) {
                langOr.push({ language: motherTongue });
                langOr.push({ referenceSource: { language: motherTongue } });
            }
            if (langOr.length > 0) pyqFilter.OR = langOr;
        }

        const oldQuestionPaperResult = await prisma.examResult.findFirst({
            where: {
                testType: "OldQuestionPaper",
                studentInstitutionId: studentInstitutionId,
                oldQuestionPaper: pyqFilter,
            },
            include: {
                oldQuestionPaper: {
                    include: {
                        questions: true,
                    },
                },
            },
            orderBy: {
                createdAt: "desc",
            },
        });

        if (!oldQuestionPaperResult) {
            res.status(404).json({
                message: "No PYQ test found for this exam",
            });
            return;
        }

        const oldQuestionPaperTitle = oldQuestionPaperResult.oldQuestionPaper?.title || "";
        const oldQuestionPaperYear = oldQuestionPaperResult.oldQuestionPaper?.year;
        responseData.testTitle = oldQuestionPaperYear
            ? `${oldQuestionPaperTitle} (${oldQuestionPaperYear})`
            : oldQuestionPaperTitle;
        responseData.year = oldQuestionPaperYear || "";
        responseData.createdAt = oldQuestionPaperResult.createdAt;
        questions = oldQuestionPaperResult.oldQuestionPaper?.questions || [];
        if (questions.length === 0 && oldQuestionPaperResult.oldQuestionPaper?.referenceSourceId) {
            const sourcePYQ = await prisma.oldQuestionPaper.findUnique({
                where: { id: oldQuestionPaperResult.oldQuestionPaper.referenceSourceId },
                include: { questions: true }
            });
            if (sourcePYQ) questions = sourcePYQ.questions;
        }
        testAnswers = Array.isArray(oldQuestionPaperResult.answers) ? oldQuestionPaperResult.answers : [];
        testLanguage = oldQuestionPaperResult.oldQuestionPaper?.language;
    } else {
        const practiceTestFilter: any = {};
        if (enrolledExamId) practiceTestFilter.examId = enrolledExamId;

        if (filterLanguage || motherTongue) {
            const langOr: any[] = [];
            if (filterLanguage) {
                langOr.push({ language: filterLanguage, test: { subject: { language: null } } });
                langOr.push({ language: filterLanguage, testId: null });
                langOr.push({ test: { language: filterLanguage, subject: { language: null } } });
                langOr.push({ test: { referenceSource: { language: filterLanguage } } });
            }
            if (motherTongue) {
                langOr.push({ questions: { some: { subject: { language: motherTongue } } } });
                langOr.push({ test: { subject: { language: motherTongue } } });
                langOr.push({ test: { referenceSource: { subject: { language: motherTongue } } } });
            }
            if (langOr.length > 0) practiceTestFilter.OR = langOr;
        }

        const practiceTestResult = await prisma.examResult.findFirst({
            where: {
                studentInstitutionId: studentInstitutionId,
                testType: "PracticeTest",
                practiceTest: practiceTestFilter,
            },
            include: {
                practiceTest: {
                    include: {
                        questions: { include: { subject: true } },
                        test: {
                            include: {
                                testQuestions: {
                                    include: { question: { include: { subject: true } } }
                                }
                            }
                        }
                    },
                },
            },
            orderBy: {
                createdAt: "desc",
            },
        });

        if (!practiceTestResult) {
            res.status(404).json({
                message: "No practice test found for this exam",
            });
            return;
        }

        responseData.testTitle = practiceTestResult.practiceTest?.title || "";
        responseData.createdAt = practiceTestResult.createdAt;
        
        questions = practiceTestResult.practiceTest?.questions || [];
        if (questions.length === 0 && practiceTestResult.practiceTest?.test) {
            let testQuestions = practiceTestResult.practiceTest.test.testQuestions || [];
            if (testQuestions.length === 0 && practiceTestResult.practiceTest.test.referenceSourceId) {
                const sourceTest = await prisma.test.findUnique({
                    where: { id: practiceTestResult.practiceTest.test.referenceSourceId },
                    include: { testQuestions: { include: { question: { include: { subject: true } } } } }
                });
                if (sourceTest) testQuestions = sourceTest.testQuestions;
            }
            questions = testQuestions.map((tq: any) => tq.question);
        }
        
        testAnswers = Array.isArray(practiceTestResult.answers) ? practiceTestResult.answers : [];
        testLanguage = practiceTestResult.practiceTest?.language;

        const totalM = practiceTestResult.practiceTest?.marks || 0;
        const qCount = questions.length;
        markPerQuestion = (totalM > 0 && qCount > 0) ? (totalM / qCount) : 1;
    }

    // Calculate Results
    questions.forEach((q: any) => {
        const questionId = q.id;
        const selectedAnswer = testAnswers.find((t: any) => t.questionId === questionId);
        
        const selectedAnswerIndex = (selectedAnswer?.selectedAnswerIndex !== undefined && selectedAnswer?.selectedAnswerIndex !== null) 
            ? Number(selectedAnswer.selectedAnswerIndex) 
            : null;

        const correctIndex = q.correctAnswer;
        const mark = markPerQuestion;
        responseData.totalMarks += mark;

        let isCorrect = false;

        if (selectedAnswerIndex === null) {
            responseData.skippedCount++;
        } else if (selectedAnswerIndex === correctIndex) {
            isCorrect = true;
            responseData.correctCount++;
            responseData.obtainedMarks += mark;
        } else {
            responseData.wrongCount++;
        }

        const optionsArray = Array.isArray(q.options) ? q.options : [];

        responseData.validationDetails.push({
            questionId,
            questionText: q.questionText,
            questionImage: q.questionImage ? `${getHost()}${q.questionImage}` : "",
            options: optionsArray.map((option: any) => ({
                option: option?.option || "",
                optionImage: option?.optionImage ? `${getHost()}${option.optionImage}` : "",
            })),
            correctAnswerIndex: correctIndex,
            difficulty: q.difficulty,
            selectedAnswerIndex,
            isCorrect,
            explanation: q.explanation || "",
            explanationImage: q.explanationImage ? `${getHost()}${q.explanationImage}` : "",
        });
    });

    responseData.percentage = responseData.totalMarks > 0 
        ? ((responseData.obtainedMarks / responseData.totalMarks) * 100).toFixed(2) 
        : "0.00";

    res.status(200).json({
        message: "Last test fetched successfully",
        result: responseData
    });
});

export const scoreReportsPDF = asyncHandler(async (req: Request, res: Response) => {
    const user = req.user;

    let institutionNameFromRequest: string | null = null;
    try {
        if (user?.institutionId) {
            const inst = await prisma.institution.findUnique({
                where: { id: user.institutionId },
                select: { user: { select: { institutionName: true, firstName: true, email: true } } }
            });
            institutionNameFromRequest = inst?.user?.institutionName || inst?.user?.firstName || inst?.user?.email || null;
        }
    } catch (e) {
        institutionNameFromRequest = null;
    }
    const search = (req.query.search as string)?.trim();
    const examId = req.query.examId as string;
    const examType = req.query.examType as string;
    const subjectId = req.query.subjectId as string;
    const startDate = req.query.startDate as string;
    const endDate = req.query.endDate as string;
    const sortBy = (req.query.sortBy as string) || "date";
    const sortOrder = (req.query.sortOrder as string)?.toLowerCase() === "asc" ? "asc" : "desc";

    let whereClause: any = {
        institutionId: user.institutionId
    };

    if (search) {
        whereClause.student = {
            user: {
                OR: [
                    { firstName: { contains: search } },
                    { lastName: { contains: search } },
                    { email: { contains: search } }
                ]
            }
        };
    }

    if (examType === "PRACTICE") {
        whereClause.practiceTestId = { not: null };
    } else if (examType === "OQP") {
        whereClause.oldQuestionPaperId = { not: null };
    }

    if (subjectId) {
        whereClause.practiceTest = {
            test: {
                subjectId: subjectId
            }
        };
    }

    if (examId) {
        whereClause.OR = [
            { practiceTest: { examId: examId } },
            { oldQuestionPaper: { examId: examId } }
        ];
    }

    if (startDate || endDate) {
        whereClause.createdAt = {};
        if (startDate) whereClause.createdAt.gte = new Date(startDate);
        if (endDate) {
            const endOfDay = new Date(endDate);
            endOfDay.setHours(23, 59, 59, 999);
            whereClause.createdAt.lte = endOfDay;
        }
    }

    const shouldFetchExams = !examType || examType === "PRACTICE" || examType === "OQP";
    const shouldFetchMocks = (!examType || examType === "MOCK") && !subjectId;

    let examResults: any[] = [];
    let mockResults: any[] = [];

    await Promise.all([
        shouldFetchExams ? (async () => {
            examResults = await prisma.examResult.findMany({
                where: whereClause,
                include: {
                    studentInstitution: {
                        select: {
                            student: { include: { user: { select: { firstName: true, lastName: true, email: true } } } }
                        }
                    },
                    practiceTest: {
                        select: {
                            title: true,
                            exam: true,
                            test: {
                                select: {
                                    subject: {
                                        select: {
                                            subjectName: true
                                        }
                                    }
                                }
                            }
                        }
                    },
                    oldQuestionPaper: { select: { title: true, exam: true } }
                }
            });
        })() : Promise.resolve(),

        shouldFetchMocks ? (async () => {
            let mockWhereClause: any = { studentInstitution: { institutionId: user.institutionId } };
            
            if (search) {
                mockWhereClause.studentInstitution = {
                    student: whereClause.student
                };
            }
            if (examId) mockWhereClause.mockTest = { examId };
            if (startDate || endDate) mockWhereClause.createdAt = whereClause.createdAt;

            mockResults = await prisma.mockTestResult.findMany({
                where: mockWhereClause,
                include: {
                    studentInstitution: {
                        select: {
                            student: { include: { user: { select: { firstName: true, lastName: true, email: true } } } }
                        }
                    },
                    mockTest: { select: { title: true, exam: true } }
                }
            });
        })() : Promise.resolve()
    ]);

    const combinedResults = [...examResults, ...mockResults].sort((a, b) => {
        const totalA = a.totalMarks || 0;
        const totalB = b.totalMarks || 0;
        const pctA = totalA > 0 ? (a.obtainedMarks / totalA) * 100 : 0;
        const pctB = totalB > 0 ? (b.obtainedMarks / totalB) * 100 : 0;
        const titleA = (a.practiceTest?.title || a.oldQuestionPaper?.title || a.mockTest?.title || "").toLowerCase();
        const titleB = (b.practiceTest?.title || b.oldQuestionPaper?.title || b.mockTest?.title || "").toLowerCase();

        let valueA: any;
        let valueB: any;
        switch (sortBy) {
            case "title":
                return sortOrder === "asc" ? titleA.localeCompare(titleB) : titleB.localeCompare(titleA);
            case "score":
            case "percentage":
                valueA = pctA;
                valueB = pctB;
                break;
            case "date":
            default:
                valueA = new Date(a.createdAt).getTime();
                valueB = new Date(b.createdAt).getTime();
                break;
        }
        if (valueA < valueB) return sortOrder === "asc" ? -1 : 1;
        if (valueA > valueB) return sortOrder === "asc" ? 1 : -1;
        return 0;
    });

    const rows = combinedResults.map((result, index) => {
        const isMock = !!result.mockTest;
        const student = result.studentInstitution?.student;
        const studentName = student?.user ? `${student.user.firstName} ${student.user.lastName}`.trim() : "Deleted Student";
        const email = student?.user ? student.user.email : "";
        
        let testTitle = "Deleted Test";
        let examName = "Deleted Exam";
        let typeStr = "";

        if (isMock) {
            testTitle = result.mockTest?.title || "Deleted Mock";
            examName = result.mockTest?.exam?.examName || "Deleted Exam";
            typeStr = "MOCK TEST";
        } else {
            const practiceTest = result.practiceTest;
            const oldQuestionPaper = result.oldQuestionPaper;
            testTitle = practiceTest?.title || oldQuestionPaper?.title || "Deleted Test";
            examName = practiceTest?.exam?.examName || oldQuestionPaper?.exam?.examName || "Deleted Exam";
            if (practiceTest) {
                const subjectName = practiceTest.test?.subject?.subjectName;
                typeStr = subjectName ? `PRACTICE TEST\n(${subjectName})` : "PRACTICE TEST";
            } else {
                typeStr = "PYQ";
            }
        }

        const dateStr = dayjs(result.createdAt).format("DD MMM YYYY, hh:mm A");
        const scoreStr = `${result.obtainedMarks} / ${result.totalMarks}`;
        const percentage = result.totalMarks > 0 ? ((result.obtainedMarks / result.totalMarks) * 100).toFixed(1) : "0.0";
        return [
            `${index + 1}`,
            `${studentName}\n(${email})`,
            examName,
            typeStr,
            testTitle,
            dateStr,
            scoreStr,
            `${percentage}%`
        ];
    });

    const doc = new PDFDocument({ size: "A4", layout: "landscape", margin: 0 });

    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `attachment; filename=Reports_Scores_${dayjs().format("YYYY-MM-DD")}.pdf`);
    doc.pipe(res);

    try {
        doc.registerFont('NotoSansTamil', path.join(process.cwd(), 'node_modules', '@expo-google-fonts', 'noto-sans-tamil', '400Regular', 'NotoSansTamil_400Regular.ttf'));
        doc.registerFont('NotoSansDevanagari', path.join(process.cwd(), 'node_modules', '@expo-google-fonts', 'noto-sans-devanagari', '400Regular', 'NotoSansDevanagari_400Regular.ttf'));

        const getFontForText = (text: string, isBold: boolean = false) => {
            const hasTamil = /[\u0B80-\u0BFF]/.test(text);
            const hasHindi = /[\u0900-\u097F]/.test(text);
            if (hasTamil) return "NotoSansTamil";
            if (hasHindi) return "NotoSansDevanagari";
            return isBold ? "Helvetica-Bold" : "Helvetica";
        };

        const primary = "#1976d2";
        const white = "#ffffff";
        const altRow = "#f5f7fa";
        const textColor = "#1f2937";
        const headerTextMuted = "#cbd5e1";

        const PW = doc.page.width;
        const PH = doc.page.height;
        const margin = 24;

        const drawPageBg = () => {
            doc.save();
            doc.rect(0, 0, PW, PH).fill("#eef1f5");
            doc.roundedRect(margin - 6, margin - 6, PW - (margin - 6) * 2, PH - (margin - 6) * 2, 8).fill(white);
            doc.restore();
        };

        drawPageBg();
        doc.on("pageAdded", () => { drawPageBg(); });

        const logoPath = path.resolve(process.cwd(), "public", "logo.png");
        const hasLogo = fs.existsSync(logoPath);

        const headerH = 66;
        doc.roundedRect(margin, margin, PW - margin * 2, headerH, 10).fill(primary);

        if (hasLogo) {
            doc.roundedRect(margin + 14, margin + 12, 40, 40, 6).fill(white);
            try {
                doc.image(logoPath, margin + 18, margin + 16, { fit: [32, 32], align: "center", valign: "center" });
            } catch (imgError) {
                console.error("Failed to render logo image:", imgError);
            }
        }

        const textStartX = hasLogo ? margin + 64 : margin + 16;

        doc.fillColor(white).font("Helvetica-Bold").fontSize(20)
            .text("Exam Infra", textStartX, margin + 14);
        doc.font("Helvetica").fontSize(9).fillColor(headerTextMuted)
            .text("Reports & Scores Summary", textStartX, margin + 38);

        if (institutionNameFromRequest) {
            doc.font(getFontForText(institutionNameFromRequest)).fontSize(9).fillColor(white).text(`Institution: ${institutionNameFromRequest}`, textStartX, margin + 52);
        }

        doc.font("Helvetica-Bold").fontSize(9).fillColor(white)
            .text(`Generated: ${dayjs().format("DD MMM YYYY, hh:mm A")}`, PW - margin - 230, margin + 14, { width: 220, align: "right" });
        doc.font("Helvetica").fontSize(9).fillColor(headerTextMuted)
            .text(`Total Records: ${rows.length}`, PW - margin - 230, margin + 36, { width: 220, align: "right" });

        const tableTop = margin + headerH + 14;
        const tableLeft = margin;
        const tableWidth = PW - margin * 2;

        const baseWidths = [35, 145, 105, 100, 120, 110, 55, 80];
        const baseTotal = baseWidths.reduce((a, b) => a + b, 0);
        const colWidths = baseWidths.map(w => (w / baseTotal) * tableWidth);

        const headers = ["S.No", "Student Info", "Exam Name", "Test Type", "Test Title", "Date Completed", "Score", "Percentage"];
        const headerRowH = 26;
        const rowPad = 6;
        const cellFontSize = 8.5;

        doc.rect(tableLeft, tableTop, tableWidth, headerRowH).fill(primary);
        doc.font("Helvetica-Bold").fontSize(9).fillColor(white);
        let cx = tableLeft;
        headers.forEach((h, i) => {
            const textH = doc.heightOfString(h, { width: colWidths[i] - 10 });
            const vOffset = (headerRowH - textH) / 2;
            doc.text(h, cx + 5, tableTop + vOffset, { width: colWidths[i] - 10, align: "left" });
            cx += colWidths[i];
        });

        let rowY = tableTop + headerRowH;

        const drawTableHeader = (y: number) => {
            doc.rect(tableLeft, y, tableWidth, headerRowH).fill(primary);
            doc.font("Helvetica-Bold").fontSize(9).fillColor(white);
            let hx = tableLeft;
            headers.forEach((h, i) => {
                const textH = doc.heightOfString(h, { width: colWidths[i] - 10 });
                const vOffset = (headerRowH - textH) / 2;
                doc.text(h, hx + 5, y + vOffset, { width: colWidths[i] - 10, align: "left" });
                hx += colWidths[i];
            });
            return y + headerRowH;
        };

        rows.forEach((row, idx) => {
            const rowH = Math.max(28, ...row.map((cell, ci) => {
                doc.font(getFontForText(String(cell))).fontSize(cellFontSize);
                return doc.heightOfString(String(cell), { width: colWidths[ci] - 10 }) + rowPad * 2;
            }));

            if (rowY + rowH > PH - margin - 20) {
                doc.addPage();
                rowY = margin + 8;
                rowY = drawTableHeader(rowY);
            }

            doc.rect(tableLeft, rowY, tableWidth, rowH).fill(idx % 2 === 0 ? altRow : white);
            doc.moveTo(tableLeft, rowY + rowH).lineTo(tableLeft + tableWidth, rowY + rowH)

            let rx = tableLeft;
            row.forEach((cell, ci) => {
                doc.font(getFontForText(String(cell))).fontSize(cellFontSize).fillColor(textColor);
                const textH = doc.heightOfString(String(cell), { width: colWidths[ci] - 10 });
                const vOffset = (rowH - textH) / 2;
                doc.text(String(cell), rx + 5, rowY + vOffset, { width: colWidths[ci] - 10 });
                rx += colWidths[ci];
            });

            rowY += rowH;
        });

        doc.rect(tableLeft, tableTop, tableWidth, rowY - tableTop)

        doc.end();
    } catch (error) {
        console.error("Error generating PDF stream:", error);
        doc.destroy();
        if (!res.headersSent) {
            res.status(500).json({ message: "Failed to generate PDF report" });
        }
    }
});