import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireAnyRole } from "@/lib/auth";
import { getUploadPolicy, maxDataUrlLength } from "@/lib/upload-policy";

type Params = { params: Promise<{ id: string }> };

/** Full student profile report: info + class/section + fee statement. */
export async function GET(_req: Request, { params }: Params) {
  const session = await requireAnyRole(["INSTITUTION_ADMIN", "TEACHER", "STUDENT", "PARENT"]);
  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const { id } = await params;

  // member roles may only read their own (child's) profile
  if ((session.role === "STUDENT" || session.role === "PARENT") && !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  if (session.role === "STUDENT" || session.role === "PARENT") {
    const me = await prisma.user.findUnique({ where: { id: session.userId } });
    const ownId = session.role === "STUDENT" ? me?.studentProfileId : me?.parentOfStudentId;
    if (ownId !== Number(id)) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }
  }

  const student = await prisma.student.findFirst({
    where: {
      id: Number(id),
      ...(session.role === "INSTITUTION_ADMIN" || session.role === "TEACHER"
        ? { institutionId: session.institutionId! }
        : {}),
    },
    include: {
      classItem: { select: { name: true, nameBn: true } },
      sectionItem: { select: { name: true, nameBn: true } },
      admission: { select: { id: true, createdAt: true } },
      optionalSubjects: {
        include: { subjectItem: { select: { id: true, name: true, nameBn: true } } },
      },
      feeSchedules: {
        orderBy: { dueDate: "asc" },
        include: {
          feeHead: { select: { name: true, nameBn: true, type: true } },
          payments: { select: { amount: true } },
        },
      },
    },
  });
  if (!student) return NextResponse.json({ error: "Not found" }, { status: 404 });

  const schedules = student.feeSchedules.map((fs) => {
    const paid = fs.payments.reduce((sum, p) => sum + Number(p.amount), 0);
    const total = Number(fs.amount);
    return {
      id: fs.id,
      title: fs.title,
      headName: fs.feeHead.name,
      headNameBn: fs.feeHead.nameBn,
      type: fs.feeHead.type,
      amount: total,
      paid,
      due: Math.max(0, total - paid),
      status: paid >= total ? "PAID" : paid > 0 ? "PARTIAL" : "DUE",
      dueDate: fs.dueDate,
      month: fs.month,
    };
  });

  return NextResponse.json({
    student: {
      id: student.id,
      roll: student.roll,
      registrationNo: student.registrationNo,
      cardNo: student.cardNo,
      cardIssuedAt: student.cardIssuedAt,
      cardValidTill: student.cardValidTill,
      bloodGroup: student.bloodGroup,
      name: student.name,
      nameBn: student.nameBn,
      gender: student.gender,
      dob: student.dob,
      guardianName: student.guardianName,
      guardianNameBn: student.guardianNameBn,
      guardianPhone: student.guardianPhone,
      motherNameBn: student.motherNameBn,
      address: student.address,
      photoUrl: student.photoUrl ?? null,
      status: student.status,
      createdAt: student.createdAt,
      admittedOn: student.admission?.createdAt ?? student.createdAt,
      className: student.classItem?.name ?? null,
      classNameBn: student.classItem?.nameBn ?? null,
      sectionName: student.sectionItem?.name ?? null,
      sectionNameBn: student.sectionItem?.nameBn ?? null,
      optionalSubjects: student.optionalSubjects.map((o) => o.subjectItem),
    },
    schedules,
    totals: {
      assigned: schedules.reduce((s, x) => s + x.amount, 0),
      paid: schedules.reduce((s, x) => s + x.paid, 0),
      due: schedules.reduce((s, x) => s + x.due, 0),
    },
  });
}

const PROFILE_FIELDS = [
  "name",
  "nameBn",
  "gender",
  "guardianName",
  "guardianNameBn",
  "guardianPhone",
  "motherName",
  "motherNameBn",
  "address",
  "photoUrl",
] as const;

/** Student Profile Update System — admins and teachers may update any
    student of their institution; student/parent accounts only their own
    profile. Accepts personal-info fields, photo (data URL), dob and the
    optional-subject allocation (validated against the class pool). */
