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

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

/** Edit a paper: { subjectItemId?, date, startTime, endTime, roomItemId?, fullMarks,
    totalPassMarks?, cqMarks?, cqPassMarks?, mcqMarks?, mcqPassMarks?, appliedMarks?,
    appliedPassMarks?, appliedDate?, note? } — same rules as create.
    Editing (and deleting) is rejected while the exam's schedule is published;
    an admin must "allow edit" (unpublish) first. */
export async function PATCH(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 schedule = await prisma.examSchedule.findFirst({
    where: {
      id: Number(id),
      OR: [{ institutionId: session.institutionId }, { exam: { branchId: session.institutionId } }],
    },
    select: { id: true, examId: true, exam: { select: { schedulePublished: true, institutionId: true } } },
  });
  if (!schedule) return NextResponse.json({ error: "Not found" }, { status: 404 });
  if (schedule.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 (other papers only)
    if (subjectItemId !== null) {
      const dup = await prisma.examSchedule.findFirst({
        where: { examId: schedule.examId, subjectItemId, id: { not: schedule.id } },
        select: { id: true },
      });
      if (dup) return NextResponse.json({ error: "SUBJECT_EXISTS" }, { status: 409 });
    }

    // room clash: same room, same date, overlapping time — other papers only
    if (roomItemId !== null) {
      const date = new Date(`${dateStr}T00:00:00Z`);
      const sameDay = await prisma.examSchedule.findMany({
        where: {
          roomItemId,
          date,
          id: { not: schedule.id },
          OR: [{ institutionId: session.institutionId }, { institutionId: schedule.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 updated = await prisma.examSchedule.update({
      where: { id: schedule.id },
      data: {
        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: updated });
  } catch {
    return NextResponse.json({ error: "Could not update paper" }, { status: 500 });
  }
}

export async function DELETE(_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;
  // own papers, or papers of an exam assigned to this branch
  const schedule = await prisma.examSchedule.findFirst({
    where: {
      id: Number(id),
      OR: [{ institutionId: session.institutionId }, { exam: { branchId: session.institutionId } }],
    },
    select: { id: true, exam: { select: { schedulePublished: true } } },
  });
  if (!schedule) return NextResponse.json({ error: "Not found" }, { status: 404 });
  if (schedule.exam.schedulePublished) {
    return NextResponse.json({ error: "SCHEDULE_PUBLISHED" }, { status: 409 });
  }

  try {
    await prisma.examSchedule.delete({ where: { id: schedule.id } });
    return NextResponse.json({ ok: true });
  } catch {
    return NextResponse.json({ error: "Could not delete paper" }, { status: 500 });
  }
}
