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

interface GroupRow {
  name: string;
  nameBn: string | null;
  total: number;
  present: number;
  absent: number;
  leave: number;
  unmarked: number;
}

interface SubjectRow {
  name: string;
  nameBn: string | null;
  records: number;
  present: number;
  absent: number;
  leave: number;
}

interface StaffRow {
  id: number;
  employeeCode: string;
  name: string;
  nameBn: string | null;
  designation: string;
  staffAccount?: { role: string } | null;
}

/** people who teach — by designation keyword or a TEACHER login link */
const TEACHER_RE = /TEACHER|LECTURER|PROFESSOR|শিক্ষক/i;
const isTeacher = (s: StaffRow) => TEACHER_RE.test(s.designation ?? "") || s.staffAccount?.role === "TEACHER";

/** share of present among decided members (P+A+L) */
const rateOf = (present: number, absent: number, leave: number) =>
  present + absent + leave > 0 ? Math.round((present / (present + absent + leave)) * 100) : 0;

/** Attendance aggregate for the institution dashboard — ?date=YYYY-MM-DD
    (default today); the 7-day trend always ends on the selected date. */
export async function GET(req: Request) {
  const session = await requireRole("INSTITUTION_ADMIN");
  if (!session || !session.institutionId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const dateParam = new URL(req.url).searchParams.get("date") ?? "";
  const today = /^\d{4}-\d{2}-\d{2}$/.test(dateParam)
    ? new Date(dateParam)
    : new Date(new Date().toISOString().slice(0, 10));
  const since = new Date(today);
  since.setDate(since.getDate() - 6);

  const [students, studentAttendance, staff, staffAttendance, leaveRequests, subjectRows, todayActivities, trendRows] =
    await Promise.all([
      prisma.student.findMany({
        where: { institutionId: session.institutionId, status: "ACTIVE" },
        select: {
          id: true,
          roll: true,
          name: true,
          nameBn: true,
          classItem: { select: { name: true, nameBn: true } },
          sectionItem: { select: { name: true, nameBn: true } },
          shiftItem: { select: { name: true, nameBn: true } },
        },
      }),
      prisma.studentAttendance.findMany({
        where: { institutionId: session.institutionId, date: today, subjectItemId: null },
        select: { studentId: true, status: true },
      }),
      prisma.staff.findMany({
        where: { institutionId: session.institutionId, status: "ACTIVE" },
        select: {
          id: true,
          employeeCode: true,
          name: true,
          nameBn: true,
          designation: true,
          staffAccount: { select: { role: true } },
        },
      }),
      prisma.staffAttendance.findMany({
        where: { institutionId: session.institutionId, date: today },
        select: { staffId: true, status: true },
      }),
      prisma.leaveRequest.findMany({
        where: {
          institutionId: session.institutionId,
          status: "APPROVED",
          fromDate: { lte: today },
          toDate: { gte: today },
        },
        include: {
          staff: { select: { id: true, employeeCode: true, name: true, nameBn: true, designation: true } },
        },
      }),
      prisma.studentAttendance.findMany({
        where: { institutionId: session.institutionId, date: today, subjectItemId: { not: null } },
        include: { subjectItem: { select: { name: true, nameBn: true } } },
      }),
      prisma.studentActivity.findMany({
        where: { institutionId: session.institutionId, activityDate: today },
        include: { _count: { select: { recipients: true } } },
        orderBy: { createdAt: "desc" },
      }),
      prisma.studentAttendance.findMany({
        where: { institutionId: session.institutionId, date: { gte: since }, subjectItemId: null },
        select: { date: true, status: true },
      }),
    ]);

  /* ---------- Students (general daily records) ---------- */
  const studentStatus = new Map(studentAttendance.map((a) => [a.studentId, a.status]));
  const classMap = new Map<string, GroupRow>();
  const sectionMap = new Map<string, GroupRow>();
  const shiftMap = new Map<string, GroupRow>();
  const group = (
    map: Map<string, GroupRow>,
    key: string,
    name: string,
    nameBn: string | null,
    st: string | undefined,
  ) => {
    const row = map.get(key) ?? { name, nameBn, total: 0, present: 0, absent: 0, leave: 0, unmarked: 0 };
    row.total += 1;
    if (st === "PRESENT") row.present += 1;
    else if (st === "ABSENT") row.absent += 1;
    else if (st === "LEAVE") row.leave += 1;
    else row.unmarked += 1;
    map.set(key, row);
  };

  const absentStudents: { roll: string; name: string; nameBn: string | null; className: string | null }[] = [];
  const leaveStudents: { roll: string; name: string; nameBn: string | null; className: string | null }[] = [];
  for (const s of students) {
    const st = studentStatus.get(s.id);
    if (s.classItem) group(classMap, `c${s.classItem.name}`, s.classItem.name, s.classItem.nameBn, st);
    else group(classMap, "c-", "—", null, st);
    if (s.sectionItem) group(sectionMap, `s${s.sectionItem.name}`, s.sectionItem.name, s.sectionItem.nameBn, st);
    else group(sectionMap, "s-", "—", null, st);
    if (s.shiftItem) group(shiftMap, `h${s.shiftItem.name}`, s.shiftItem.name, s.shiftItem.nameBn, st);
    else group(shiftMap, "h-", "—", null, st);
    if (st === "ABSENT") absentStudents.push({ roll: s.roll, name: s.name, nameBn: s.nameBn, className: s.classItem?.name ?? null });
    if (st === "LEAVE") leaveStudents.push({ roll: s.roll, name: s.name, nameBn: s.nameBn, className: s.classItem?.name ?? null });
  }

  const studentCounts = {
    total: students.length,
    marked: studentAttendance.length,
    present: studentAttendance.filter((a) => a.status === "PRESENT").length,
    absent: studentAttendance.filter((a) => a.status === "ABSENT").length,
    leave: studentAttendance.filter((a) => a.status === "LEAVE").length,
    unmarked: Math.max(0, students.length - studentAttendance.length),
  };
  const studentRate = rateOf(studentCounts.present, studentCounts.absent, studentCounts.leave);

  /* ---------- Subject-wise rows (today's subject records) ---------- */
  const subjectMap = new Map<string, SubjectRow>();
  for (const r of subjectRows) {
    const key = r.subjectItem?.name ?? "—";
    const row =
      subjectMap.get(key) ??
      ({ name: key, nameBn: r.subjectItem?.nameBn ?? null, records: 0, present: 0, absent: 0, leave: 0 } as SubjectRow);
    row.records += 1;
    if (r.status === "PRESENT") row.present += 1;
    else if (r.status === "ABSENT") row.absent += 1;
    else if (r.status === "LEAVE") row.leave += 1;
    subjectMap.set(key, row);
  }

  /* ---------- Teachers vs Staff (employees split by role signal) ---------- */
  const staffById = new Map(staff.map((s) => [s.id, s]));
  const teachers = staff.filter(isTeacher);
  const others = staff.filter((s) => !isTeacher(s));

  /** clean partition for one employee group: attendance rows + approved
      leave requests covering today for unmarked members */
  const splitGroup = (members: StaffRow[]) => {
    const ids = new Set(members.map((m) => m.id));
    const att = staffAttendance.filter((a) => ids.has(a.staffId));
    const present = att.filter((a) => a.status === "PRESENT").length;
    const absent = att.filter((a) => a.status === "ABSENT").length;
    let leave = att.filter((a) => a.status === "LEAVE").length;
    const markedIds = new Set(att.map((a) => a.staffId));
    for (const lr of leaveRequests) {
      if (lr.staff && ids.has(lr.staff.id) && !markedIds.has(lr.staff.id)) leave += 1;
    }
    const total = members.length;
    const decided = present + absent + leave;
    return {
      total,
      marked: att.length,
      present,
      absent,
      leave,
      unmarked: Math.max(0, total - decided),
      rate: rateOf(present, absent, leave),
    };
  };
  const teacherCounts = splitGroup(teachers);
  const staffCounts = splitGroup(others);

  const absentStaff = staffAttendance
    .filter((a) => a.status === "ABSENT")
    .map((a) => staffById.get(a.staffId))
    .filter((s): s is NonNullable<typeof s> => !!s)
    .map((s) => ({ employeeCode: s.employeeCode, name: s.name, nameBn: s.nameBn, designation: s.designation }));

  // On leave today = marked LEAVE in attendance ∪ approved leave requests spanning today (deduped)
  const onLeaveMap = new Map<number, { employeeCode: string; name: string; nameBn: string | null; designation: string }>();
  for (const a of staffAttendance) {
    if (a.status !== "LEAVE") continue;
    const s = staffById.get(a.staffId);
    if (s) onLeaveMap.set(s.id, { employeeCode: s.employeeCode, name: s.name, nameBn: s.nameBn, designation: s.designation });
  }
  for (const lr of leaveRequests) {
    if (!lr.staff) continue;
    onLeaveMap.set(lr.staff.id, {
      employeeCode: lr.staff.employeeCode,
      name: lr.staff.name,
      nameBn: lr.staff.nameBn,
      designation: lr.staff.designation,
    });
  }

  /* ---------- 7-day student attendance trend ---------- */
  const byDay = new Map<string, { present: number; absent: number; leave: number }>();
  for (const r of trendRows) {
    const key = r.date.toISOString().slice(0, 10);
    const day = byDay.get(key) ?? { present: 0, absent: 0, leave: 0 };
    if (r.status === "PRESENT") day.present += 1;
    else if (r.status === "ABSENT") day.absent += 1;
    else if (r.status === "LEAVE") day.leave += 1;
    byDay.set(key, day);
  }
  const trend: { date: string; rate: number }[] = [];
  for (let i = 6; i >= 0; i--) {
    const d = new Date(since);
    d.setDate(since.getDate() + (6 - i));
    const key = d.toISOString().slice(0, 10);
    const day = byDay.get(key) ?? { present: 0, absent: 0, leave: 0 };
    trend.push({ date: key, rate: rateOf(day.present, day.absent, day.leave) });
  }

  return NextResponse.json({
    date: today.toISOString().slice(0, 10),
    students: {
      ...studentCounts,
      rate: studentRate,
      byClass: Array.from(classMap.values()),
      bySection: Array.from(sectionMap.values()),
      byShift: Array.from(shiftMap.values()),
      bySubject: Array.from(subjectMap.values()).sort((a, b) => b.records - a.records),
      absentStudents: absentStudents.slice(0, 10),
      leaveStudents: leaveStudents.slice(0, 10),
    },
    teachers: teacherCounts,
    staff: {
      ...staffCounts,
      absentStaff: absentStaff.slice(0, 10),
      onLeaveStaff: Array.from(onLeaveMap.values()).slice(0, 10),
    },
    trend,
    activities: todayActivities.map((a) => ({
      id: a.id,
      title: a.title,
      description: a.description,
      isForAll: a.isForAll,
      recipientCount: a.isForAll ? null : a._count.recipients,
    })),
  });
}
