"use server";

import ExcelJS from "exceljs";
import { hash } from "bcryptjs";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { z } from "zod";
import { requireTeacher } from "@/lib/auth/session";
import { prisma } from "@/lib/db/prisma";

const MAX_FILE_SIZE = 2 * 1024 * 1024;
const MAX_ROWS = 500;
const headers = ["nama", "nis", "nisn", "email", "kelas", "jenis kelamin", "status", "kata sandi"];
const rowSchema = z.object({
  name: z.string().min(2).max(120),
  nis: z.string().min(1).max(40),
  nisn: z.string().max(40),
  email: z.union([z.literal(""), z.string().email().max(191)]),
  className: z.string().min(1).max(80),
  gender: z.union([z.literal(""), z.enum(["Laki-laki", "Perempuan"])]),
  status: z.enum(["ACTIVE", "GRADUATED", "INACTIVE"]),
  password: z.union([z.literal(""), z.string().min(12).max(72)]),
});

const cellText = (cell: ExcelJS.Cell) => cell.text.trim();
const resultUrl = (imported: number, failed: number, errors: string[]) => {
  const params = new URLSearchParams({ imported: String(imported), failed: String(failed) });
  if (errors.length) params.set("errors", errors.slice(0, 3).join(" | ").slice(0, 500));
  return `/dashboard/siswa?${params}`;
};

export async function importStudents(formData: FormData) {
  await requireTeacher();
  const file = formData.get("file");
  if (!(file instanceof File) || !file.name.toLowerCase().endsWith(".xlsx") || file.size === 0 || file.size > MAX_FILE_SIZE) {
    redirect(resultUrl(0, 1, ["Gunakan file .xlsx dengan ukuran maksimal 2 MB."]));
  }

  const bytes = new Uint8Array(await file.arrayBuffer());
  if (bytes[0] !== 0x50 || bytes[1] !== 0x4b) redirect(resultUrl(0, 1, ["Isi file bukan workbook Excel .xlsx yang valid."]));

  const workbook = new ExcelJS.Workbook();
  try {
    await workbook.xlsx.load(bytes as unknown as ExcelJS.Buffer);
  } catch {
    redirect(resultUrl(0, 1, ["File Excel rusak atau tidak dapat dibaca."]));
  }
  const sheet = workbook.getWorksheet("Data Siswa") ?? workbook.worksheets[0];
  if (!sheet) redirect(resultUrl(0, 1, ["Worksheet Data Siswa tidak ditemukan."]));

  const actualHeaders = sheet.getRow(1).values;
  const normalizedHeaders = Array.from({ length: headers.length }, (_, index) => String(Array.isArray(actualHeaders) ? actualHeaders[index + 1] ?? "" : "").trim().toLowerCase());
  if (headers.some((header, index) => normalizedHeaders[index] !== header)) redirect(resultUrl(0, 1, ["Nama atau urutan kolom berubah. Unduh kembali format Excel."]));

  const classes = await prisma.class.findMany({ where: { isActive: true }, select: { id: true, name: true } });
  const classMap = new Map(classes.map((item) => [item.name.toLocaleLowerCase("id-ID"), item]));
  const parsedRows: Array<z.infer<typeof rowSchema> & { rowNumber: number; classId: string }> = [];
  const errors: string[] = [];
  const seenNis = new Set<string>();
  const seenNisn = new Set<string>();
  const seenEmail = new Set<string>();
  const lastRow = Math.min(sheet.actualRowCount, MAX_ROWS + 1);

  if (sheet.actualRowCount - 1 > MAX_ROWS) errors.push(`File melebihi batas ${MAX_ROWS} baris; sisanya tidak diproses.`);
  for (let rowNumber = 2; rowNumber <= lastRow; rowNumber += 1) {
    const row = sheet.getRow(rowNumber);
    if (row.values.length === 0 || row.eachCell === undefined) continue;
    const raw = {
      name: cellText(row.getCell(1)), nis: cellText(row.getCell(2)), nisn: cellText(row.getCell(3)),
      email: cellText(row.getCell(4)).toLowerCase(), className: cellText(row.getCell(5)), gender: cellText(row.getCell(6)),
      status: (cellText(row.getCell(7)).toUpperCase() || "ACTIVE"), password: cellText(row.getCell(8)),
    };
    if (Object.values(raw).every((value) => !value)) continue;
    const parsed = rowSchema.safeParse(raw);
    const targetClass = classMap.get(raw.className.toLocaleLowerCase("id-ID"));
    if (!parsed.success) { errors.push(`Baris ${rowNumber}: data tidak valid.`); continue; }
    if (!targetClass) { errors.push(`Baris ${rowNumber}: kelas “${raw.className}” tidak ditemukan.`); continue; }
    if (raw.password && !raw.email) { errors.push(`Baris ${rowNumber}: email wajib untuk membuat akun.`); continue; }
    if (seenNis.has(raw.nis) || (raw.nisn && seenNisn.has(raw.nisn)) || (raw.email && seenEmail.has(raw.email))) { errors.push(`Baris ${rowNumber}: NIS, NISN, atau email ganda dalam file.`); continue; }
    seenNis.add(raw.nis); if (raw.nisn) seenNisn.add(raw.nisn); if (raw.email) seenEmail.add(raw.email);
    parsedRows.push({ ...parsed.data, rowNumber, classId: targetClass.id });
  }

  const [existingStudents, existingUsers] = await Promise.all([
    prisma.student.findMany({ where: { OR: [
      ...(seenNis.size ? [{ nis: { in: [...seenNis] } }] : []),
      ...(seenNisn.size ? [{ nisn: { in: [...seenNisn] } }] : []),
      ...(seenEmail.size ? [{ email: { in: [...seenEmail] } }] : []),
    ] }, select: { nis: true, nisn: true, email: true } }),
    seenEmail.size ? prisma.user.findMany({ where: { email: { in: [...seenEmail] } }, select: { email: true } }) : Promise.resolve([]),
  ]);
  const unavailableNis = new Set(existingStudents.map((item) => item.nis));
  const unavailableNisn = new Set(existingStudents.flatMap((item) => item.nisn ? [item.nisn] : []));
  const unavailableEmail = new Set([...existingStudents.flatMap((item) => item.email ? [item.email.toLowerCase()] : []), ...existingUsers.map((item) => item.email.toLowerCase())]);

  let imported = 0;
  for (const row of parsedRows) {
    if (unavailableNis.has(row.nis) || (row.nisn && unavailableNisn.has(row.nisn)) || (row.email && unavailableEmail.has(row.email))) {
      errors.push(`Baris ${row.rowNumber}: NIS, NISN, atau email sudah digunakan.`); continue;
    }
    try {
      if (row.password) {
        const passwordHash = await hash(row.password, 12);
        await prisma.$transaction(async (tx) => {
          const user = await tx.user.create({ data: { name: row.name, email: row.email, passwordHash, role: "STUDENT", status: "ACTIVE" } });
          await tx.student.create({ data: { userId: user.id, classId: row.classId, nis: row.nis, nisn: row.nisn || null, name: row.name, email: row.email, gender: row.gender || null, status: row.status } });
        });
      } else {
        await prisma.student.create({ data: { classId: row.classId, nis: row.nis, nisn: row.nisn || null, name: row.name, email: row.email || null, gender: row.gender || null, status: row.status } });
      }
      imported += 1;
    } catch {
      errors.push(`Baris ${row.rowNumber}: gagal disimpan ke database.`);
    }
  }

  revalidatePath("/dashboard/siswa");
  redirect(resultUrl(imported, errors.length, errors));
}