export async function PATCH(req: Request, { params }: Params) {
  const session = await requireAnyRole(["INSTITUTION_ADMIN", "TEACHER", "STUDENT", "PARENT"]);
  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const { id } = await params;
  const studentId = Number(id);

  // resolve access scope
  let institutionId: number | null = null;
  if (session.role === "INSTITUTION_ADMIN" || session.role === "TEACHER") {
    institutionId = session.institutionId;
  } else {
    const me = await prisma.user.findUnique({ where: { id: session.userId } });
    const ownId = session.role === "STUDENT" ? me?.studentProfileId : me?.parentOfStudentId;
    if (ownId !== studentId) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }
  }

  try {
    const body = await req.json();
    const student = await prisma.student.findFirst({
      where: institutionId ? { id: studentId, institutionId } : { id: studentId },
      select: { id: true, classItemId: true, roll: true, institutionId: true },
    });
    if (!student) return NextResponse.json({ error: "Not found" }, { status: 404 });

    const data: Record<string, unknown> = {};
    for (const key of PROFILE_FIELDS) {
      if (body[key] !== undefined) data[key] = body[key] === "" ? null : String(body[key]);
    }
    if (body.name !== undefined && !String(body.name).trim()) {
      return NextResponse.json({ error: "Name is required" }, { status: 400 });
    }
    if (body.photoUrl !== undefined && body.photoUrl !== null) {
      const photo = String(body.photoUrl);
      const policy = await getUploadPolicy();
      if (photo && !photo.startsWith("data:image/")) {
        return NextResponse.json({ error: "Invalid photo" }, { status: 400 });
      }
      if (photo && photo.length > maxDataUrlLength(policy.imageMaxKb)) {
        return NextResponse.json({ error: "PHOTO_TOO_LARGE" }, { status: 400 });
      }
      data.photoUrl = photo || null;
    }
    if (body.dob !== undefined) {
      data.dob = body.dob ? new Date(String(body.dob)) : null;
    }

    // academic identity: group (school/college) or department (university)
    for (const [field, category] of [["groupItemId", "GROUP"], ["departmentItemId", "DEPARTMENT"]] as const) {
      if (body[field] !== undefined) {
        const n = Number(body[field]);
        if (Number.isFinite(n) && n > 0) {
          const item = await prisma.academicItem.findFirst({
            where: { id: n, category, isActive: true },
            select: { id: true },
          });
          if (!item) return NextResponse.json({ error: `Invalid ${category}` }, { status: 400 });
          data[field] = n;
        } else {
          data[field] = null;
        }
      }
    }

    // optional subjects must come from the class pool
    let optionalSubjects: { id: number; name: string; nameBn: string }[] = [];
    if (Array.isArray(body.optionalSubjectIds)) {
      const wanted: number[] = [
        ...new Set(
          (body.optionalSubjectIds as unknown[]).map((v) => Number(v)).filter((n) => Boolean(n))
        ),
      ];
      const pool = await prisma.classOptionalSubject.findMany({
        where: { institutionId: student.institutionId, classItemId: student.classItemId ?? -1 },
        select: { subjectItemId: true },
      });
      const allowed = new Set(pool.map((p) => p.subjectItemId));
      const valid = wanted.filter((s) => allowed.has(s));
      await prisma.studentOptionalSubject.deleteMany({ where: { studentId } });
      if (valid.length) {
        await prisma.studentOptionalSubject.createMany({
          data: valid.map((subjectItemId) => ({ studentId, subjectItemId })),
        });
      }
      optionalSubjects = await prisma.studentOptionalSubject.findMany({
        where: { studentId },
        include: { subjectItem: { select: { id: true, name: true, nameBn: true } } },
      }).then((rows) => rows.map((r) => r.subjectItem));
    }

    const updated = await prisma.student.update({
      where: { id: studentId },
      data,
      select: { id: true, name: true },
    });

    return NextResponse.json({ student: updated, optionalSubjects });
  } catch {
    return NextResponse.json({ error: "Could not update student" }, { status: 500 });
  }
}
