import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireCampusInstitution } from "@/lib/auth";
import { cuGradeFor, cuGpa } from "@/lib/campus-grades";

/** Computed results — two views:

    ?examId=<id>   → semester result sheet: every enrolled student × every
                     paper with marks, letter grade, grade point, credits,
                     semester GPA (Σ point×credit ÷ Σ credit) and pass/fail,
                     graded on the program's scale (GPA4 = UGC/NU, GPA5 = HSC
                     board). Absent → 0 → F; a missing mark leaves the course
                     pending and the student's result "INCOMPLETE".

    ?studentId=<id>→ academic transcript: term-wise GPA from the student's
                     published final exams + cumulative CGPA. */
export async function GET(req: Request) {
  const session = await requireCampusInstitution();
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const sp = new URL(req.url).searchParams;
  const examId = Number(sp.get("examId"));
  const studentId = Number(sp.get("studentId"));

  /* ------------------------- semester result ------------------------- */
  if (Number.isFinite(examId) && examId > 0) {
    const exam = await prisma.cuExam.findFirst({
      where: { id: examId, institutionId: session.institutionId },
      include: {
        program: true,
        term: { select: { id: true, name: true, nameBn: true } },
        schedules: {
          orderBy: [{ examDate: "asc" }, { startTime: "asc" }, { id: "asc" }],
          include: {
            course: { select: { id: true, code: true, name: true, nameBn: true, credits: true } },
            marks: true,
          },
        },
      },
    });
    if (!exam) return NextResponse.json({ error: "Not found" }, { status: 404 });

    // eligible students (same rule as marks input)
    const courseIds = exam.schedules.map((s) => s.course.id);
    const enrollments =
      courseIds.length === 0
        ? []
        : await prisma.cuEnrollment.findMany({
            where: {
              institutionId: session.institutionId,
              status: "ENROLLED",
              offering: { courseId: { in: courseIds }, ...(exam.sessionYear ? { sessionYear: exam.sessionYear } : {}) },
              student: { status: "ACTIVE" },
            },
            select: { student: { select: { id: true, studentIdNo: true, name: true, nameBn: true, sessionYear: true, boardRollNo: true, registrationNo: true } } },
          });
    const students = [...new Map(enrollments.map((e) => [e.student.id, e.student])).values()]
      .sort((a, b) => a.studentIdNo.localeCompare(b.studentIdNo));

    const rows = students.map((st) => {
      const courses = exam.schedules.map((sc) => {
        const mk = sc.marks.find((m) => m.studentId === st.id);
        const full = sc.fullMarks;
        const pass = sc.passMarks;
        const obtained = mk ? (mk.absent ? 0 : mk.marks) : null;
        const has = obtained !== null;
        const percent = has ? (obtained! / full) * 100 : 0;
        const g = has ? cuGradeFor(exam.program.gradeScale, percent) : null;
        const failed = has ? obtained! < pass : false;
        return {
          scheduleId: sc.id,
          courseCode: sc.course.code,
          courseName: sc.course.name,
          courseNameBn: sc.course.nameBn,
          credits: sc.course.credits,
          fullMarks: full,
          passMarks: pass,
          marks: obtained,
          absent: mk?.absent ?? false,
          entered: has,
          percent: has ? Math.round(percent * 100) / 100 : null,
          grade: g?.grade ?? null,
          point: g?.point ?? null,
          failed,
        };
      });
      const graded = courses.filter((c) => c.entered);
      const gpa = cuGpa(graded.map((c) => ({ point: c.point!, credits: c.credits })));
      const failedAny = graded.some((c) => c.failed);
      const status = graded.length < courses.length ? "INCOMPLETE" : failedAny ? "FAILED" : "PASSED";
      return {
        student: st,
        courses,
        creditsAttempted: graded.reduce((s, c) => s + c.credits, 0),
        creditsEarned: graded.filter((c) => !c.failed).reduce((s, c) => s + c.credits, 0),
        totalMarks: graded.reduce((s, c) => s + (c.marks ?? 0), 0),
        fullMarksTotal: graded.reduce((s, c) => s + c.fullMarks, 0),
        gpa: graded.length ? Math.round(gpa * 100) / 100 : null,
        status,
      };
    });

    const gradedRows = rows.filter((r) => r.gpa !== null);
    return NextResponse.json({
      exam: {
        id: exam.id,
        name: exam.name,
        nameBn: exam.nameBn,
        examType: exam.examType,
        sessionYear: exam.sessionYear,
        startDate: exam.startDate,
        endDate: exam.endDate,
        resultPublished: exam.resultPublished,
        program: exam.program,
        term: exam.term,
      },
      rows,
      stats: {
        students: rows.length,
        passed: rows.filter((r) => r.status === "PASSED").length,
        failed: rows.filter((r) => r.status === "FAILED").length,
        incomplete: rows.filter((r) => r.status === "INCOMPLETE").length,
        highestGpa: gradedRows.length ? Math.max(...gradedRows.map((r) => r.gpa!)) : null,
        lowestGpa: gradedRows.length ? Math.min(...gradedRows.map((r) => r.gpa!)) : null,
      },
    });
  }

  /* --------------------------- transcript ---------------------------- */
  if (Number.isFinite(studentId) && studentId > 0) {
    const student = await prisma.cuStudent.findFirst({
      where: { id: studentId, institutionId: session.institutionId },
      include: { program: true },
    });
    if (!student) return NextResponse.json({ error: "Not found" }, { status: 404 });

    // published final exams of this student's program, oldest term first
    const exams = await prisma.cuExam.findMany({
      where: {
        institutionId: session.institutionId,
        programId: student.programId,
        resultPublished: true,
        examType: { in: ["SEMESTER_FINAL", "FINAL"] },
      },
      orderBy: [{ startDate: "asc" }, { id: "asc" }],
      include: {
        term: { select: { id: true, name: true, nameBn: true, sequence: true } },
        schedules: {
          orderBy: [{ examDate: "asc" }, { id: "asc" }],
          include: {
            course: { select: { id: true, code: true, name: true, nameBn: true, credits: true } },
            marks: { where: { studentId: student.id } },
          },
        },
      },
    });

    const terms: {
      examId: number;
      examName: string;
      termName: string;
      termNameBn: string | null;
      courses: { code: string; name: string; nameBn: string | null; credits: number; marks: number | null; fullMarks: number; grade: string; point: number }[];
      credits: number;
      gpa: number;
    }[] = [];

    for (const ex of exams) {
      const courses = ex.schedules.map((sc) => {
        const mk = sc.marks[0];
        const obtained = mk ? (mk.absent ? 0 : mk.marks) : null;
        const percent = obtained !== null ? (obtained / sc.fullMarks) * 100 : 0;
        const g = obtained !== null ? cuGradeFor(student.program.gradeScale, percent) : null;
        return {
          code: sc.course.code,
          name: sc.course.name,
          nameBn: sc.course.nameBn,
          credits: sc.course.credits,
          marks: obtained,
          fullMarks: sc.fullMarks,
          grade: g?.grade ?? "—",
          point: g?.point ?? 0,
        };
      });
      const graded = courses.filter((c) => c.marks !== null);
      if (graded.length === 0) continue;
      const gpa = cuGpa(graded.map((c) => ({ point: c.point, credits: c.credits })));
      terms.push({
        examId: ex.id,
        examName: ex.name,
        termName: ex.term?.name ?? "—",
        termNameBn: ex.term?.nameBn ?? null,
        courses,
        credits: graded.reduce((s, c) => s + c.credits, 0),
        gpa: Math.round(gpa * 100) / 100,
      });
    }

    const allCourses = terms.flatMap((t) => t.courses.filter((c) => c.marks !== null).map((c) => ({ point: c.point, credits: c.credits })));
    const cgpa = allCourses.length ? Math.round(cuGpa(allCourses) * 100) / 100 : null;
    const totalCredits = terms.reduce((s, t) => s + t.credits, 0);

    return NextResponse.json({
      student: {
        id: student.id,
        studentIdNo: student.studentIdNo,
        name: student.name,
        nameBn: student.nameBn,
        sessionYear: student.sessionYear,
        boardRollNo: student.boardRollNo,
        registrationNo: student.registrationNo,
        program: student.program,
      },
      terms,
      cgpa,
      totalCredits,
      programCredits: student.program.totalCredits,
    });
  }

  return NextResponse.json({ error: "Missing parameter" }, { status: 400 });
}
