"use client";

import { useEffect, useState } from "react";
import { useLang } from "@/context/LanguageContext";
import {
  Alert,
  Button,
  DatePicker,
  EmptyState,
  Input,
  Modal,
  PageHeader,
  Pagination,
  Select,
  StatusBadge,
  Textarea,
  TimePicker,
  usePagination,
} from "@/components/ui";
import { api, useApi } from "@/lib/client";
import { formatDate, formatTime } from "@/lib/utils";
import { REPORT_PRESETS, mix } from "@/lib/report-html";
import { buildExamScheduleReportHtml, type ScheduleReportLabels, type ScheduleReportExam, type ScheduleReportPaper } from "@/lib/exam-schedule-report";
import { IconCheck, IconEdit, IconFile, IconPlus, IconTrash } from "@/components/icons";
import { emsConfirm } from "@/components/ConfirmDialog";

interface Exam {
  id: number;
  name: string;
  nameBn: string | null;
  branchId: number | null;
  branchName: string | null;
  classItemId: number | null;
  className: string | null;
  classNameBn: string | null;
  sectionItemId: number | null;
  sectionName: string | null;
  sectionNameBn: string | null;
  groupItemId: number | null;
  groupName: string | null;
  groupNameBn: string | null;
  departmentItemId: number | null;
  departmentName: string | null;
  departmentNameBn: string | null;
  shiftItemId: number | null;
  shiftName: string | null;
  shiftNameBn: string | null;
  startDate: string;
  endDate: string | null;
  status: string;
  format?: string;
  schedulePublished: boolean;
  schedulePublishedAt: string | null;
  resultPublished: boolean;
  resultPublishedAt: string | null;
  note: string | null;
  paperCount: number;
}

interface Paper {
  id: number;
  examId: number;
  subjectItemId: number | null;
  subjectName: string | null;
  subjectNameBn: string | null;
  date: string;
  startTime: string;
  endTime: string;
  roomItemId: number | null;
  roomName: string | null;
  roomNameBn: string | null;
  fullMarks: number;
  totalPassMarks: number | null;
  cqMarks: number | null;
  cqPassMarks: number | null;
  mcqMarks: number | null;
  mcqPassMarks: number | null;
  appliedMarks: number | null;
  appliedPassMarks: number | null;
  appliedDate: string | null;
  note: string | null;
}

interface AcademicItem {
  id: number;
  category: string;
  name: string;
  nameBn: string;
}

interface Branch {
  id: number;
  name: string;
  type: string;
  status: string;
}

interface StudentMini {
  id: number;
  roll: string;
  name: string;
  nameBn: string | null;
}

interface MarkRow {
  id: number;
  examScheduleId: number;
  studentId: number;
  cq: number | null;
  mcq: number | null;
  applied: number | null;
  total: number | null;
  note: string | null;
}

interface MarkCell {
  cq: string;
  mcq: string;
  applied: string;
  total: string;
}

const EMPTY_CELL: MarkCell = { cq: "", mcq: "", applied: "", total: "" };

type Tab = "exams" | "schedule" | "marks";
type MarkMode = "subject" | "student";

const EXAM_STATUSES = ["UPCOMING", "RUNNING", "FINISHED"];

function todayISO(): string {
  return new Date().toISOString().slice(0, 10);
}

const emptyExam = {
  name: "", nameBn: "", branchId: "", classItemId: "", sectionItemId: "", groupItemId: "", departmentItemId: "",
  shiftItemId: "", startDate: "", endDate: "", status: "UPCOMING", format: "SINGLE", note: "",
};
const emptyPaper = {
  subjectItemId: "", date: "", appliedDate: "", startTime: "10:00", endTime: "13:00", roomItemId: "",
  fullMarks: "100", totalPassMarks: "", cqMarks: "", cqPassMarks: "", mcqMarks: "", mcqPassMarks: "",
  appliedMarks: "", appliedPassMarks: "", note: "",
};

