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

/**
 * Student attendance sheet for a date.
 * ?date=YYYY-MM-DD (default today) + optional classItemId / sectionItemId / shiftItemId filters
 * for the student list, and subjectItemId — when set, the sheet shows that subject's
 * attendance instead of the general (daily) one, and saves subject-wise records.
 */
export async function GET(req: Request) {
  const session = await requireRole("INSTITUTION_ADMIN");
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const url = new URL(req.url);
  const dateParam = url.searchParams.get("date");
  const idParam = (key: string) => {
    const v = url.searchParams.get(key);
    return v && /^\d+$/.test(v) ? Number(v) : null;
  };
  const classItemId = idParam("classItemId");
  const sectionItemId = idParam("sectionItemId");
  const shiftItemId = idParam("shiftItemId");
  const subjectItemId = idParam("subjectItemId");
  const date = new Date(
    dateParam && /^\d{4}-\d{2}-\d{2}$/.test(dateParam) ? dateParam : new Date().toISOString().slice(0, 10),
  );

  const [students, attendance] = await Promise.all([
    prisma.student.findMany({
      where: {
        institutionId: session.institutionId,
        status: "ACTIVE",
        ...(classItemId ? { classItemId } : {}),
        ...(sectionItemId ? { sectionItemId } : {}),
        ...(shiftItemId ? { shiftItemId } : {}),
      },
      orderBy: [{ roll: "asc" }],
      select: {
        id: true,
        roll: true,
        name: true,
        nameBn: true,
        photoUrl: true,
        classItemId: true,
        classItem: { select: { name: true, nameBn: true } },
      },
    }),
    prisma.studentAttendance.findMany({
      where: {
        institutionId: session.institutionId,
        date,
        subjectItemId: subjectItemId ?? null,
      },
      select: { studentId: true, status: true, note: true, inTime: true, outTime: true, source: true },
    }),
  ]);

  const byStudent = new Map(attendance.map((a) => [a.studentId, a]));
  const scoped = students.filter((s) => byStudent.has(s.id));
  const statusOf = (id: number) => byStudent.get(id)?.status;
  return NextResponse.json({
    date: date.toISOString().slice(0, 10),
    subjectItemId,
    entries: students.map((s) => ({
      studentId: s.id,
      roll: s.roll,
      name: s.name,
      nameBn: s.nameBn,
      photoUrl: s.photoUrl ?? null,
      classItemId: s.classItemId,
      className: s.classItem?.name ?? null,
      classNameBn: s.classItem?.nameBn ?? null,
      status: byStudent.get(s.id)?.status ?? null,
      note: byStudent.get(s.id)?.note ?? null,
      inTime: byStudent.get(s.id)?.inTime?.toISOString() ?? null,
      outTime: byStudent.get(s.id)?.outTime?.toISOString() ?? null,
      source: byStudent.get(s.id)?.source ?? null,
    })),
    counts: {
      marked: scoped.length,
      present: scoped.filter((s) => statusOf(s.id) === "PRESENT").length,
      absent: scoped.filter((s) => statusOf(s.id) === "ABSENT").length,
      leave: scoped.filter((s) => statusOf(s.id) === "LEAVE").length,
    },
  });
}

/**
 * Save attendance for a date: { date, subjectItemId?, entries: [{ studentId, status }] }.
 * With subjectItemId the rows are subject-wise; without it they are the general daily record.
 */
export async function POST(req: Request) {
  const session = await requireRole("INSTITUTION_ADMIN");
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  try {
    const body = await req.json();
    const dateStr = String(body.date ?? "").slice(0, 10);
    if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
      return NextResponse.json({ error: "Invalid date" }, { status: 400 });
    }
    const date = new Date(dateStr);
    const subjectItemId =
      body.subjectItemId !== undefined && body.subjectItemId !== null && body.subjectItemId !== ""
        ? Number(body.subjectItemId)
        : null;
    const entries: Array<{ studentId: number; status: string }> = Array.isArray(body.entries) ? body.entries : [];
    if (entries.length === 0) {
      return NextResponse.json({ error: "No entries" }, { status: 400 });
    }

    const valid = new Set(["PRESENT", "ABSENT", "LEAVE"]);
    const studentIds = new Set(
      (
        await prisma.student.findMany({
          where: { institutionId: session.institutionId },
          select: { id: true },
        })
      ).map((s) => s.id)
    );
    if (subjectItemId !== null) {
      const subject = await prisma.academicItem.findFirst({
        where: { id: subjectItemId, category: "SUBJECT" },
        select: { id: true },
      });
      if (!subject) return NextResponse.json({ error: "Subject not found" }, { status: 404 });
    }

    let saved = 0;
    await prisma.$transaction(async (tx) => {
      for (const e of entries) {
        if (!studentIds.has(Number(e.studentId)) || !valid.has(e.status)) continue;
        const existing = await tx.studentAttendance.findFirst({
          where: { studentId: Number(e.studentId), date, subjectItemId },
          select: { id: true },
        });
        if (existing) {
          await tx.studentAttendance.update({
            where: { id: existing.id },
            data: { status: e.status as "PRESENT" | "ABSENT" | "LEAVE" },
          });
        } else {
          await tx.studentAttendance.create({
            data: {
              institutionId: session.institutionId!,
              studentId: Number(e.studentId),
              date,
              status: e.status as "PRESENT" | "ABSENT" | "LEAVE",
              subjectItemId,
            },
          });
        }
        saved += 1;
      }
    });

    return NextResponse.json({ success: true, saved });
  } catch {
    return NextResponse.json({ error: "Could not save attendance" }, { status: 500 });
  }
}
