"use client";

import { useEffect, useMemo, useState } from "react";
import { useLang } from "@/context/LanguageContext";
import {
  Alert,
  Button,
  DatePicker,
  EmptyState,
  Input,
  Modal,
  PageHeader,
  Select,
  StatusBadge,
  Textarea,
  TimePicker,
} from "@/components/ui";
import { api, useApi } from "@/lib/client";
import { formatDate, formatTime } from "@/lib/utils";
import { REPORT_PRESETS, mix } from "@/lib/report-html";
import { buildCampusRoutineHtml } from "@/lib/campus-reports";
import { IconCheck, IconEdit, IconFile, IconPlus, IconTrash } from "@/components/icons";
import { emsConfirm } from "@/components/ConfirmDialog";

interface Program { id: number; name: string; nameBn: string | null; code: string; gradeScale: string }
interface Term { id: number; programId: number; name: string; nameBn: string | null; sequence: number }
interface Course { id: number; programId: number; code: string; name: string; nameBn: string | null; credits: number }
interface Exam {
  id: number;
  programId: number;
  termId: number | null;
  name: string;
  nameBn: string | null;
  examType: string;
  sessionYear: string | null;
  startDate: string | null;
  endDate: string | null;
  status: string;
  schedulePublished: boolean;
  resultPublished: boolean;
  note: string | null;
  paperCount: number;
  program: { id: number; name: string; nameBn: string | null; gradeScale: string };
  term: { id: number; name: string; nameBn: string | null } | null;
}
interface Paper {
  id: number;
  examId: number;
  courseId: number;
  examDate: string | null;
  startTime: string | null;
  endTime: string | null;
  room: string | null;
  fullMarks: number;
  passMarks: number;
  note: string | null;
  course: { id: number; code: string; name: string; nameBn: string | null; credits: number; courseType: string };
  _count?: { marks: number };
}
interface MarksSchedule {
  id: number;
  examDate: string | null;
  startTime: string | null;
  endTime: string | null;
  room: string | null;
  fullMarks: number;
  passMarks: number;
  course: { id: number; code: string; name: string; nameBn: string | null; credits: number };
  marks: { studentId: number; marks: number | null; absent: boolean }[];
}
interface MarksStudent { id: number; studentIdNo: string; name: string; nameBn: string | null; sessionYear: string; courseIds: number[] }
interface Me { user: { institution: { id: number; name: string; logo?: string | null } | null } | null }

type Tab = "exams" | "schedule" | "marks";
type MarkMode = "course" | "student";

const EXAM_TYPES = ["MIDTERM", "SEMESTER_FINAL", "FINAL", "PRELIMINARY", "TEST", "QUIZ"];
const EXAM_STATUSES = ["UPCOMING", "RUNNING", "FINISHED"];

const EMPTY_EXAM = { name: "", nameBn: "", programId: "", termId: "", examType: "SEMESTER_FINAL", sessionYear: "", startDate: "", endDate: "", status: "UPCOMING", note: "" };
const EMPTY_PAPER = { courseId: "", examDate: "", startTime: "", endTime: "", room: "", fullMarks: "", passMarks: "" };

/** College & University exams: exam CRUD, course-wise routine with publish
    lock + printable report, and marks entry (course-wise / student-wise). */
