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

/** Bulk-create STUDENT or PARENT credentials for every active student in a
    class (or the whole institution when no classItemId is given). Usernames
    default to the roll (parents: roll + "-P"); passwords are generated.
    Students who already have an account of the requested type are skipped.
    Returns the generated credentials once, so the admin can hand them out
    (list CSV / print) — they are not recoverable afterwards. */
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 role = String(body.role ?? "");
    if (role !== "STUDENT" && role !== "PARENT") {
      return NextResponse.json({ error: "Invalid role" }, { status: 400 });
    }
    const classItemId = body.classItemId ? Number(body.classItemId) : null;

    const students = await prisma.student.findMany({
      where: {
        institutionId: session.institutionId,
        status: "ACTIVE",
        ...(classItemId ? { classItemId } : {}),
      },
      select: { id: true, name: true, roll: true, guardianName: true },
      orderBy: { roll: "asc" },
    });
    if (students.length === 0) {
      return NextResponse.json({ error: "NO_STUDENTS_IN_SCOPE" }, { status: 400 });
    }

    // skip students that already have an account of this type
    const linked = await prisma.user.findMany({
      where: {
        institutionId: session.institutionId,
        ...(role === "STUDENT"
          ? { studentProfileId: { in: students.map((s) => s.id) } }
          : { parentOfStudentId: { in: students.map((s) => s.id) } }),
      },
      select: { studentProfileId: true, parentOfStudentId: true },
    });
    const takenStudentIds = new Set(
      linked.map((u) => (role === "STUDENT" ? u.studentProfileId : u.parentOfStudentId))
    );
    const pending = students.filter((s) => !takenStudentIds.has(s.id));
    if (pending.length === 0) {
      return NextResponse.json({ created: 0, skipped: students.length, credentials: [] });
    }

    // usernames must be unique per institution — roll-based defaults that
    // collide with an existing username get a numeric suffix
    const existingUsernames = new Set(
      (
        await prisma.user.findMany({
          where: { institutionId: session.institutionId, username: { not: null } },
          select: { username: true },
        })
      ).map((u) => u.username as string)
    );

    const generated: {
      username: string;
      password: string;
      name: string;
      roll: string;
      studentName: string;
    }[] = [];

    for (const s of pending) {
      const base = `${s.roll}${role === "PARENT" ? "-P" : ""}`;
      let username = base;
      let n = 1;
      while (existingUsernames.has(username)) {
        username = `${base}-${++n}`;
      }
      existingUsernames.add(username);

      const password = randomPassword();
      await prisma.user.create({
        data: {
          role,
          username,
          institutionId: session.institutionId,
          name:
            role === "PARENT"
              ? s.guardianName || `${s.name}'s Guardian`
              : s.name,
          passwordHash: await bcrypt.hash(password, 10),
          ...(role === "STUDENT" ? { studentProfileId: s.id } : { parentOfStudentId: s.id }),
        },
      });
      generated.push({
        username,
        password,
        name: role === "PARENT" ? s.guardianName || `${s.name}'s Guardian` : s.name,
        roll: s.roll,
        studentName: s.name,
      });
    }

    return NextResponse.json({
      created: generated.length,
      skipped: students.length - generated.length,
      credentials: generated,
    });
  } catch {
    return NextResponse.json({ error: "Could not create credentials" }, { status: 500 });
  }
}

/** 8-char alphanumeric (mixed case + digit guaranteed) — meets the 6-char
    minimum rule and stays readable for hand-out sheets. */
function randomPassword(): string {
  const chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
  const pick = () => chars[Math.floor(Math.random() * chars.length)];
  let pwd = [pick(), pick(), pick(), pick(), pick(), pick(), pick(), pick()].join("");
  if (!/[A-Z]/.test(pwd)) pwd = `A${pwd.slice(1)}`;
  if (!/[a-z]/.test(pwd)) pwd = `${pwd.slice(0, -1)}b`;
  if (!/\d/.test(pwd)) pwd = `${pwd.slice(0, -1)}5`;
  return pwd;
}