export default function PortalExamsPage() {
  const { t, locale } = useLang();
  const [tab, setTab] = useState<Tab>("exams");
  const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
  const [busy, setBusy] = useState(false);

  const examsData = useApi<{ exams: Exam[] }>("/api/portal/exams");
  const academic = useApi<{ items: AcademicItem[] }>("/api/portal/academic");
  const branchesData = useApi<{ institution: { id: number; name: string } | null; branches: Branch[] }>("/api/portal/branches");
  const me = useApi<{ user: { institution?: { name: string; logo?: string | null } | null } }>("/api/auth/me");

  // schedule report theme (like the marksheet / certificates color picker)
  const [rptPrimary, setRptPrimary] = useState(REPORT_PRESETS[0].primary);
  const [rptAccent, setRptAccent] = useState(REPORT_PRESETS[0].accent);
  // optional verification QR on the report (like the marksheet QRs)
  const [showReportQr, setShowReportQr] = useState(true);
  // verification module switch from the credential panels (Control Panel →
  // Email/SMS card); when exam-schedule verification is off, no QR checkbox
  // and no codes are issued for schedule reports
  const notifyCfg = useApi<{ verification?: { examSchedule?: boolean } }>("/api/portal/settings/notify");
  const scheduleQrAllowed = notifyCfg.data?.verification?.examSchedule !== false;
  const branches = branchesData.data?.branches ?? [];
  const classItems = (academic.data?.items ?? []).filter((i) => i.category === "CLASS");
  const sectionItems = (academic.data?.items ?? []).filter((i) => i.category === "SECTION");
  const groupItems = (academic.data?.items ?? []).filter((i) => i.category === "GROUP");
  const departmentItems = (academic.data?.items ?? []).filter((i) => i.category === "DEPARTMENT");
  const shiftItems = (academic.data?.items ?? []).filter((i) => i.category === "SHIFT");
  const subjectItems = (academic.data?.items ?? []).filter((i) => i.category === "SUBJECT");
  const roomItems = (academic.data?.items ?? []).filter((i) => i.category === "ROOM");
  // section must be chosen when the institution manages sections
  const sectionRequired = sectionItems.length > 0;

  // exam tab filters
  const [filterClass, setFilterClass] = useState("");
  const [filterShift, setFilterShift] = useState("");
  const exams = (examsData.data?.exams ?? []).filter(
    (e) =>
      (!filterClass || String(e.classItemId) === filterClass) &&
      (!filterShift || String(e.shiftItemId) === filterShift),
  );
  const examsPager = usePagination(exams, 10);

  // exam modal
  const [examModal, setExamModal] = useState(false);
  const [editingExam, setEditingExam] = useState<Exam | null>(null);
  const [examForm, setExamForm] = useState(emptyExam);

  // schedule tab
  const [examId, setExamId] = useState("");
  const papers = useApi<{ schedules: Paper[] }>(examId ? `/api/portal/exams/${examId}/schedules` : null);
  const papersPager = usePagination(papers.data?.schedules ?? [], 10);

  // paper modal
  const [paperModal, setPaperModal] = useState(false);
  const [paperForm, setPaperForm] = useState(emptyPaper);
  const [editingPaper, setEditingPaper] = useState<Paper | null>(null);

  // marks input tab
  const [marksExamId, setMarksExamId] = useState("");
  const [markMode, setMarkMode] = useState<MarkMode>("subject");
  const [markPaperId, setMarkPaperId] = useState("");
  const [markStudentId, setMarkStudentId] = useState("");
  const [markCells, setMarkCells] = useState<Record<string, MarkCell>>({});
  const [marksBusy, setMarksBusy] = useState(false);
  // themed modal alerts (never window.alert/confirm) for the marks-input locks
  const [notice, setNotice] = useState<{ title: string; text: string } | null>(null);
  const [confirmModal, setConfirmModal] = useState<{ title: string; text: string; onYes: () => void | Promise<void> } | null>(null);

  const markStudents = useApi<{ students: StudentMini[] }>(marksExamId ? `/api/portal/exams/${marksExamId}/students` : null);
  const markPapers = useApi<{ schedules: Paper[] }>(marksExamId ? `/api/portal/exams/${marksExamId}/schedules` : null);
  const markRows = useApi<{ rows: MarkRow[] }>(marksExamId ? `/api/portal/exams/${marksExamId}/marks` : null);

  // load saved marks into the editable grid
  useEffect(() => {
    const cells: Record<string, MarkCell> = {};
    for (const r of markRows.data?.rows ?? []) {
      cells[`${r.examScheduleId}-${r.studentId}`] = {
        cq: r.cq != null ? String(r.cq) : "",
        mcq: r.mcq != null ? String(r.mcq) : "",
        applied: r.applied != null ? String(r.applied) : "",
        total: r.total != null ? String(r.total) : "",
      };
    }
    setMarkCells(cells);
  }, [markRows.data]);

  const papersList = markPapers.data?.schedules ?? [];
  const studentsList = markStudents.data?.students ?? [];
  const selectedMarkPaper = papersList.find((p) => String(p.id) === markPaperId) ?? null;
  const selectedMarkStudent = studentsList.find((s) => String(s.id) === markStudentId) ?? null;
  const marksExam = examsData.data?.exams.find((e) => String(e.id) === marksExamId) ?? null;
  // once the exam's result is published, marks are read-only
  const marksLocked = !!marksExam?.resultPublished;

  /** Ask (themed modal) → publish or unpublish the exam result. */
  function askResultPublish(publish: boolean) {
    setConfirmModal({
      title: publish ? t.portal.examsPage.resultPublishTitle : t.portal.examsPage.resultUnpublishTitle,
      text: publish ? t.portal.examsPage.resultPublishText : t.portal.examsPage.resultUnpublishText,
      onYes: async () => {
        try {
          await api(`/api/portal/exams/${marksExamId}`, {
            method: "PATCH",
            body: JSON.stringify({ resultPublished: publish }),
          });
          setMessage({
            type: "success",
            text: publish ? t.portal.examsPage.resultPublishedMsg : t.portal.examsPage.resultUnpublishedMsg,
          });
          examsData.reload();
        } catch {
          setMessage({ type: "error", text: t.common.tryAgain });
        }
      },
    });
  }

  const hasParts = (p: Paper) => p.cqMarks != null || p.mcqMarks != null || p.appliedMarks != null;

  /** Part maximum hints for the column headers (subject mode only — in student
      mode each row/paper has its own split). */
  function partHints(): { cq: number | null; mcq: number | null; applied: number | null } {
    if (markMode === "subject" && selectedMarkPaper) {
      return { cq: selectedMarkPaper.cqMarks, mcq: selectedMarkPaper.mcqMarks, applied: selectedMarkPaper.appliedMarks };
    }
    return { cq: null, mcq: null, applied: null };
  }

  /** In subject mode, a paper without a CQ/MCQ/Applied split hides the part
      columns — the Total column becomes the direct marks input. */
  function hidePartColumns(): boolean {
    return markMode === "subject" && !!selectedMarkPaper && !hasParts(selectedMarkPaper);
  }

  /** How many rows of the current slice already have saved marks. */
  function enteredCount() {
    const rows = markRows.data?.rows ?? [];
    if (markMode === "subject" && selectedMarkPaper) {
      return rows.filter((r) => r.examScheduleId === selectedMarkPaper.id).length;
    }
    if (markMode === "student" && selectedMarkStudent) {
      return rows.filter((r) => r.studentId === selectedMarkStudent.id).length;
    }
    return 0;
  }

  function cellOf(scheduleId: number, studentId: number): MarkCell {
    return markCells[`${scheduleId}-${studentId}`] ?? EMPTY_CELL;
  }

  /** A part changed: clamp to the paper's part maximum and auto-total when the
      paper has a CQ/MCQ/Applied split. */
  function onMarkPart(paper: Paper, studentId: number, field: "cq" | "mcq" | "applied", raw: string) {
    const max = field === "cq" ? paper.cqMarks : field === "mcq" ? paper.mcqMarks : paper.appliedMarks;
    let value = raw;
    if (value !== "" && Number(value) < 0) value = "";
    if (value !== "" && max != null && Number(value) > max) value = String(max);
    const key = `${paper.id}-${studentId}`;
    const next: MarkCell = { ...cellOf(paper.id, studentId), [field]: value };
    if (hasParts(paper)) {
      const sum = ["cq", "mcq", "applied"].reduce(
        (acc, f) => acc + (next[f as keyof MarkCell] === "" ? 0 : Number(next[f as keyof MarkCell])),
        0,
      );
      next.total = String(sum);
    }
    setMarkCells((cur) => ({ ...cur, [key]: next }));
  }

  /** Total changed directly (only for papers without a parts split). */
  function onMarkTotal(paper: Paper, studentId: number, raw: string) {
    let value = raw;
    if (value !== "" && Number(value) < 0) value = "";
    if (value !== "" && Number(value) > paper.fullMarks) value = String(paper.fullMarks);
    setMarkCells((cur) => ({ ...cur, [`${paper.id}-${studentId}`]: { ...cellOf(paper.id, studentId), total: value } }));
  }

  /** The marks <td>s for one paper × student pair — shared by both entry modes.
      For papers WITHOUT a CQ/MCQ/Applied split, the Total column becomes the
      direct input (marks are entered on the total only). */
  function markInputs(paper: Paper, studentId: number, withParts = true) {
    const cell = cellOf(paper.id, studentId);
    const numInput = (
      value: string,
      max: number | null,
      onChange: (v: string) => void,
      readOnly = false,
    ) => (
      <input
        type="number"
        min="0"
        max={max ?? undefined}
        className={`form-control form-control-sm ${readOnly ? "bg-light" : ""}`}
        style={{ maxWidth: 92 }}
        value={value}
        readOnly={readOnly}
        disabled={marksLocked}
        onChange={(e) => onChange(e.target.value)}
      />
    );
    return (
      <>
        {withParts && (
          <>
            <td>{paper.cqMarks != null ? numInput(cell.cq, paper.cqMarks, (v) => onMarkPart(paper, studentId, "cq", v)) : "—"}</td>
            <td>{paper.mcqMarks != null ? numInput(cell.mcq, paper.mcqMarks, (v) => onMarkPart(paper, studentId, "mcq", v)) : "—"}</td>
            <td>{paper.appliedMarks != null ? numInput(cell.applied, paper.appliedMarks, (v) => onMarkPart(paper, studentId, "applied", v)) : "—"}</td>
          </>
        )}
        <td>
          {hasParts(paper)
            ? numInput(cell.total, paper.fullMarks, () => {}, true)
            : numInput(cell.total, paper.fullMarks, (v) => onMarkTotal(paper, studentId, v))}
        </td>
      </>
    );
  }

  /** Rows currently visible in the active entry mode. */
  function visiblePairs(): { paper: Paper; studentId: number }[] {
    if (markMode === "subject" && selectedMarkPaper) {
      return studentsList.map((s) => ({ paper: selectedMarkPaper, studentId: s.id }));
    }
    if (markMode === "student" && selectedMarkStudent) {
      return papersList.map((p) => ({ paper: p, studentId: selectedMarkStudent.id }));
    }
    return [];
  }

  async function saveMarks() {
    const pairs = visiblePairs().filter(({ paper, studentId }) => {
      const c = cellOf(paper.id, studentId);
      return c.cq !== "" || c.mcq !== "" || c.applied !== "" || c.total !== "";
    });
    if (!marksExamId || pairs.length === 0) return;
    setMarksBusy(true);
    setMessage(null);
    try {
      const res = await api<{ saved: number }>(`/api/portal/exams/${marksExamId}/marks`, {
        method: "POST",
        body: JSON.stringify({
          rows: pairs.map(({ paper, studentId }) => {
            const c = cellOf(paper.id, studentId);
            return {
              examScheduleId: paper.id,
              studentId,
              cq: c.cq === "" ? null : Number(c.cq),
              mcq: c.mcq === "" ? null : Number(c.mcq),
              applied: c.applied === "" ? null : Number(c.applied),
              total: c.total === "" ? null : Number(c.total),
            };
          }),
        }),
      });
      setMessage({ type: "success", text: `${t.portal.examsPage.marksSaved} (${res.saved})` });
      markRows.reload();
    } catch (err) {
      const code = err instanceof Error ? err.message : "";
      setMessage({
        type: "error",
        text:
          code === "MARKS_EXCEED"
            ? t.portal.examsPage.marksExceedError
            : code === "RESULT_PUBLISHED"
              ? t.portal.examsPage.marksLockedText
              : t.common.tryAgain,
      });
    } finally {
      setMarksBusy(false);
    }
  }

  const selectedExam = examsData.data?.exams.find((e) => String(e.id) === examId) ?? null;

  function openExamCreate() {
    setEditingExam(null);
    setExamForm({ ...emptyExam, startDate: todayISO() });
    setExamModal(true);
  }
  function openExamEdit(e: Exam) {
    setEditingExam(e);
    setExamForm({
      name: e.name,
      nameBn: e.nameBn ?? "",
      branchId: e.branchId ? String(e.branchId) : "",
      classItemId: e.classItemId ? String(e.classItemId) : "",
      sectionItemId: e.sectionItemId ? String(e.sectionItemId) : "",
      groupItemId: e.groupItemId ? String(e.groupItemId) : "",
      departmentItemId: e.departmentItemId ? String(e.departmentItemId) : "",
      shiftItemId: e.shiftItemId ? String(e.shiftItemId) : "",
      startDate: e.startDate.slice(0, 10),
      endDate: e.endDate ? e.endDate.slice(0, 10) : "",
      status: e.status,
      format: e.format ?? "SINGLE",
      note: e.note ?? "",
    });
    setExamModal(true);
  }

  async function submitExam(ev: React.FormEvent) {
    ev.preventDefault();
    setBusy(true);
    setMessage(null);
    try {
      const payload = {
        name: examForm.name,
        nameBn: examForm.nameBn,
        branchId: examForm.branchId || null,
        classItemId: examForm.classItemId || null,
        sectionItemId: examForm.sectionItemId || null,
        groupItemId: examForm.groupItemId || null,
        departmentItemId: examForm.departmentItemId || null,
        shiftItemId: examForm.shiftItemId || null,
        startDate: examForm.startDate,
        endDate: examForm.endDate || null,
        status: examForm.status,
        format: examForm.format,
        note: examForm.note,
      };
      if (editingExam) {
        await api(`/api/portal/exams/${editingExam.id}`, { method: "PATCH", body: JSON.stringify(payload) });
        setMessage({ type: "success", text: t.portal.examsPage.examUpdated });
      } else {
        await api("/api/portal/exams", { method: "POST", body: JSON.stringify(payload) });
        setMessage({ type: "success", text: t.portal.examsPage.examCreated });
      }
      setExamModal(false);
      examsData.reload();
    } catch (err) {
      setMessage({
        type: "error",
        text: err instanceof Error && err.message === "END_BEFORE_START" ? t.portal.examsPage.endBeforeStart : t.common.tryAgain,
      });
    } finally {
      setBusy(false);
    }
  }

  async function removeExam(e: Exam) {
    if (!(await emsConfirm({ message: t.portal.examsPage.deleteExamConfirm, danger: true }))) return;
    try {
      await api(`/api/portal/exams/${e.id}`, { method: "DELETE" });
      if (String(e.id) === examId) setExamId("");
      setMessage({ type: "success", text: t.portal.examsPage.examDeleted });
      examsData.reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    }
  }

  function openPaper() {
    setEditingPaper(null);
    setPaperForm({ ...emptyPaper, date: selectedExam?.startDate?.slice(0, 10) ?? todayISO() });
    setPaperModal(true);
  }

  function openPaperEdit(p: Paper) {
    setEditingPaper(p);
    setPaperForm({
      subjectItemId: p.subjectItemId ? String(p.subjectItemId) : "",
      date: p.date.slice(0, 10),
      appliedDate: p.appliedDate ? p.appliedDate.slice(0, 10) : "",
      startTime: p.startTime,
      endTime: p.endTime,
      roomItemId: p.roomItemId ? String(p.roomItemId) : "",
      fullMarks: String(p.fullMarks),
      totalPassMarks: p.totalPassMarks != null ? String(p.totalPassMarks) : "",
      cqMarks: p.cqMarks != null ? String(p.cqMarks) : "",
      cqPassMarks: p.cqPassMarks != null ? String(p.cqPassMarks) : "",
      mcqMarks: p.mcqMarks != null ? String(p.mcqMarks) : "",
      mcqPassMarks: p.mcqPassMarks != null ? String(p.mcqPassMarks) : "",
      appliedMarks: p.appliedMarks != null ? String(p.appliedMarks) : "",
      appliedPassMarks: p.appliedPassMarks != null ? String(p.appliedPassMarks) : "",
      note: p.note ?? "",
    });
    setPaperModal(true);
  }

  async function submitPaper(ev: React.FormEvent) {
    ev.preventDefault();
    setBusy(true);
    setMessage(null);
    try {
      const payload = {
        subjectItemId: paperForm.subjectItemId || null,
        date: paperForm.date,
        startTime: paperForm.startTime,
        endTime: paperForm.endTime,
        roomItemId: paperForm.roomItemId || null,
        fullMarks: Number(paperForm.fullMarks),
        totalPassMarks: paperForm.totalPassMarks === "" ? null : Number(paperForm.totalPassMarks),
        cqMarks: paperForm.cqMarks === "" ? null : Number(paperForm.cqMarks),
        cqPassMarks: paperForm.cqPassMarks === "" ? null : Number(paperForm.cqPassMarks),
        mcqMarks: paperForm.mcqMarks === "" ? null : Number(paperForm.mcqMarks),
        mcqPassMarks: paperForm.mcqPassMarks === "" ? null : Number(paperForm.mcqPassMarks),
        appliedMarks: paperForm.appliedMarks === "" ? null : Number(paperForm.appliedMarks),
        appliedPassMarks: paperForm.appliedPassMarks === "" ? null : Number(paperForm.appliedPassMarks),
        appliedDate: paperForm.appliedDate || null,
        note: paperForm.note,
      };
      if (editingPaper) {
        await api(`/api/portal/exam-schedules/${editingPaper.id}`, { method: "PATCH", body: JSON.stringify(payload) });
        setMessage({ type: "success", text: t.portal.examsPage.paperUpdated });
      } else {
        await api(`/api/portal/exams/${examId}/schedules`, { method: "POST", body: JSON.stringify(payload) });
        setMessage({ type: "success", text: t.portal.examsPage.paperAdded });
      }
      setPaperModal(false);
      papers.reload();
      examsData.reload();
    } catch (err) {
      const code = err instanceof Error ? err.message : "";
      setMessage({
        type: "error",
        text:
          code === "ROOM_CLASH"
            ? t.portal.examsPage.roomClash
            : code === "SUBJECT_EXISTS"
              ? t.portal.examsPage.subjectExists
              : code === "END_BEFORE_START"
                ? t.portal.examsPage.endBeforeStart
                : code === "PASS_EXCEEDS_MARKS"
                  ? t.portal.examsPage.passExceedsMarks
                  : code === "SCHEDULE_PUBLISHED"
                    ? t.portal.examsPage.scheduleLocked
                    : t.common.tryAgain,
      });
      setPaperModal(false);
    } finally {
      setBusy(false);
    }
  }

  async function removePaper(p: Paper) {
    if (!(await emsConfirm({ message: t.portal.examsPage.deletePaperConfirm, danger: true }))) return;
    try {
      await api(`/api/portal/exam-schedules/${p.id}`, { method: "DELETE" });
      setMessage({ type: "success", text: t.portal.examsPage.paperDeleted });
      papers.reload();
      examsData.reload();
    } catch (err) {
      const code = err instanceof Error ? err.message : "";
      setMessage({
        type: "error",
        text: code === "SCHEDULE_PUBLISHED" ? t.portal.examsPage.scheduleLocked : t.common.tryAgain,
      });
    }
  }

  /** Publish the routine (locks add/edit/delete of papers) or allow edit again. */
  async function setSchedulePublished(val: boolean) {
    if (!selectedExam) return;
    const confirmed = (await emsConfirm({ message: val ? t.portal.examsPage.publishConfirm : t.portal.examsPage.allowEditConfirm, danger: false }));
    if (!confirmed) return;
    try {
      await api(`/api/portal/exams/${selectedExam.id}`, {
        method: "PATCH",
        body: JSON.stringify({ schedulePublished: val }),
      });
      setMessage({ type: "success", text: val ? t.portal.examsPage.schedulePublishedMsg : t.portal.examsPage.allowEditDone });
      examsData.reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    }
  }

  /** Printable exam schedule report — the shared builder (lib/exam-schedule-report.ts)
      renders the same design the member side prints. kind="details" includes the
      marks split (full / "out of" passing) and applied dates; kind="summary"
      omits marks. Optional verification QR (checkbox-controlled). */
  async function printScheduleReport(kind: "details" | "summary") {
    const exam = selectedExam;
    const rows = papers.data?.schedules ?? [];
    if (!exam) return;
    const L = t.portal.examsPage;
    const labels: ScheduleReportLabels = {
      summaryReport: L.summaryReport,
      detailsReport: L.detailsReport,
      branchLabel: L.branchLabel,
      mainBranch: L.mainBranch,
      classLabel: L.classLabel,
      sectionLabel: L.sectionLabel,
      groupLabel: L.groupLabel,
      departmentLabel: L.departmentLabel,
      shiftLabel: L.shiftLabel,
      startDate: L.startDate,
      endDate: L.endDate,
      totalPapers: L.totalPapers,
      subject: L.subject,
      date: L.date,
      startTime: L.startTime,
      endTime: L.endTime,
      room: L.room,
      appliedExamDate: L.appliedExamDate,
      marksColumn: L.marksColumn,
      fullPass: L.fullPass,
      appliedShort: L.appliedShort,
      reportTitle: L.reportTitle,
      publishedBadge: L.publishedBadge,
      generatedOn: L.generatedOn,
      preparedBy: L.preparedBy,
      approvedBy: L.approvedBy,
      scanToVerify: t.portal.resultsPage.scanToVerify,
    };
    const html = await buildExamScheduleReportHtml({
      kind,
      exam: exam as ScheduleReportExam,
      schedules: rows as ScheduleReportPaper[],
      inst: me.data?.user?.institution ?? null,
      locale,
      labels,
      noteLabel: t.common.note,
      statusText: t.status[exam.status as keyof typeof t.status] ?? exam.status,
      publishedBadge: exam.schedulePublished,
      primary: rptPrimary,
      accent: rptAccent,
      showQr: showReportQr && scheduleQrAllowed,
      mintQr: async () => {
        const res = await api<{ code: string }>("/api/portal/verify-codes", {
          method: "POST",
          body: JSON.stringify({
            kind: "EXAM_SCHEDULE",
            refKey: `exam:${exam.id}`,
            label: exam.name,
          }),
        });
        return res.code;
      },
      poweredBy: t.landing.poweredBy,
    });
    const w = window.open("", "_blank", "width=980,height=760");
    if (!w) return;
    w.document.write(html);
    w.document.close();
    w.focus();
    setTimeout(() => w.print(), 350);
  }

  const tabs: { id: Tab; label: string }[] = [
    { id: "exams", label: t.portal.examsPage.tabExams },
    { id: "schedule", label: t.portal.examsPage.tabSchedule },
    { id: "marks", label: t.portal.examsPage.marksInput },
  ];

  return (
    <div>
      <PageHeader
        title={t.portal.examsPage.title}
        subtitle={t.portal.examsPage.subtitle}
        action={
          <Button onClick={openExamCreate}>
            <IconPlus className="svg-16" />
            <span className="ms-1">{t.portal.examsPage.newExam}</span>
          </Button>
        }
      />

      {message && (
        <div className="mb-4">
          <Alert type={message.type} onClose={() => setMessage(null)}>{message.text}</Alert>
        </div>
      )}

      {/* Tabs */}
      <ul className="nav nav-tabs mb-4">
        {tabs.map((x) => (
          <li className="nav-item" key={x.id}>
            <button className={`nav-link f-w-600 ${tab === x.id ? "active" : ""}`} type="button" onClick={() => setTab(x.id)}>
              {x.label}
            </button>
          </li>
        ))}
      </ul>

      {/* ---------- Exams tab ---------- */}
      {tab === "exams" && (
        <div className="card">
          <div className="card-header">
            <div className="row g-2 align-items-end">
              <div className="col-md-3">
                <Select label={t.portal.examsPage.filterClass} wrapperClassName="form-group mb-0" value={filterClass} onChange={(e) => setFilterClass(e.target.value)}>
                  <option value="">{t.portal.examsPage.allClasses}</option>
                  {classItems.map((c) => (
                    <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                  ))}
                </Select>
              </div>
              <div className="col-md-3">
                <Select label={t.portal.examsPage.filterShift} wrapperClassName="form-group mb-0" value={filterShift} onChange={(e) => setFilterShift(e.target.value)}>
                  <option value="">{t.portal.examsPage.allShifts}</option>
                  {shiftItems.map((c) => (
                    <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                  ))}
                </Select>
              </div>
            </div>
          </div>
          {examsData.loading && !examsData.data ? (
            <div className="card-body text-center py-4">
              <span className="spinner-border text-primary" role="status" />
            </div>
          ) : exams.length === 0 ? (
            <div className="card-body">
              <EmptyState icon={<IconFile />} title={t.portal.examsPage.noExams} />
            </div>
          ) : (
            <>
              <div className="table-responsive">
                <table className="table table-hover align-middle mb-0">
                  <thead className="ems-table-head">
                    <tr>
                      <th>{t.portal.examsPage.examName}</th>
                      <th>{t.portal.examsPage.branchLabel}</th>
                      <th>{t.portal.examsPage.classLabel}</th>
                      <th>{t.portal.examsPage.sectionLabel}</th>
                      <th>{t.portal.examsPage.groupLabel}</th>
                      <th>{t.portal.examsPage.departmentLabel}</th>
                      <th>{t.portal.examsPage.shiftLabel}</th>
                      <th>{t.portal.examsPage.startDate} → {t.portal.examsPage.endDate}</th>
                      <th className="text-center">{t.portal.examsPage.papers}</th>
                      <th>{t.common.status}</th>
                      <th className="text-end">{t.common.actions}</th>
                    </tr>
                  </thead>
                  <tbody>
                    {examsPager.paged.map((e) => (
                      <tr key={e.id}>
                        <td>
                          <p className="mb-0 f-w-700">{e.name}</p>
                          {e.nameBn && <p className="mb-0 ems-fs-11 text-muted">{e.nameBn}</p>}
                        </td>
                        <td>
                          {e.branchName ? (
                            <span className="badge badge-secondary">{e.branchName}</span>
                          ) : (
                            <span className="ems-fs-12 text-muted">{t.portal.examsPage.mainBranch}</span>
                          )}
                        </td>
                        <td>
                          {e.className ? <span className="ems-type-pill ems-cat-CLASS">{locale === "bn" ? e.classNameBn : e.className}</span> : "—"}
                        </td>
                        <td className="ems-fs-13">
                          {e.sectionName ? (locale === "bn" && e.sectionNameBn ? e.sectionNameBn : e.sectionName) : "—"}
                        </td>
                        <td className="ems-fs-13">
                          {e.groupName ? (locale === "bn" && e.groupNameBn ? e.groupNameBn : e.groupName) : "—"}
                        </td>
                        <td className="ems-fs-13">
                          {e.departmentName ? (locale === "bn" && e.departmentNameBn ? e.departmentNameBn : e.departmentName) : "—"}
                        </td>
                        <td>{e.shiftName ? <span className="badge badge-info">{locale === "bn" ? e.shiftNameBn : e.shiftName}</span> : "—"}</td>
                        <td className="ems-fs-12 text-muted">
                          {formatDate(e.startDate, locale)}{e.endDate ? ` → ${formatDate(e.endDate, locale)}` : ""}
                        </td>
                        <td className="text-center f-w-700">{e.paperCount}</td>
                        <td>
                          <StatusBadge status={e.status} label={t.status[e.status as keyof typeof t.status]} />
                        </td>
                        <td className="text-end">
                          <div className="d-inline-flex gap-1">
                            <Button
                              size="sm"
                              variant="secondary"
                              title={t.portal.examsPage.tabSchedule}
                              onClick={() => {
                                setTab("schedule");
                                setExamId(String(e.id));
                              }}
                            >
                              <IconFile className="svg-16" />
                            </Button>
                            <Button size="sm" variant="secondary" title={t.common.edit} onClick={() => openExamEdit(e)}>
                              <IconEdit className="svg-16" />
                            </Button>
                            <Button size="sm" variant="danger" title={t.common.delete} onClick={() => removeExam(e)}>
                              <IconTrash className="svg-16" />
                            </Button>
                          </div>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              <Pagination {...examsPager} onPageChange={examsPager.setPage} onSizeChange={examsPager.setSize} />
            </>
          )}
        </div>
      )}

      {/* ---------- Schedule tab ---------- */}
      {tab === "schedule" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div className="d-flex flex-wrap align-items-center gap-2" style={{ minWidth: 320, maxWidth: 480 }}>
              <div className="flex-grow-1">
                <Select
                  wrapperClassName="form-group mb-0"
                  value={examId}
                  onChange={(e) => setExamId(e.target.value)}
                >
                  <option value="">— {t.portal.examsPage.selectExam} —</option>
                  {(examsData.data?.exams ?? []).map((e) => (
                    <option key={e.id} value={e.id}>
                      {e.name}
                      {e.branchName ? ` — ${e.branchName}` : ""}
                      {e.className ? ` · ${e.className}` : ""}
                      {e.sectionName ? ` · ${e.sectionName}` : ""}
                      {e.shiftName ? ` · ${e.shiftName}` : ""}
                    </option>
                  ))}
                </Select>
              </div>
            </div>
            {selectedExam && (
              <div className="d-flex flex-wrap align-items-center gap-2">
                <StatusBadge status={selectedExam.status} label={t.status[selectedExam.status as keyof typeof t.status]} />
                {selectedExam.schedulePublished && (
                  <span className="badge badge-success">{t.portal.examsPage.publishedBadge}</span>
                )}
                <Button
                  size="sm"
                  disabled={selectedExam.schedulePublished}
                  title={selectedExam.schedulePublished ? t.portal.examsPage.scheduleLocked : undefined}
                  onClick={openPaper}
                >
                  <IconPlus className="svg-16" />
                  <span className="ms-1">{t.portal.examsPage.addPaper}</span>
                </Button>
                {selectedExam.schedulePublished ? (
                  <Button size="sm" variant="warning" title={t.portal.examsPage.allowEdit} onClick={() => setSchedulePublished(false)}>
                    <IconEdit className="svg-16" />
                    <span className="ms-1">{t.portal.examsPage.allowEdit}</span>
                  </Button>
                ) : (
                  <Button size="sm" variant="success" title={t.portal.examsPage.publishSchedule} onClick={() => setSchedulePublished(true)}>
                    <IconCheck className="svg-16" />
                    <span className="ms-1">{t.portal.examsPage.publishSchedule}</span>
                  </Button>
                )}
                <Button size="sm" variant="secondary" onClick={() => printScheduleReport("details")}>
                  <IconFile className="svg-16" />
                  <span className="ms-1">{t.portal.examsPage.detailsReport}</span>
                </Button>
                <Button size="sm" variant="secondary" onClick={() => printScheduleReport("summary")}>
                  <IconFile className="svg-16" />
                  <span className="ms-1">{t.portal.examsPage.summaryReport}</span>
                </Button>

                {/* report theme — same preset/custom picker as the marksheet */}
                <div className="d-flex align-items-center gap-2 ms-sm-2 ps-sm-2 border-start">
                  <span className="ems-fs-12 f-w-700 text-muted">{t.portal.resultsPage.colorsLabel}</span>
                  {REPORT_PRESETS.map((preset, i) => {
                    const active = rptPrimary === preset.primary && rptAccent === preset.accent;
                    return (
                      <button
                        key={i}
                        type="button"
                        className={`btn btn-sm p-0 ${active ? "" : "btn-outline-secondary"}`}
                        style={{
                          width: 32, height: 28, borderRadius: 8,
                          background: `linear-gradient(135deg, ${preset.primary} 55%, ${preset.accent} 55%)`,
                          border: active ? `2px solid ${preset.primary}` : undefined,
                          boxShadow: active ? `0 0 0 3px ${preset.primary}33` : undefined,
                        }}
                        onClick={() => { setRptPrimary(preset.primary); setRptAccent(preset.accent); }}
                        aria-label={`report-theme-${i}`}
                      >
                        {active && <span style={{ color: "#fff", fontSize: 12, lineHeight: 1 }}>✓</span>}
                      </button>
                    );
                  })}
                  <input
                    type="color"
                    className="form-control form-control-color cursor-pointer"
                    style={{ width: 36, height: 28 }}
                    value={rptPrimary}
                    onChange={(e) => setRptPrimary(e.target.value)}
                    title={t.portal.resultsPage.primaryLabel}
                  />
                  <input
                    type="color"
                    className="form-control form-control-color cursor-pointer"
                    style={{ width: 36, height: 28 }}
                    value={rptAccent}
                    onChange={(e) => setRptAccent(e.target.value)}
                    title={t.portal.resultsPage.accentLabel}
                  />
                  {scheduleQrAllowed && (
                    <label
                      className="form-check d-flex align-items-center gap-2 ems-fs-12 f-w-600 cursor-pointer mb-0"
                      title={t.portal.examsPage.showQrHint}
                    >
                      <input
                        className="form-check-input mt-0 cursor-pointer"
                        type="checkbox"
                        checked={showReportQr}
                        onChange={(e) => setShowReportQr(e.target.checked)}
                      />
                      {t.portal.examsPage.showQr}
                    </label>
                  )}
                </div>
              </div>
            )}
          </div>
          {!examId ? (
            <div className="card-body">
              <EmptyState icon={<IconFile />} title={t.portal.examsPage.selectExam} />
            </div>
          ) : papers.loading && !papers.data ? (
            <div className="card-body text-center py-4">
              <span className="spinner-border text-primary" role="status" />
            </div>
          ) : !papers.data || papers.data.schedules.length === 0 ? (
            <div className="card-body">
              <EmptyState icon={<IconFile />} title={t.portal.examsPage.noPapers} />
            </div>
          ) : (
            <>
              <div className="table-responsive">
                <table className="table table-hover align-middle mb-0">
                  <thead className="ems-table-head">
                    <tr>
                      <th>{t.portal.examsPage.subject}</th>
                      <th>{t.portal.examsPage.date}</th>
                      <th>{t.portal.examsPage.appliedExamDate}</th>
                      <th>{t.portal.examsPage.startTime} → {t.portal.examsPage.endTime}</th>
                      <th>{t.portal.examsPage.room}</th>
                      <th className="text-center">{t.portal.examsPage.marksColumn}</th>
                      <th className="text-center">CQ</th>
                      <th className="text-center">MCQ</th>
                      <th className="text-center">{t.portal.examsPage.appliedShort}</th>
                      <th className="text-end">{t.common.actions}</th>
                    </tr>
                  </thead>
                  <tbody>
                    {papersPager.paged.map((p) => (
                      <tr key={p.id}>
                        <td className="f-w-700">
                          {p.subjectName ? (locale === "bn" && p.subjectNameBn ? p.subjectNameBn : p.subjectName) : "—"}
                          {p.note && <p className="mb-0 ems-fs-11 text-muted f-w-500">{p.note}</p>}
                        </td>
                        <td className="ems-fs-12 text-muted">{formatDate(p.date, locale)}</td>
                        <td className="ems-fs-12 text-muted">
                          {p.appliedDate ? formatDate(p.appliedDate, locale) : "—"}
                        </td>
                        <td className="ems-fs-12">
                          {formatTime(p.startTime, locale)} → {formatTime(p.endTime, locale)}
                        </td>
                        <td>{p.roomName ? <span className="badge badge-secondary">{locale === "bn" && p.roomNameBn ? p.roomNameBn : p.roomName}</span> : "—"}</td>
                        <td className="text-center f-w-700">
                          {p.fullMarks}
                          {p.totalPassMarks != null ? ` / ${p.totalPassMarks}` : ""}
                        </td>
                        <td className="text-center ems-fs-13">
                          {p.cqMarks != null ? `${p.cqMarks}${p.cqPassMarks != null ? ` / ${p.cqPassMarks}` : ""}` : "—"}
                        </td>
                        <td className="text-center ems-fs-13">
                          {p.mcqMarks != null ? `${p.mcqMarks}${p.mcqPassMarks != null ? ` / ${p.mcqPassMarks}` : ""}` : "—"}
                        </td>
                        <td className="text-center ems-fs-13">
                          {p.appliedMarks != null ? `${p.appliedMarks}${p.appliedPassMarks != null ? ` / ${p.appliedPassMarks}` : ""}` : "—"}
                        </td>
                        <td className="text-end">
                          <div className="d-inline-flex gap-1">
                            <Button
                              size="sm"
                              variant="secondary"
                              title={selectedExam?.schedulePublished ? t.portal.examsPage.scheduleLocked : t.portal.examsPage.editPaper}
                              disabled={selectedExam?.schedulePublished}
                              onClick={() => openPaperEdit(p)}
                            >
                              <IconEdit className="svg-16" />
                            </Button>
                            <Button
                              size="sm"
                              variant="danger"
                              title={selectedExam?.schedulePublished ? t.portal.examsPage.scheduleLocked : t.common.delete}
                              disabled={selectedExam?.schedulePublished}
                              onClick={() => removePaper(p)}
                            >
                              <IconTrash className="svg-16" />
                            </Button>
                          </div>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              <Pagination {...papersPager} onPageChange={papersPager.setPage} onSizeChange={papersPager.setSize} />
            </>
          )}
        </div>
      )}

      {/* ---------- Marks input tab ---------- */}
      {tab === "marks" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div className="d-flex flex-wrap align-items-center gap-2">
              <div style={{ minWidth: 260, maxWidth: 360 }}>
                <Select
                  wrapperClassName="form-group mb-0"
                  value={marksExamId}
                  onChange={(e) => {
                    setMarksExamId(e.target.value);
                    setMarkPaperId("");
                    setMarkStudentId("");
                    setMarkCells({});
                  }}
                >
                  <option value="">— {t.portal.examsPage.selectExam} —</option>
                  {(examsData.data?.exams ?? []).map((e) => (
                    <option key={e.id} value={e.id}>
                      {e.name}
                      {e.branchName ? ` — ${e.branchName}` : ""}
                      {e.className ? ` · ${e.className}` : ""}
                      {e.sectionName ? ` · ${e.sectionName}` : ""}
                    </option>
                  ))}
                </Select>
              </div>
              {marksExamId && (
                <>
                  <div className="btn-group" role="group">
                    <button
                      type="button"
                      className={`btn btn-sm ${markMode === "subject" ? "btn-primary" : "btn-outline-primary"}`}
                      onClick={() => setMarkMode("subject")}
                    >
                      {t.portal.examsPage.subjectWise}
                    </button>
                    <button
                      type="button"
                      className={`btn btn-sm ${markMode === "student" ? "btn-primary" : "btn-outline-primary"}`}
                      onClick={() => setMarkMode("student")}
                    >
                      {t.portal.examsPage.studentWise}
                    </button>
                  </div>
                  {markMode === "subject" ? (
                    <div style={{ minWidth: 220 }}>
                      <Select
                        wrapperClassName="form-group mb-0"
                        value={markPaperId}
                        onChange={(e) => setMarkPaperId(e.target.value)}
                      >
                        <option value="">— {t.portal.examsPage.selectSubject} —</option>
                        {papersList.map((p) => (
                          <option key={p.id} value={p.id}>
                            {p.subjectName ?? `#${p.id}`} · {formatDate(p.date, locale)}
                          </option>
                        ))}
                      </Select>
                    </div>
                  ) : (
                    <div style={{ minWidth: 220 }}>
                      <Select
                        wrapperClassName="form-group mb-0"
                        value={markStudentId}
                        onChange={(e) => setMarkStudentId(e.target.value)}
                      >
                        <option value="">— {t.portal.examsPage.selectStudent} —</option>
                        {studentsList.map((s) => (
                          <option key={s.id} value={s.id}>
                            {s.roll} — {locale === "bn" && s.nameBn ? s.nameBn : s.name}
                          </option>
                        ))}
                      </Select>
                    </div>
                  )}
                </>
              )}
              {marksExam && (
                marksExam.resultPublished ? (
                  <div className="d-flex align-items-center gap-2 ms-auto">
                    <span className="badge badge-success">{t.portal.examsPage.resultPublishedBadge}</span>
                    <Button size="sm" variant="warning" onClick={() => askResultPublish(false)}>
                      {t.portal.examsPage.unpublishResult}
                    </Button>
                  </div>
                ) : (
                  <Button size="sm" variant="success" className="ms-auto" onClick={() => askResultPublish(true)}>
                    <IconCheck className="svg-16" />
                    <span className="ms-1">{t.portal.examsPage.publishResult}</span>
                  </Button>
                )
              )}
            </div>
          </div>

          {!marksExamId ? (
            <div className="card-body">
              <EmptyState icon={<IconEdit />} title={t.portal.examsPage.selectExam} />
            </div>
          ) : papersList.length === 0 ? (
            <div className="card-body">
              <EmptyState icon={<IconEdit />} title={t.portal.examsPage.noPapers} />
            </div>
          ) : studentsList.length === 0 ? (
            <div className="card-body">
              <EmptyState icon={<IconEdit />} title={t.portal.examsPage.noStudents} />
            </div>
          ) : (markMode === "subject" && !selectedMarkPaper) || (markMode === "student" && !selectedMarkStudent) ? (
            <div className="card-body">
              <EmptyState
                icon={<IconEdit />}
                title={markMode === "subject" ? t.portal.examsPage.selectSubject : t.portal.examsPage.selectStudent}
              />
            </div>
          ) : (
            <>
              <div className="table-responsive" style={{ position: "relative" }}>
                {/* result published → clicks anywhere on the grid show a themed modal alert */}
                {marksLocked && (
                  <div
                    role="presentation"
                    onClick={() =>
                      setNotice({ title: t.portal.examsPage.marksLockedTitle, text: t.portal.examsPage.marksLockedText })
                    }
                    style={{ position: "absolute", inset: 0, zIndex: 5, cursor: "not-allowed", background: "rgba(233,236,239,0.35)" }}
                  />
                )}
                <table className="table table-hover align-middle mb-0">
                  <thead className="ems-table-head">
                    <tr>
                      <th style={{ minWidth: 120 }}>{markMode === "subject" ? t.portal.examsPage.rollLabel : t.portal.examsPage.subject}</th>
                      <th>{markMode === "subject" ? t.portal.examsPage.studentLabel : t.portal.examsPage.marksColumn}</th>
                      {!hidePartColumns() && (
                        <>
                          <th className="text-center">CQ{partHints().cq != null ? ` (≤ ${partHints().cq})` : ""}</th>
                          <th className="text-center">MCQ{partHints().mcq != null ? ` (≤ ${partHints().mcq})` : ""}</th>
                          <th className="text-center">{t.portal.examsPage.appliedShort}{partHints().applied != null ? ` (≤ ${partHints().applied})` : ""}</th>
                        </>
                      )}
                      <th className="text-center">{t.portal.examsPage.totalMarks}</th>
                    </tr>
                  </thead>
                  <tbody>
                    {markMode === "subject"
                      ? studentsList.map((s) => (
                          <tr key={s.id}>
                            <td className="f-w-600">{s.roll}</td>
                            <td className="f-w-600">{locale === "bn" && s.nameBn ? s.nameBn : s.name}</td>
                            {markInputs(selectedMarkPaper!, s.id, !hidePartColumns())}
                          </tr>
                        ))
                      : papersList.map((p) => (
                          <tr key={p.id}>
                            <td className="f-w-600">{p.subjectName ?? `#${p.id}`}</td>
                            <td className="ems-fs-12 text-muted">
                              {formatDate(p.date, locale)} · {t.portal.examsPage.marksColumn} {p.fullMarks}
                            </td>
                            {markInputs(p, selectedMarkStudent!.id)}
                          </tr>
                        ))}
                  </tbody>
                </table>
              </div>
              <div className="d-flex flex-wrap align-items-center justify-content-between gap-2 px-3 py-2 border-top">
                <p className="mb-0 ems-fs-12 text-muted">
                  {t.portal.examsPage.enteredCount}: <b>{enteredCount()}</b> / {markMode === "subject" ? studentsList.length : papersList.length}
                  {" · "}
                  {t.portal.examsPage.notGradedHint}
                </p>
                <span
                  onClick={() =>
                    marksLocked &&
                    setNotice({ title: t.portal.examsPage.marksLockedTitle, text: t.portal.examsPage.marksLockedText })
                  }
                >
                  <Button loading={marksBusy} disabled={marksLocked || visiblePairs().length === 0} onClick={saveMarks}>
                    <IconCheck className="svg-16" />
                    <span className="ms-1">{t.portal.examsPage.saveMarks}</span>
                  </Button>
                </span>
              </div>
            </>
          )}
        </div>
      )}

      {/* Exam modal */}
      <Modal open={examModal} onClose={() => setExamModal(false)} title={editingExam ? t.portal.examsPage.editExam : t.portal.examsPage.newExam} wide>
        <form className="theme-form" onSubmit={submitExam}>
          <div className="row">
            <div className="col-md-6">
              <Input label={t.portal.examsPage.examName} required value={examForm.name} onChange={(e) => setExamForm({ ...examForm, name: e.target.value })} />
            </div>
            <div className="col-md-6">
              <Input label={t.portal.examsPage.examNameBn} value={examForm.nameBn} onChange={(e) => setExamForm({ ...examForm, nameBn: e.target.value })} />
            </div>
            <div className="col-md-3">
              <Select label={t.portal.examsPage.branchLabel} value={examForm.branchId} onChange={(e) => setExamForm({ ...examForm, branchId: e.target.value })}>
                <option value="">
                  {branchesData.data?.institution
                    ? `${branchesData.data.institution.name} (${t.portal.examsPage.mainBranch})`
                    : `— ${t.portal.examsPage.mainBranch}`}
                </option>
                {branches.map((b) => (
                  <option key={b.id} value={b.id}>{b.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-3">
              <Select label={t.portal.examsPage.classLabel} value={examForm.classItemId} onChange={(e) => setExamForm({ ...examForm, classItemId: e.target.value })}>
                <option value="">—</option>
                {classItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-3">
              <Select
                label={sectionRequired ? `${t.portal.examsPage.sectionLabel} *` : t.portal.examsPage.sectionLabel}
                value={examForm.sectionItemId}
                onChange={(e) => setExamForm({ ...examForm, sectionItemId: e.target.value })}
              >
                <option value="">—</option>
                {sectionItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-3">
              <Select label={t.portal.examsPage.shiftLabel} value={examForm.shiftItemId} onChange={(e) => setExamForm({ ...examForm, shiftItemId: e.target.value })}>
                <option value="">—</option>
                {shiftItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-4">
              <Select label={t.portal.examsPage.groupLabel} value={examForm.groupItemId} onChange={(e) => setExamForm({ ...examForm, groupItemId: e.target.value })}>
                <option value="">—</option>
                {groupItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-4">
              <Select label={t.portal.examsPage.departmentLabel} value={examForm.departmentItemId} onChange={(e) => setExamForm({ ...examForm, departmentItemId: e.target.value })}>
                <option value="">—</option>
                {departmentItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-4">
              <Select label={t.portal.resultsPage.formatLabel} value={examForm.format} onChange={(e) => setExamForm({ ...examForm, format: e.target.value })}>
                {(["SINGLE", "PERIODICAL", "FINAL"] as const).map((f) => (
                  <option key={f} value={f}>{t.portal.resultsPage.formats[f]}</option>
                ))}
              </Select>
              <Select label={t.common.status} value={examForm.status} onChange={(e) => setExamForm({ ...examForm, status: e.target.value })}>
                {EXAM_STATUSES.map((s) => (
                  <option key={s} value={s}>{t.status[s as keyof typeof t.status]}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-6">
              <DatePicker label={t.portal.examsPage.startDate} value={examForm.startDate} onChange={(e) => setExamForm({ ...examForm, startDate: e.target.value })} />
            </div>
            <div className="col-md-6">
              <DatePicker label={t.portal.examsPage.endDate} value={examForm.endDate} min={examForm.startDate || undefined} onChange={(e) => setExamForm({ ...examForm, endDate: e.target.value })} />
            </div>
            <div className="col-md-12">
              <Textarea label={t.common.note} value={examForm.note} onChange={(e) => setExamForm({ ...examForm, note: e.target.value })} />
            </div>
          </div>
          <div className="d-flex justify-content-end gap-2">
            <Button type="button" variant="secondary" onClick={() => setExamModal(false)}>
              {t.common.cancel}
            </Button>
            <Button
              type="submit"
              loading={busy}
              disabled={!examForm.name.trim() || !examForm.startDate || (sectionRequired && !examForm.sectionItemId)}
            >
              {editingExam ? t.common.saveChanges : t.common.create}
            </Button>
          </div>
        </form>
      </Modal>

      {/* Paper modal */}
      <Modal
        open={paperModal}
        onClose={() => setPaperModal(false)}
        title={`${editingPaper ? t.portal.examsPage.editPaper : t.portal.examsPage.addPaper} — ${selectedExam?.name ?? ""}`}
        wide
      >
        <form className="theme-form" onSubmit={submitPaper}>
          <div className="row">
            <div className="col-md-4">
              <Select label={t.portal.examsPage.subject} value={paperForm.subjectItemId} onChange={(e) => setPaperForm({ ...paperForm, subjectItemId: e.target.value })}>
                <option value="">—</option>
                {subjectItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-4">
              <DatePicker label={t.portal.examsPage.date} value={paperForm.date} onChange={(e) => setPaperForm({ ...paperForm, date: e.target.value })} />
            </div>
            <div className="col-md-4">
              <DatePicker label={t.portal.examsPage.appliedExamDate} value={paperForm.appliedDate} onChange={(e) => setPaperForm({ ...paperForm, appliedDate: e.target.value })} />
            </div>
            <div className="col-md-3">
              <TimePicker label={t.portal.examsPage.startTime} value={paperForm.startTime} onChange={(e) => setPaperForm({ ...paperForm, startTime: e.target.value })} />
            </div>
            <div className="col-md-3">
              <TimePicker label={t.portal.examsPage.endTime} value={paperForm.endTime} onChange={(e) => setPaperForm({ ...paperForm, endTime: e.target.value })} />
            </div>
            <div className="col-md-6">
              <Select label={t.portal.examsPage.room} value={paperForm.roomItemId} onChange={(e) => setPaperForm({ ...paperForm, roomItemId: e.target.value })}>
                <option value="">—</option>
                {roomItems.map((c) => (
                  <option key={c.id} value={c.id}>{locale === "bn" ? c.nameBn : c.name}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-12 mt-2">
              <p className="mb-1 f-w-700 ems-fs-12">{t.portal.examsPage.marksSection}</p>
              <p className="mb-0 ems-fs-11 text-muted">{t.portal.examsPage.marksHint}</p>
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.totalMarks}
                type="number"
                min="1"
                required
                value={paperForm.fullMarks}
                onChange={(e) => setPaperForm({ ...paperForm, fullMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.outOfMarks}
                type="number"
                min="0"
                value={paperForm.totalPassMarks}
                onChange={(e) => setPaperForm({ ...paperForm, totalPassMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.cqMarks}
                type="number"
                min="0"
                value={paperForm.cqMarks}
                onChange={(e) => setPaperForm({ ...paperForm, cqMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.outOfCqMarks}
                type="number"
                min="0"
                value={paperForm.cqPassMarks}
                onChange={(e) => setPaperForm({ ...paperForm, cqPassMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.mcqMarks}
                type="number"
                min="0"
                value={paperForm.mcqMarks}
                onChange={(e) => setPaperForm({ ...paperForm, mcqMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.outOfMcqMarks}
                type="number"
                min="0"
                value={paperForm.mcqPassMarks}
                onChange={(e) => setPaperForm({ ...paperForm, mcqPassMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.appliedMarks}
                type="number"
                min="0"
                value={paperForm.appliedMarks}
                onChange={(e) => setPaperForm({ ...paperForm, appliedMarks: e.target.value })}
              />
            </div>
            <div className="col-md-3">
              <Input
                label={t.portal.examsPage.outOfAppliedMarks}
                type="number"
                min="0"
                value={paperForm.appliedPassMarks}
                onChange={(e) => setPaperForm({ ...paperForm, appliedPassMarks: e.target.value })}
              />
            </div>
            <div className="col-md-12">
              <Textarea label={t.common.note} value={paperForm.note} onChange={(e) => setPaperForm({ ...paperForm, note: e.target.value })} />
            </div>
          </div>
          <div className="d-flex justify-content-end gap-2">
            <Button type="button" variant="secondary" onClick={() => setPaperModal(false)}>
              {t.common.cancel}
            </Button>
            <Button
              type="submit"
              loading={busy}
              disabled={!paperForm.date || !(Number(paperForm.fullMarks) > 0) || paperForm.endTime <= paperForm.startTime}
            >
              {t.common.save}
            </Button>
          </div>
        </form>
      </Modal>

      {/* Themed notice modal (instead of window.alert) */}
      <Modal open={!!notice} onClose={() => setNotice(null)} title={notice?.title ?? ""}>
        <div className="mb-3">
          <Alert type="warning">{notice?.text}</Alert>
        </div>
        <div className="d-flex justify-content-end">
          <Button variant="secondary" onClick={() => setNotice(null)}>
            {t.common.close}
          </Button>
        </div>
      </Modal>

      {/* Themed confirm modal (instead of window.confirm) */}
      <Modal open={!!confirmModal} onClose={() => setConfirmModal(null)} title={confirmModal?.title ?? ""}>
        <div className="mb-3">
          <Alert type="warning">{confirmModal?.text}</Alert>
        </div>
        <div className="d-flex justify-content-end gap-2">
          <Button variant="secondary" onClick={() => setConfirmModal(null)}>
            {t.common.cancel}
          </Button>
          <Button
            onClick={async () => {
              const action = confirmModal?.onYes;
              setConfirmModal(null);
              await action?.();
            }}
          >
            {t.common.yes}
          </Button>
        </div>
      </Modal>
    </div>
  );
}
