import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireRole } from "@/lib/auth";

type Params = { params: Promise<{ id: string }> };

/** Papers (routine rows) of one exam, sorted by date + start time. */
export async function GET(_req: Request, { params }: Params) {
  const session = await requireRole("INSTITUTION_ADMIN");
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { id } = await params;
  const exam = await prisma.exam.findFirst({
    where: { id: Number(id), OR: [{ institutionId: session.institutionId }, { branchId: session.institutionId }] },
    select: { id: true },
  });
  if (!exam) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const schedules = await prisma.examSchedule.findMany({
    where: { examId: exam.id },
    orderBy: [{ date: "asc" }, { startTime: "asc" }],
    include: {
      subjectItem: { select: { name: true, nameBn: true } },
      roomItem: { select: { name: true, nameBn: true } },
    },
  });

  return NextResponse.json({
    schedules: schedules.map(({ subjectItem, roomItem, ...s }) => ({
      ...s,
      subjectName: subjectItem?.name ?? null,
      subjectNameBn: subjectItem?.nameBn ?? null,
      roomName: roomItem?.name ?? null,
      roomNameBn: roomItem?.nameBn ?? null,
    })),
  });
}

/** Add a paper: { subjectItemId?, date, startTime, endTime, roomItemId?, fullMarks,
    totalPassMarks?, cqMarks?, cqPassMarks?, mcqMarks?, mcqPassMarks?, appliedMarks?,
    appliedPassMarks?, appliedDate?, note? }.
    The "passMarks" fields are the "Out of" (minimum passing) marks of each part.
    Rejects a subject that already has a paper in this exam, and a room booked
    at an overlapping time on the same date (any exam of the institution/branch). */
export async function POST(req: Request, { params }: Params) {
  const session = await requireRole("INSTITUTION_ADMIN");
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { id } = await params;
  const exam = await prisma.exam.findFirst({
    where: { id: Number(id), OR: [{ institutionId: session.institutionId }, { branchId: session.institutionId }] },
    select: { id: true, institutionId: true, schedulePublished: true },
  });
  if (!exam) return NextResponse.json({ error: "Not found" }, { status: 404 });
  if (exam.schedulePublished) {
    return NextResponse.json({ error: "SCHEDULE_PUBLISHED" }, { status: 409 });
  }

  try {
    const body = await req.json();
    const dateStr = String(body.date ?? "").slice(0, 10);
    const appliedStr = String(body.appliedDate ?? "").slice(0, 10);
    const start = String(body.startTime ?? "");
    const end = String(body.endTime ?? "");
    const num = (v: unknown) => {
      const n = Number(v);
      return Number.isFinite(n) && n > 0 ? n : null;
    };
    // optional marks: empty → null, otherwise a non-negative integer
    const marks = (v: unknown) => {
      if (v === undefined || v === null || v === "") return null;
      const n = Number(v);
      return Number.isFinite(n) && n >= 0 ? Math.round(n) : null;
    };
    const subjectItemId = num(body.subjectItemId);
    const roomItemId = num(body.roomItemId);
    const fullMarks = Number(body.fullMarks);
    const totalPassMarks = marks(body.totalPassMarks);
    const cqMarks = marks(body.cqMarks);
    const cqPassMarks = marks(body.cqPassMarks);
    const mcqMarks = marks(body.mcqMarks);
    const mcqPassMarks = marks(body.mcqPassMarks);
    const appliedMarks = marks(body.appliedMarks);
    const appliedPassMarks = marks(body.appliedPassMarks);

    if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr) || !/^\d{2}:\d{2}$/.test(start) || !/^\d{2}:\d{2}$/.test(end)) {
      return NextResponse.json({ error: "Missing or invalid fields" }, { status: 400 });
    }
    if (appliedStr && !/^\d{4}-\d{2}-\d{2}$/.test(appliedStr)) {
      return NextResponse.json({ error: "Invalid applied exam date" }, { status: 400 });
    }
    if (end <= start) {
      return NextResponse.json({ error: "END_BEFORE_START" }, { status: 400 });
    }
    if (!Number.isFinite(fullMarks) || fullMarks <= 0) {
      return NextResponse.json({ error: "Invalid full marks" }, { status: 400 });
    }
    // "Out of" = passing marks: can never exceed the part's full marks
    const passExceeds =
      (totalPassMarks !== null && totalPassMarks > fullMarks) ||
      (cqMarks !== null && cqPassMarks !== null && cqPassMarks > cqMarks) ||
      (mcqMarks !== null && mcqPassMarks !== null && mcqPassMarks > mcqMarks) ||
      (appliedMarks !== null && appliedPassMarks !== null && appliedPassMarks > appliedMarks);
    if (passExceeds) {
      return NextResponse.json({ error: "PASS_EXCEEDS_MARKS" }, { status: 400 });
    }

    // same subject twice in one exam
    if (subjectItemId !== null) {
      const dup = await prisma.examSchedule.findFirst({
        where: { examId: exam.id, subjectItemId },
        select: { id: true },
      });
      if (dup) return NextResponse.json({ error: "SUBJECT_EXISTS" }, { status: 409 });
    }

    // room clash: same room, same date, overlapping time — across the owning
    // institution's and the branch's exams
    if (roomItemId !== null) {
      const date = new Date(`${dateStr}T00:00:00Z`);
      const sameDay = await prisma.examSchedule.findMany({
        where: {
          roomItemId,
          date,
          OR: [{ institutionId: session.institutionId }, { institutionId: exam.institutionId }],
        },
        select: { startTime: true, endTime: true },
      });
      const clashes = sameDay.some((r) => start < r.endTime && r.startTime < end);
      if (clashes) return NextResponse.json({ error: "ROOM_CLASH" }, { status: 409 });
    }

    const schedule = await prisma.examSchedule.create({
      data: {
        institutionId: session.institutionId,
        examId: exam.id,
        subjectItemId,
        date: new Date(`${dateStr}T00:00:00Z`),
        startTime: start,
        endTime: end,
        roomItemId,
        fullMarks: Math.round(fullMarks),
        totalPassMarks,
        cqMarks,
        cqPassMarks,
        mcqMarks,
        mcqPassMarks,
        appliedMarks,
        appliedPassMarks,
        appliedDate: appliedStr ? new Date(`${appliedStr}T00:00:00Z`) : null,
        note: String(body.note ?? "").trim() || null,
      },
      include: {
        subjectItem: { select: { name: true, nameBn: true } },
        roomItem: { select: { name: true, nameBn: true } },
      },
    });
    return NextResponse.json({ schedule });
  } catch {
    return NextResponse.json({ error: "Could not add paper" }, { status: 500 });
  }
}