export default function CampusExamsPage() {
  const { t, locale } = useLang();
  const L = t.portal.campusExamsPage;

  const [tab, setTab] = useState<Tab>("exams");
  const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
  const [saving, setSaving] = useState(false);

  const programsData = useApi<{ programs: Program[] }>("/api/portal/campus/programs");
  const termsData = useApi<{ terms: Term[] }>("/api/portal/campus/terms");
  const coursesData = useApi<{ courses: Course[] }>("/api/portal/campus/courses");
  const examsData = useApi<{ exams: Exam[] }>("/api/portal/campus/exams");
  const me = useApi<Me>("/api/auth/me");

  const programs = programsData.data?.programs ?? [];
  const terms = termsData.data?.terms ?? [];
  const courses = coursesData.data?.courses ?? [];
  const exams = examsData.data?.exams ?? [];

  const [filterProgram, setFilterProgram] = useState("");
  const filteredExams = filterProgram ? exams.filter((e) => e.programId === Number(filterProgram)) : exams;

  const [selectedExamId, setSelectedExamId] = useState<string>("");
  const selectedExam = exams.find((e) => e.id === Number(selectedExamId)) ?? null;
  const papersUrl = selectedExamId ? `/api/portal/campus/exams/${selectedExamId}/schedules` : null;
  const papersData = useApi<{ schedules: Paper[] }>(papersUrl);
  const papers = papersData.data?.schedules ?? [];

  const marksUrl = selectedExamId ? `/api/portal/campus/exams/${selectedExamId}/marks` : null;
  const marksData = useApi<{ exam: { resultPublished: boolean }; schedules: MarksSchedule[]; students: MarksStudent[] }>(marksUrl);
  const [markMode, setMarkMode] = useState<MarkMode>("course");
  const [markCourseId, setMarkCourseId] = useState<string>("");
  const [markStudentId, setMarkStudentId] = useState<string>("");
  const [markCells, setMarkCells] = useState<Record<string, { marks: string; absent: boolean }>>({});

  // exam modal
  const [examModal, setExamModal] = useState(false);
  const [editingExam, setEditingExam] = useState<Exam | null>(null);
  const [examForm, setExamForm] = useState({ ...EMPTY_EXAM });

  // paper modal
  const [paperModal, setPaperModal] = useState(false);
  const [editingPaper, setEditingPaper] = useState<Paper | null>(null);
  const [paperForm, setPaperForm] = useState({ ...EMPTY_PAPER });

  const nm = (en: string | null | undefined, bn: string | null | undefined) =>
    locale === "bn" && bn ? bn : en ?? "—";

  const errorText = (err: unknown, fallback: string) => {
    const code = err instanceof Error ? err.message : "";
    if (code === "COURSE_EXISTS") return L.errCourseExists;
    if (code === "COURSE_NOT_IN_PROGRAM") return L.errCourseNotInProgram;
    if (code === "ROOM_CLASH") return L.errRoomClash;
    if (code === "SCHEDULE_PUBLISHED") return L.errSchedulePublished;
    if (code === "RESULT_PUBLISHED") return L.errResultPublished;
    if (code === "END_BEFORE_START") return L.errEndBeforeStart;
    return fallback;
  };

  /* ------------------------------- exams ------------------------------- */

  function openExamCreate() {
    setEditingExam(null);
    setExamForm({ ...EMPTY_EXAM, programId: filterProgram });
    setExamModal(true);
  }
  function openExamEdit(e: Exam) {
    setEditingExam(e);
    setExamForm({
      name: e.name, nameBn: e.nameBn ?? "", programId: String(e.programId),
      termId: e.termId ? String(e.termId) : "", examType: e.examType,
      sessionYear: e.sessionYear ?? "",
      startDate: e.startDate ? String(e.startDate).slice(0, 10) : "",
      endDate: e.endDate ? String(e.endDate).slice(0, 10) : "",
      status: e.status, note: e.note ?? "",
    });
    setExamModal(true);
  }
  async function submitExam(ev: React.FormEvent) {
    ev.preventDefault();
    setSaving(true);
    try {
      const res = await api<{ exam: Exam }>(editingExam ? `/api/portal/campus/exams/${editingExam.id}` : "/api/portal/campus/exams", {
        method: editingExam ? "PATCH" : "POST",
        body: JSON.stringify(examForm),
      });
      setExamModal(false);
      setMessage({ type: "success", text: L.examSaved });
      examsData.reload();
      if (!editingExam) setSelectedExamId(String(res.exam.id));
    } catch (err) {
      setMessage({ type: "error", text: errorText(err, L.examSaved) });
    } finally {
      setSaving(false);
    }
  }
  async function deleteExam(e: Exam) {
    if (!(await emsConfirm({ message: L.confirmDeleteExam, danger: true }))) return;
    try {
      await api(`/api/portal/campus/exams/${e.id}`, { method: "DELETE" });
      if (selectedExamId === String(e.id)) setSelectedExamId("");
      setMessage({ type: "success", text: L.examDeleted });
      examsData.reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    }
  }
  async function patchExam(id: number, body: Record<string, unknown>) {
    await api(`/api/portal/campus/exams/${id}`, { method: "PATCH", body: JSON.stringify(body) });
    examsData.reload();
  }
  async function setSchedulePublished(val: boolean) {
    if (!selectedExam) return;
    const ok = await emsConfirm({ message: val ? L.publishRoutineConfirm : L.allowEditConfirm, danger: false });
    if (!ok) return;
    try {
      await patchExam(selectedExam.id, { schedulePublished: val });
      setMessage({ type: "success", text: val ? L.routinePublished : L.editAllowed });
      papersData.reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    }
  }
  async function setResultPublished(val: boolean) {
    if (!selectedExam) return;
    const ok = await emsConfirm({ message: val ? L.publishResultConfirm : L.unpublishResultConfirm, danger: false });
    if (!ok) return;
    try {
      await patchExam(selectedExam.id, { resultPublished: val });
      setMessage({ type: "success", text: val ? L.resultPublishedMsg : L.resultUnpublishedMsg });
      marksData.reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    }
  }

  /* ------------------------------ papers ------------------------------- */

  function openPaperCreate() {
    setEditingPaper(null);
    setPaperForm({ ...EMPTY_PAPER });
    setPaperModal(true);
  }
  function openPaperEdit(p: Paper) {
    setEditingPaper(p);
    setPaperForm({
      courseId: String(p.courseId),
      examDate: p.examDate ? String(p.examDate).slice(0, 10) : "",
      startTime: p.startTime ?? "", endTime: p.endTime ?? "",
      room: p.room ?? "", fullMarks: String(p.fullMarks), passMarks: String(p.passMarks),
    });
    setPaperModal(true);
  }
  async function submitPaper(ev: React.FormEvent) {
    ev.preventDefault();
    if (!selectedExam) return;
    setSaving(true);
    try {
      await api(editingPaper ? `/api/portal/campus/exam-schedules/${editingPaper.id}` : `/api/portal/campus/exams/${selectedExam.id}/schedules`, {
        method: editingPaper ? "PATCH" : "POST",
        body: JSON.stringify(paperForm),
      });
      setPaperModal(false);
      setMessage({ type: "success", text: L.paperSaved });
      papersData.reload();
      examsData.reload();
    } catch (err) {
      setMessage({ type: "error", text: errorText(err, L.paperSaved) });
    } finally {
      setSaving(false);
    }
  }
  async function deletePaper(p: Paper) {
    if (!(await emsConfirm({ message: L.confirmDeletePaper, danger: true }))) return;
    try {
      await api(`/api/portal/campus/exam-schedules/${p.id}`, { method: "DELETE" });
      setMessage({ type: "success", text: L.paperDeleted });
      papersData.reload();
      examsData.reload();
    } catch (err) {
      setMessage({ type: "error", text: errorText(err, L.paperDeleted) });
    }
  }

  function printRoutine() {
    if (!selectedExam) return;
    const inst = me.data?.user?.institution ?? null;
    const primary = REPORT_PRESETS[0].primary;
    const accent = REPORT_PRESETS[0].accent;
    const html = buildCampusRoutineHtml({
      inst: inst ? { name: inst.name, logo: inst.logo ?? null } : null,
      locale,
      examName: selectedExam.name,
      programName: nm(selectedExam.program.name, (selectedExam.program as { nameBn?: string | null }).nameBn),
      termName: selectedExam.term ? nm(selectedExam.term.name, selectedExam.term.nameBn) : null,
      sessionYear: selectedExam.sessionYear,
      startDate: selectedExam.startDate ? String(selectedExam.startDate).slice(0, 10) : null,
      endDate: selectedExam.endDate ? String(selectedExam.endDate).slice(0, 10) : null,
      papers: papers.map((p) => ({
        courseCode: p.course.code, courseName: p.course.name, courseNameBn: p.course.nameBn,
        credits: p.course.credits, examDate: p.examDate ? String(p.examDate).slice(0, 10) : null,
        startTime: p.startTime, endTime: p.endTime, room: p.room, fullMarks: p.fullMarks, passMarks: p.passMarks,
      })),
      labels: {
        routineTitle: L.printRoutine,
        program: L.fieldProgram, term: L.fieldTerm, session: L.fieldSession,
        examDates: L.thDates, papers: L.thPapers, course: L.thCourse, credits: L.thCredits,
        date: L.thDate, time: L.thTime, room: L.thRoom, marks: L.thFull, passMarks: L.thPass,
      },
      primary, accent,
      poweredBy: t.landing.poweredBy,
      scanLabel: t.portal.resultsPage.scanToVerify,
    });
    const w = window.open("", "_blank", "width=1020,height=760");
    if (!w) return;
    w.document.write(html);
    w.document.close();
    w.focus();
    setTimeout(() => w.print(), 350);
  }

  /* ------------------------------- marks -------------------------------- */

  const marksSchedules = marksData.data?.schedules ?? [];
  const marksStudents = marksData.data?.students ?? [];
  const marksLocked = marksData.data?.exam.resultPublished ?? false;

  // rows for the current mode
  interface MarkRow {
    schedule: MarksSchedule;
    student: MarksStudent;
    value: { marks: string; absent: boolean };
  }
  const coursePapers = useMemo(
    () => (markMode === "course" ? marksSchedules.filter((s) => String(s.id) === markCourseId) : marksSchedules),
    [markMode, marksSchedules, markCourseId]
  );
  const activeStudent = marksStudents.find((s) => s.id === Number(markStudentId)) ?? null;
  const rows: MarkRow[] = useMemo(() => {
    const out: MarkRow[] = [];
    for (const sc of coursePapers) {
      const eligible =
        markMode === "course"
          ? marksStudents.filter((st) => st.courseIds.includes(sc.course.id))
          : activeStudent
            ? (activeStudent.courseIds.includes(sc.course.id) ? [activeStudent] : [])
            : [];
      for (const st of eligible) {
        const key = `${sc.id}-${st.id}`;
        const existing = sc.marks.find((m) => m.studentId === st.id);
        out.push({
          schedule: sc,
          student: st,
          value: markCells[key] ?? {
            marks: existing && !existing.absent && existing.marks !== null ? String(existing.marks) : "",
            absent: existing?.absent ?? false,
          },
        });
      }
    }
    return out;
  }, [coursePapers, marksStudents, markCells, markMode, activeStudent]);

  // load saved marks into the editable cells whenever the grid changes
  useEffect(() => {
    setMarkCells({});
  }, [markMode, markCourseId, markStudentId, marksData.data]);

  async function saveMarks() {
    if (!selectedExamId) return;
    const payload = rows
      .map((r) => ({
        scheduleId: r.schedule.id,
        studentId: r.student.id,
        marks: r.value.absent ? undefined : r.value.marks,
        absent: r.value.absent,
      }))
      .filter((r) => r.absent || r.marks !== "");
    if (payload.length === 0) return;
    setSaving(true);
    try {
      await api(`/api/portal/campus/exams/${selectedExamId}/marks`, {
        method: "POST",
        body: JSON.stringify({ rows: payload }),
      });
      setMessage({ type: "success", text: L.marksSaved });
      marksData.reload();
    } catch (err) {
      setMessage({ type: "error", text: errorText(err, L.marksSaved) });
    } finally {
      setSaving(false);
    }
  }

  const tabs: { id: Tab; label: string }[] = [
    { id: "exams", label: L.tabExams },
    { id: "schedule", label: L.tabSchedule },
    { id: "marks", label: L.tabMarks },
  ];

  return (
    <div>
      <PageHeader
        title={L.title}
        subtitle={L.subtitle}
        action={tab === "exams" ? (
          <Button onClick={openExamCreate}>
            <IconPlus className="svg-16" />
            <span className="ms-1">{L.newExam}</span>
          </Button>
        ) : undefined}
      />

      {message && (
        <div className="mb-4">
          <Alert type={message.type} onClose={() => setMessage(null)}>{message.text}</Alert>
        </div>
      )}

      <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-4">
                <Select label={L.thProgram} wrapperClassName="form-group mb-0" value={filterProgram} onChange={(e) => setFilterProgram(e.target.value)}>
                  <option value="">{t.portal.campusAcademicPage.allPrograms}</option>
                  {programs.map((p) => <option key={p.id} value={p.id}>{nm(p.name, p.nameBn)}</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>
          ) : filteredExams.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconFile />} title={L.noExams} /></div>
          ) : (
            <div className="table-responsive">
              <table className="table table-hover align-middle mb-0">
                <thead className="ems-table-head">
                  <tr>
                    <th>{L.thExam}</th>
                    <th>{L.thProgram}</th>
                    <th>{L.thTerm}</th>
                    <th>{L.thType}</th>
                    <th>{L.thSession}</th>
                    <th>{L.thDates}</th>
                    <th className="text-center">{L.thPapers}</th>
                    <th>{L.thStatus}</th>
                    <th className="text-end">{t.common.actions}</th>
                  </tr>
                </thead>
                <tbody>
                  {filteredExams.map((e) => (
                    <tr key={e.id} style={selectedExamId === String(e.id) ? { background: mix("#24695c", "#ffffff", 0.92) } : undefined}>
                      <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>}
                        <p className="mb-0 ems-fs-11">
                          {e.schedulePublished && <span className="badge badge-success me-1">{L.routinePublished}</span>}
                          {e.resultPublished && <span className="badge badge-info">{L.resultPublishedMsg}</span>}
                        </p>
                      </td>
                      <td className="ems-fs-13">{nm(e.program.name, (e.program as { nameBn?: string | null }).nameBn)}</td>
                      <td className="ems-fs-13">{e.term ? nm(e.term.name, e.term.nameBn) : "—"}</td>
                      <td><span className="badge badge-secondary">{L[`ty_${e.examType}` as keyof typeof L] ?? e.examType}</span></td>
                      <td className="ems-fs-13">{e.sessionYear ?? "—"}</td>
                      <td className="ems-fs-12 text-muted">
                        {e.startDate ? 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] ?? e.status} /></td>
                      <td className="text-end">
                        <button className="btn btn-sm btn-light me-1" onClick={() => { setSelectedExamId(String(e.id)); setTab("schedule"); }} title={L.tabSchedule}><IconFile className="svg-16" /></button>
                        <button className="btn btn-sm btn-light me-1" onClick={() => openExamEdit(e)} title={t.common.edit}><IconEdit className="svg-16" /></button>
                        <button className="btn btn-sm btn-light text-danger" onClick={() => deleteExam(e)} title={t.common.delete}><IconTrash className="svg-16" /></button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}

      {/* ------------------------- Schedule tab ------------------------- */}
      {tab === "schedule" && (
        <div className="card">
          <div className="card-header">
            <div className="row g-2 align-items-end">
              <div className="col-md-5">
                <Select label={L.pickExam} wrapperClassName="form-group mb-0" value={selectedExamId}
                  onChange={(e) => setSelectedExamId(e.target.value)}>
                  <option value="">—</option>
                  {exams.map((e) => <option key={e.id} value={e.id}>{e.name} · {nm(e.program.name, (e.program as { nameBn?: string | null }).nameBn)}</option>)}
                </Select>
              </div>
              {selectedExam && (
                <div className="col-md-7 d-flex justify-content-md-end gap-2 flex-wrap">
                  <Button variant="secondary" onClick={openPaperCreate} disabled={selectedExam.schedulePublished}>
                    <IconPlus className="svg-16" /><span className="ms-1">{L.addPaper}</span>
                  </Button>
                  <Button variant="secondary" onClick={printRoutine} disabled={papers.length === 0}>{L.printRoutine}</Button>
                  <Button variant={selectedExam.schedulePublished ? "secondary" : "primary"} onClick={() => setSchedulePublished(!selectedExam.schedulePublished)}>
                    {selectedExam.schedulePublished ? <><IconCheck className="svg-16" /><span className="ms-1">{L.allowEdit}</span></> : <span>{L.publishRoutine}</span>}
                  </Button>
                </div>
              )}
            </div>
          </div>
          {!selectedExam ? (
            <div className="card-body"><EmptyState title={L.noExamSelected} /></div>
          ) : papersData.loading && !papersData.data ? (
            <div className="card-body text-center py-4"><span className="spinner-border text-primary" role="status" /></div>
          ) : papers.length === 0 ? (
            <div className="card-body"><EmptyState title={L.noPapers} /></div>
          ) : (
            <div className="table-responsive">
              <table className="table table-hover align-middle mb-0">
                <thead className="ems-table-head">
                  <tr>
                    <th>{L.thCourse}</th>
                    <th className="text-center">{L.thCredits}</th>
                    <th>{L.thDate}</th>
                    <th>{L.thTime}</th>
                    <th>{L.thRoom}</th>
                    <th className="text-center">{L.thFull} / {L.thPass}</th>
                    <th className="text-center">{L.thEntered}</th>
                    <th className="text-end">{t.common.actions}</th>
                  </tr>
                </thead>
                <tbody>
                  {papers.map((p) => (
                    <tr key={p.id}>
                      <td className="f-w-700">{p.course.code} — {nm(p.course.name, p.course.nameBn)}</td>
                      <td className="text-center">{p.course.credits}</td>
                      <td className="ems-fs-12">{p.examDate ? formatDate(p.examDate, locale) : "—"}</td>
                      <td className="ems-fs-12">
                        {p.startTime && p.endTime ? `${formatTime(p.startTime, locale)} → ${formatTime(p.endTime, locale)}` : "—"}
                      </td>
                      <td>{p.room ?? "—"}</td>
                      <td className="text-center">{p.fullMarks} / {p.passMarks}</td>
                      <td className="text-center f-w-700">{p._count?.marks ?? 0}</td>
                      <td className="text-end">
                        <button className="btn btn-sm btn-light me-1" onClick={() => openPaperEdit(p)} disabled={selectedExam.schedulePublished} title={t.common.edit}><IconEdit className="svg-16" /></button>
                        <button className="btn btn-sm btn-light text-danger" onClick={() => deletePaper(p)} disabled={selectedExam.schedulePublished} title={t.common.delete}><IconTrash className="svg-16" /></button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}

      {/* -------------------------- Marks tab -------------------------- */}
      {tab === "marks" && (
        <div className="card">
          <div className="card-header">
            <div className="row g-2 align-items-end">
              <div className="col-md-4">
                <Select label={L.pickExam} wrapperClassName="form-group mb-0" value={selectedExamId} onChange={(e) => setSelectedExamId(e.target.value)}>
                  <option value="">—</option>
                  {exams.map((e) => <option key={e.id} value={e.id}>{e.name} · {nm(e.program.name, (e.program as { nameBn?: string | null }).nameBn)}</option>)}
                </Select>
              </div>
              {selectedExamId && (
                <>
                  <div className="col-md-4">
                    <Select label={L.thType} wrapperClassName="form-group mb-0" value={markMode} onChange={(e) => { setMarkMode(e.target.value as MarkMode); setMarkCourseId(""); setMarkStudentId(""); }}>
                      <option value="course">{L.modeCourse}</option>
                      <option value="student">{L.modeStudent}</option>
                    </Select>
                  </div>
                  {markMode === "course" ? (
                    <div className="col-md-4">
                      <Select label={L.pickCourse} wrapperClassName="form-group mb-0" value={markCourseId} onChange={(e) => setMarkCourseId(e.target.value)}>
                        <option value="">—</option>
                        {marksSchedules.map((s) => <option key={s.id} value={s.id}>{s.course.code} — {nm(s.course.name, s.course.nameBn)}</option>)}
                      </Select>
                    </div>
                  ) : (
                    <div className="col-md-4">
                      <Select label={L.pickStudent} wrapperClassName="form-group mb-0" value={markStudentId} onChange={(e) => setMarkStudentId(e.target.value)}>
                        <option value="">—</option>
                        {marksStudents.map((s) => <option key={s.id} value={s.id}>{s.studentIdNo} — {nm(s.name, s.nameBn)}</option>)}
                      </Select>
                    </div>
                  )}
                </>
              )}
            </div>
          </div>
          {!selectedExamId ? (
            <div className="card-body"><EmptyState title={L.noExamSelected} /></div>
          ) : marksData.loading && !marksData.data ? (
            <div className="card-body text-center py-4"><span className="spinner-border text-primary" role="status" /></div>
          ) : marksLocked ? (
            <div className="card-body"><Alert type="info">{L.marksLocked}</Alert></div>
          ) : (markMode === "course" && !markCourseId) || (markMode === "student" && !markStudentId) ? (
            <div className="card-body text-muted">
              {markMode === "course" ? L.pickCourse : L.pickStudent}
            </div>
          ) : rows.length === 0 ? (
            <div className="card-body"><EmptyState title={L.noEligible} /></div>
          ) : (
            <>
              <div className="table-responsive">
                <table className="table table-hover align-middle mb-0">
                  <thead className="ems-table-head">
                    <tr>
                      {markMode === "course" ? <><th>{L.thStudent}</th><th>{L.thCourse}</th></> : <><th>{L.thCourse}</th><th className="text-center">{L.thFull} / {L.thPass}</th></>}
                      <th style={{ width: 140 }}>{L.thObtained}</th>
                      <th className="text-center">{L.thAbsent}</th>
                    </tr>
                  </thead>
                  <tbody>
                    {rows.map((r) => {
                      const key = `${r.schedule.id}-${r.student.id}`;
                      const cell = markCells[key] ?? r.value;
                      const setCell = (next: { marks: string; absent: boolean }) => setMarkCells({ ...markCells, [key]: next });
                      return (
                        <tr key={key}>
                          {markMode === "course" ? (
                            <>
                              <td className="f-w-700">{r.student.studentIdNo} — {nm(r.student.name, r.student.nameBn)}</td>
                              <td className="ems-fs-12">{r.schedule.course.code} ({r.schedule.fullMarks} / {r.schedule.passMarks})</td>
                            </>
                          ) : (
                            <>
                              <td className="f-w-700">{r.schedule.course.code} — {nm(r.schedule.course.name, r.schedule.course.nameBn)}</td>
                              <td className="text-center">{r.schedule.fullMarks} / {r.schedule.passMarks}</td>
                            </>
                          )}
                          <td>
                            <input
                              className="form-control form-control-sm" type="number" min={0} max={r.schedule.fullMarks}
                              step="0.01"
                              disabled={cell.absent}
                              value={cell.marks}
                              onChange={(e) => setCell({ ...cell, marks: e.target.value })}
                            />
                          </td>
                          <td className="text-center">
                            <input
                              className="form-check-input" type="checkbox"
                              checked={cell.absent}
                              onChange={(e) => setCell({ ...cell, absent: e.target.checked, marks: e.target.checked ? "" : cell.marks })}
                            />
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
              <div className="card-footer d-flex justify-content-end">
                <Button onClick={saveMarks} disabled={saving}>{L.saveMarks}</Button>
              </div>
            </>
          )}
        </div>
      )}

      {/* ------------------------- exam modal ------------------------- */}
      <Modal open={examModal} onClose={() => setExamModal(false)} title={editingExam ? L.editExam : L.newExam} wide>
        <form className="theme-form" onSubmit={submitExam}>
          <div className="row">
            <div className="col-md-6"><Input label={L.fieldName} required value={examForm.name} onChange={(e) => setExamForm({ ...examForm, name: e.target.value })} /></div>
            <div className="col-md-6"><Input label={L.fieldNameBn} value={examForm.nameBn} onChange={(e) => setExamForm({ ...examForm, nameBn: e.target.value })} /></div>
            <div className="col-md-4">
              <Select label={L.fieldProgram} required value={examForm.programId} onChange={(e) => setExamForm({ ...examForm, programId: e.target.value, termId: "" })}>
                <option value="">—</option>
                {programs.map((p) => <option key={p.id} value={p.id}>{nm(p.name, p.nameBn)}</option>)}
              </Select>
            </div>
            <div className="col-md-4">
              <Select label={L.fieldTerm} value={examForm.termId} onChange={(e) => setExamForm({ ...examForm, termId: e.target.value })}>
                <option value="">—</option>
                {terms.filter((x) => String(x.programId) === examForm.programId).map((x) => (
                  <option key={x.id} value={x.id}>{nm(x.name, x.nameBn)}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-4">
              <Select label={L.fieldExamType} value={examForm.examType} onChange={(e) => setExamForm({ ...examForm, examType: e.target.value })}>
                {EXAM_TYPES.map((x) => <option key={x} value={x}>{L[`ty_${x}` as keyof typeof L]}</option>)}
              </Select>
            </div>
            <div className="col-md-4"><Input label={L.fieldSession} placeholder={t.portal.campusStudentsPage.sessionPlaceholder} value={examForm.sessionYear} onChange={(e) => setExamForm({ ...examForm, sessionYear: e.target.value })} /></div>
            <div className="col-md-4"><DatePicker label={L.fieldStartDate} value={examForm.startDate} onChange={(e) => setExamForm({ ...examForm, startDate: e.target.value })} /></div>
            <div className="col-md-4"><DatePicker label={L.fieldEndDate} value={examForm.endDate} onChange={(e) => setExamForm({ ...examForm, endDate: e.target.value })} /></div>
            {!editingExam && (
              <div className="col-md-4">
                <Select label={L.thStatus} 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] ?? s}</option>)}
                </Select>
              </div>
            )}
            <div className="col-12"><Textarea label={L.fieldNote} rows={2} value={examForm.note} onChange={(e) => setExamForm({ ...examForm, note: e.target.value })} /></div>
          </div>
          <div className="d-flex justify-content-end gap-2 mt-3">
            <Button type="button" variant="secondary" onClick={() => setExamModal(false)}>{t.common.cancel}</Button>
            <Button type="submit" disabled={saving}>{t.common.save}</Button>
          </div>
        </form>
      </Modal>

      {/* ------------------------- paper modal ------------------------- */}
      <Modal open={paperModal} onClose={() => setPaperModal(false)} title={editingPaper ? L.editPaper : L.addPaper} wide>
        <form className="theme-form" onSubmit={submitPaper}>
          <div className="row">
            <div className="col-md-6">
              <Select label={L.fieldCourse} required disabled={!!editingPaper} value={paperForm.courseId} onChange={(e) => setPaperForm({ ...paperForm, courseId: e.target.value })}>
                <option value="">—</option>
                {(selectedExam ? courses.filter((c) => c.programId === selectedExam.programId) : courses).map((c) => (
                  <option key={c.id} value={c.id}>{c.code} — {nm(c.name, c.nameBn)}</option>
                ))}
              </Select>
            </div>
            <div className="col-md-3"><Input label={L.fieldFull} type="number" min={1} placeholder="100" value={paperForm.fullMarks} onChange={(e) => setPaperForm({ ...paperForm, fullMarks: e.target.value })} /></div>
            <div className="col-md-3"><Input label={L.fieldPass} type="number" min={0} placeholder="40" value={paperForm.passMarks} onChange={(e) => setPaperForm({ ...paperForm, passMarks: e.target.value })} /></div>
            <div className="col-md-3"><DatePicker label={L.fieldDate} value={paperForm.examDate} onChange={(e) => setPaperForm({ ...paperForm, examDate: e.target.value })} /></div>
            <div className="col-md-3"><TimePicker label={L.fieldStart} value={paperForm.startTime} onChange={(e) => setPaperForm({ ...paperForm, startTime: e.target.value })} /></div>
            <div className="col-md-3"><TimePicker label={L.fieldEnd} value={paperForm.endTime} onChange={(e) => setPaperForm({ ...paperForm, endTime: e.target.value })} /></div>
            <div className="col-md-3"><Input label={L.fieldRoom} value={paperForm.room} onChange={(e) => setPaperForm({ ...paperForm, room: e.target.value })} /></div>
          </div>
          <div className="d-flex justify-content-end gap-2 mt-3">
            <Button type="button" variant="secondary" onClick={() => setPaperModal(false)}>{t.common.cancel}</Button>
            <Button type="submit" disabled={saving}>{t.common.save}</Button>
          </div>
        </form>
      </Modal>
    </div>
  );
}
