import { NextResponse } from "next/server";
import { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
import { requireAnyRole } from "@/lib/auth";
import { STAFF_SIDE_ROLES } from "@/lib/auth";
import { BLOOD_GROUPS } from "@/lib/constants";
import { getManagerForUser, parseScope, scopeAllows } from "@/lib/identity";

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

/* Update one student's identity data (roll / registration / ID-card info).
   Admins may edit anyone; TEACHER/STAFF maintainers only students inside
   their assigned class scope. */
export async function PATCH(req: Request, { params }: Params) {
  const session = await requireAnyRole(["INSTITUTION_ADMIN", ...STAFF_SIDE_ROLES]);
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const { studentId: idParam } = await params;
  const studentId = Number(idParam);
  if (!studentId) {
    return NextResponse.json({ error: "Invalid student" }, { status: 400 });
  }

  // member roles are bound to their manager scope
  let scope: number[] | null = null;
  if (session.role !== "INSTITUTION_ADMIN") {
    const manager = await getManagerForUser(session.userId, session.institutionId);
    if (!manager) return NextResponse.json({ error: "FORBIDDEN" }, { status: 403 });
    scope = parseScope(manager.scopeClassItemIds);
  }

  try {
    const body = await req.json();
    const student = await prisma.student.findFirst({
      where: { id: studentId, institutionId: session.institutionId },
      select: { id: true, classItemId: true },
    });
    if (!student) return NextResponse.json({ error: "Not found" }, { status: 404 });
    if (!scopeAllows(scope, student.classItemId)) {
      return NextResponse.json({ error: "OUT_OF_SCOPE" }, { status: 403 });
    }

    const data: Prisma.StudentUpdateInput = {};

    if (body.roll !== undefined) {
      const roll = String(body.roll).trim();
      if (!roll) return NextResponse.json({ error: "ROLL_REQUIRED" }, { status: 400 });
      const taken = await prisma.student.findFirst({
        where: { institutionId: session.institutionId, roll, NOT: { id: studentId } },
        select: { id: true },
      });
      if (taken) return NextResponse.json({ error: "ROLL_EXISTS" }, { status: 409 });
      data.roll = roll;
    }

    if (body.registrationNo !== undefined) {
      const registrationNo = String(body.registrationNo).trim();
      if (registrationNo) {
        const taken = await prisma.student.findFirst({
          where: {
            institutionId: session.institutionId,
            registrationNo,
            NOT: { id: studentId },
          },
          select: { id: true },
        });
        if (taken) return NextResponse.json({ error: "REGISTRATION_EXISTS" }, { status: 409 });
      }
      data.registrationNo = registrationNo || null;
    }

    if (body.cardNo !== undefined) {
      data.cardNo = String(body.cardNo).trim() || null;
    }

    if (body.rfidCardNo !== undefined) {
      data.rfidCardNo = String(body.rfidCardNo).trim() || null;
    }

    if (body.bloodGroup !== undefined) {
      const bloodGroup = String(body.bloodGroup).trim();
      if (bloodGroup && !(BLOOD_GROUPS as readonly string[]).includes(bloodGroup)) {
        return NextResponse.json({ error: "INVALID_BLOOD_GROUP" }, { status: 400 });
      }
      data.bloodGroup = bloodGroup || null;
    }

    for (const key of ["cardIssuedAt", "cardValidTill"] as const) {
      if (body[key] !== undefined) {
        const raw = String(body[key]).trim();
        if (!raw) {
          data[key] = null;
        } else {
          const date = new Date(raw);
          if (isNaN(date.getTime())) {
            return NextResponse.json({ error: "INVALID_DATE" }, { status: 400 });
          }
          data[key] = date;
        }
      }
    }

    if (Object.keys(data).length === 0) {
      return NextResponse.json({ error: "NOTHING_TO_UPDATE" }, { status: 400 });
    }

    const updated = await prisma.student.update({
      where: { id: studentId },
      data,
      select: {
        id: true, roll: true, registrationNo: true, cardNo: true, rfidCardNo: true,
        cardIssuedAt: true, cardValidTill: true, bloodGroup: true,
      },
    });
    return NextResponse.json({ student: updated });
  } catch (e) {
    if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2002") {
      const target = String(e.meta?.target ?? "");
      const code = target.includes("registrationNo") ? "REGISTRATION_EXISTS" : "ROLL_EXISTS";
      return NextResponse.json({ error: code }, { status: 409 });
    }
    return NextResponse.json({ error: "Could not update identity" }, { status: 500 });
  }
}
