import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireRole } from "@/lib/auth";
import {
  BULK_ADMISSION_COLUMNS,
  isValidBulkAdmissionHeaders,
  normalizeBulkHeader,
} from "@/lib/bulk-admission-template";

/* Bulk admission application insert — the CSV/Excel upload from the portal
   Admissions page lands here. The client sends the file's header row as
   `headers`; it must match the template arrangement exactly (same columns,
   same order — see bulk-admission-template.ts) or the whole upload is
   refused with HEADER_MISMATCH. Rows are then validated individually; valid
   ones are created as RAPID-style applications (fee snapshotted like any
   other), invalid ones are reported per row with a reason. */

const MAX_ROWS = 500;

interface BulkRow {
  studentName?: string;
  guardianPhone?: string;
  guardianName?: string;
  studentNameBn?: string;
  gender?: string;
  dob?: string;
  className?: string;
  email?: string;
  studentPhone?: string;
  address?: string;
  note?: string;
}

const str = (v: unknown) => String(v ?? "").trim();

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 rows: BulkRow[] = Array.isArray(body.rows) ? body.rows : [];
    if (!rows.length) return NextResponse.json({ error: "NO_ROWS" }, { status: 400 });
    if (rows.length > MAX_ROWS) {
      return NextResponse.json({ error: "TOO_MANY_ROWS", max: MAX_ROWS }, { status: 400 });
    }

    /* header arrangement guard — refuse the whole upload when the file's
       columns don't match the template (also rejects unknown row keys, so a
       tampered payload can't smuggle extra fields past the client check) */
    const allowedKeys = new Set<string>(
      BULK_ADMISSION_COLUMNS.map((c) => normalizeBulkHeader(c))
    );
    const headersOk =
      isValidBulkAdmissionHeaders(Array.isArray(body.headers) ? body.headers.map(String) : []) &&
      rows.every((r) => r && Object.keys(r).every((k) => allowedKeys.has(normalizeBulkHeader(k))));
    if (!headersOk) {
      return NextResponse.json(
        { error: "HEADER_MISMATCH", expected: BULK_ADMISSION_COLUMNS },
        { status: 400 }
      );
    }

    // institution context: fee snapshot + class catalog for className matching
    const [me, classes] = await Promise.all([
      prisma.institution.findUnique({
        where: { id: session.institutionId },
        select: { admissionFeeEnabled: true, admissionFeeAmount: true },
      }),
      prisma.academicSelection.findMany({
        where: { institutionId: session.institutionId, academicItem: { category: "CLASS" } },
        select: { academicItem: { select: { id: true, name: true, nameBn: true } } },
      }),
    ]);
    const classByName = new Map<string, number>();
    for (const c of classes) {
      classByName.set(c.academicItem.name.toLowerCase(), c.academicItem.id);
      classByName.set((c.academicItem.nameBn ?? "").toLowerCase(), c.academicItem.id);
    }
    const feeAmount =
      me?.admissionFeeEnabled && Number(me.admissionFeeAmount) > 0 ? Number(me.admissionFeeAmount) : 0;

    const results: { row: number; studentName: string; ok: boolean; reference?: string; error?: string }[] = [];
    let created = 0;

    for (let i = 0; i < rows.length; i++) {
      const r = rows[i];
      const studentName = str(r.studentName);
      const guardianPhone = str(r.guardianPhone);
      const rowNo = i + 2; // +2 = header row + 1-based

      if (!studentName || !guardianPhone) {
        results.push({ row: rowNo, studentName: studentName || "—", ok: false, error: "MISSING_FIELDS" });
        continue;
      }
      const className = str(r.className).toLowerCase();
      const classItemId = className ? classByName.get(className) ?? null : null;
      if (className && !classItemId) {
        results.push({ row: rowNo, studentName, ok: false, error: "CLASS_NOT_FOUND" });
        continue;
      }
      const guardianName = str(r.guardianName) || studentName;
      const note = str(r.note) || null;

      try {
        const admission = await prisma.admission.create({
          data: {
            institutionId: session.institutionId!,
            studentName,
            applicationType: "DETAILS",
            studentNameBn: str(r.studentNameBn) || null,
            gender: ["MALE", "FEMALE", "OTHER"].includes(str(r.gender).toUpperCase())
              ? str(r.gender).toUpperCase()
              : "MALE",
            dob: r.dob ? new Date(str(r.dob)) : null,
            guardianName,
            guardianPhone,
            email: str(r.email) || null,
            address: str(r.address) || null,
            studentPhone: str(r.studentPhone) || null,
            classItemId,
            note,
            feeAmount,
            feeStatus: feeAmount > 0 ? "DUE" : "NONE",
          },
        });
        results.push({
          row: rowNo,
          studentName,
          ok: true,
          reference: `APP-${String(admission.id).padStart(5, "0")}`,
        });
        created++;
      } catch {
        results.push({ row: rowNo, studentName, ok: false, error: "DB_ERROR" });
      }
    }

    return NextResponse.json({ created, failed: rows.length - created, results });
  } catch {
    return NextResponse.json({ error: "Could not process bulk upload" }, { status: 500 });
  }
}
