"use client";

import { useMemo, useState } from "react";
import { useLang } from "@/context/LanguageContext";
import {
  Alert,
  Button,
  EmptyState,
  Input,
  Modal,
  PageHeader,
  PageLoader,
  Pagination,
  Select,
  StatusBadge,
  usePagination,
} from "@/components/ui";
import { api, useApi } from "@/lib/client";
import Papa from "papaparse";
import { formatDate, formatBDT } from "@/lib/utils";
import { PAYMENT_METHODS } from "@/lib/constants";
import {
  BULK_ADMISSION_COLUMNS,
  buildBulkAdmissionTemplateCsv,
  isValidBulkAdmissionHeaders,
  normalizeBulkHeader,
} from "@/lib/bulk-admission-template";
import { IconCheck, IconClipboard, IconDownload, IconTrash, IconUpload, IconWallet, IconX } from "@/components/icons";
import { emsConfirm } from "@/components/ConfirmDialog";
import {
  AdmissionFormWizard,
  emptyAdmissionForm,
  type AdmissionFormValues,
} from "@/components/AdmissionFormWizard";
import type { ReactNode } from "react";

interface Admission {
  id: number;
  studentName: string;
  studentNameBn: string | null;
  applicationType: string;
  gender: string;
  dob: string | null;
  guardianName: string;
  guardianNameBn: string | null;
  guardianPhone: string;
  motherName: string | null;
  motherNameBn: string | null;
  email: string | null;
  address: string | null;
  note: string | null;
  feeAmount: number;
  feeStatus: string;
  feeMethod: string | null;
  feeDetails: Record<string, string> | null;
  feePaidAt: string | null;
  status: string;
  createdAt: string;
  decidedAt: string | null;
  classItemId: number | null;
  sessionItemId: number | null;
  classItem: { name: string; nameBn: string } | null;
  sessionItem: { name: string; nameBn: string } | null;
  student: { id: number; roll: string } | null;
}

interface Item {
  id: number;
  category: string;
  name: string;
  nameBn: string;
}

const emptyForm: AdmissionFormValues = emptyAdmissionForm;

/** compact "TrxID / bank / ref" summary for a payment details JSON */
function feeDetailsSummary(details: Record<string, string> | null | undefined): string {
  if (!details) return "";
  return [
    details.txnId && `TrxID: ${details.txnId}`,
    details.bankName,
    details.branch,
    details.refNo && `Ref: ${details.refNo}`,
    details.cardType,
    details.last4 && `•••• ${details.last4}`,
    details.account,
  ]
    .filter(Boolean)
    .join(" · ");
}

export default function PortalAdmissionsPage() {
  const { t, locale } = useLang();
  const { data: admissionsData, loading, reload } = useApi<{ admissions: Admission[] }>("/api/portal/admissions");
  const { data: academic } = useApi<{ items: Item[]; selectedIds: number[] }>("/api/portal/academic");

  const [modalOpen, setModalOpen] = useState(false);
  const [applicationType, setApplicationType] = useState<"RAPID" | "DETAILS">("RAPID");
  const [form, setForm] = useState(emptyForm);
  // detailed applications are entered through the shared step wizard
  const [wizardStep, setWizardStep] = useState(0);

  // bulk CSV upload
  const [bulkOpen, setBulkOpen] = useState(false);
  const [bulkRows, setBulkRows] = useState<Record<string, string>[]>([]);
  // the file's header row — sent along for the server to re-check the arrangement
  const [bulkHeaders, setBulkHeaders] = useState<string[]>([]);
  const [bulkHeaderError, setBulkHeaderError] = useState(false);
  const [bulkSaving, setBulkSaving] = useState(false);
  const [bulkResult, setBulkResult] = useState<{
    created: number;
    failed: number;
    results: { row: number; studentName: string; ok: boolean; reference?: string; error?: string }[];
  } | null>(null);
  const bulkValidRows = bulkRows.filter((r) => (r.studentName ?? "").trim() && (r.guardianPhone ?? "").trim());
  const [saving, setSaving] = useState(false);
  const [busyId, setBusyId] = useState<number | null>(null);
  const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
  // approve step: optional custom roll (blank → server auto-assigns the next one)
  const [approveAdm, setApproveAdm] = useState<Admission | null>(null);
  const [approveRoll, setApproveRoll] = useState("");
  const [approving, setApproving] = useState(false);

  // application fee: modal state (opened for any application that carries a fee)
  const [feeAdm, setFeeAdm] = useState<Admission | null>(null);
  const [feeMethod, setFeeMethod] = useState("CASH");
  const [feeDetails, setFeeDetails] = useState<Record<string, string>>({});
  const [feeSaving, setFeeSaving] = useState(false);
  const MOBILE_METHODS = ["BKASH", "NAGAD", "ROCKET"];
  function feeRequiredFields(): string[] {
    if (MOBILE_METHODS.includes(feeMethod)) return ["txnId"];
    if (feeMethod === "BANK_TRANSFER") return ["bankName", "refNo"];
    if (feeMethod === "CARD") return ["refNo"];
    return [];
  }
  const feeMissing = feeRequiredFields().filter((k) => !(feeDetails[k] ?? "").trim());
  function setFeeDetail(key: string, value: string) {
    setFeeDetails((d) => ({ ...d, [key]: value }));
  }

  function openFee(adm: Admission) {
    setFeeAdm(adm);
    setFeeMethod("CASH");
    setFeeDetails({});
  }

  async function decideFee(adm: Admission, feeAction: "mark-paid" | "waive" | "reopen") {
    if (feeAction === "waive" && !(await emsConfirm({ message: t.portal.admissionsPage.feeWaiveConfirm }))) return;
    if (feeAction === "reopen" && !(await emsConfirm({ message: t.portal.admissionsPage.feeReopenConfirm }))) return;
    setFeeSaving(true);
    try {
      await api(`/api/portal/admissions/${adm.id}`, {
        method: "PATCH",
        body: JSON.stringify({
          action: "fee",
          feeAction,
          method: feeAction === "mark-paid" ? feeMethod : undefined,
          details: feeAction === "mark-paid" ? feeDetails : undefined,
        }),
      });
      setFeeAdm(null);
      setMessage({
        type: "success",
        text:
          feeAction === "mark-paid"
            ? t.portal.admissionsPage.feeRecorded
            : feeAction === "waive"
              ? t.portal.admissionsPage.feeWaivedDone
              : t.portal.admissionsPage.feeReopened,
      });
      reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    } finally {
      setFeeSaving(false);
    }
  }

  // only the classes/sessions this institution actually operates (academic selections)
  const selectedIds = new Set(academic?.selectedIds ?? []);
  const classes = (academic?.items ?? []).filter(
    (i) => i.category === "CLASS" && selectedIds.has(i.id)
  );
  const sessions = (academic?.items ?? []).filter(
    (i) => i.category === "SESSION" && selectedIds.has(i.id)
  );
  // ---- application list: search + filters + sorting ----
  type SortKey = "createdAt" | "studentName" | "className" | "guardianName" | "feeAmount" | "status";
  const [search, setSearch] = useState("");
  const [statusFilter, setStatusFilter] = useState("");
  const [classFilter, setClassFilter] = useState("");
  const [sessionFilter, setSessionFilter] = useState("");
  const [sortKey, setSortKey] = useState<SortKey>("createdAt");
  const [sortDir, setSortDir] = useState<"asc" | "desc">("desc");

  function toggleSort(k: SortKey) {
    if (sortKey === k) {
      setSortDir((d) => (d === "asc" ? "desc" : "asc"));
    } else {
      setSortKey(k);
      // dates and amounts read naturally newest/highest first
      setSortDir(k === "createdAt" || k === "feeAmount" ? "desc" : "asc");
    }
  }

  const admissions = useMemo(() => {
    let list = admissionsData?.admissions ?? [];
    if (statusFilter) list = list.filter((a) => a.status === statusFilter);
    if (classFilter) list = list.filter((a) => a.classItemId === Number(classFilter));
    if (sessionFilter) list = list.filter((a) => a.sessionItemId === Number(sessionFilter));
    const q = search.trim().toLowerCase();
    if (q) {
      list = list.filter(
        (a) =>
          a.studentName.toLowerCase().includes(q) ||
          (a.studentNameBn ?? "").toLowerCase().includes(q) ||
          a.guardianName.toLowerCase().includes(q) ||
          a.guardianPhone.toLowerCase().includes(q) ||
          (a.email ?? "").toLowerCase().includes(q)
      );
    }
    const dir = sortDir === "asc" ? 1 : -1;
    const val = (a: Admission): string | number => {
      switch (sortKey) {
        case "studentName":
          return a.studentName.toLowerCase();
        case "className":
          return a.classItem?.name?.toLowerCase() ?? "";
        case "guardianName":
          return a.guardianName.toLowerCase();
        case "feeAmount":
          return a.feeAmount;
        case "status":
          return a.status;
        default:
          return new Date(a.createdAt).getTime();
      }
    };
    return [...list].sort((x, y) => {
      const vx = val(x);
      const vy = val(y);
      if (vx < vy) return -dir;
      if (vx > vy) return dir;
      return 0;
    });
  }, [admissionsData, search, statusFilter, classFilter, sessionFilter, sortKey, sortDir]);
  const admissionsPager = usePagination(admissions, 10);

  /** sortable column header with a direction marker */
  function sortTh(label: string, k: SortKey) {
    const active = sortKey === k;
    return (
      <th
        role="button"
        className="user-select-none"
        style={{ cursor: "pointer", whiteSpace: "nowrap" }}
        onClick={() => toggleSort(k)}
      >
        {label}
        <span className={`ms-1 ems-fs-10 ${active ? "f-w-800" : "text-muted"}`}>
          {active ? (sortDir === "asc" ? "▲" : "▼") : "⇅"}
        </span>
      </th>
    );
  }

  function parseBulkFile(file: File | undefined) {
    if (!file) return;
    setBulkResult(null);
    Papa.parse<Record<string, string>>(file, {
      header: true,
      skipEmptyLines: true,
      transformHeader: normalizeBulkHeader,
      complete: (res) => {
        const headers = res.meta.fields ?? [];
        // header arrangement must match the template exactly, or nothing is submitted
        if (!isValidBulkAdmissionHeaders(headers)) {
          setBulkHeaderError(true);
          setBulkHeaders([]);
          setBulkRows([]);
          return;
        }
        setBulkHeaderError(false);
        setBulkHeaders(headers);
        setBulkRows(res.data as Record<string, string>[]);
      },
      error: () => setMessage({ type: "error", text: t.portal.admissionsPage.bulkParseError }),
    });
  }

  function downloadBulkTemplate() {
    const blob = new Blob(["\uFEFF" + buildBulkAdmissionTemplateCsv()], {
      type: "text/csv;charset=utf-8;",
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "admission-applications-template.csv";
    a.click();
    URL.revokeObjectURL(url);
  }

  async function submitBulk() {
    if (!bulkValidRows.length || bulkSaving) return;
    setBulkSaving(true);
    setBulkResult(null);
    try {
      const res = await api<{
        created: number;
        failed: number;
        results: { row: number; studentName: string; ok: boolean; reference?: string; error?: string }[];
      }>("/api/portal/admissions/bulk", {
        method: "POST",
        body: JSON.stringify({ rows: bulkValidRows, headers: bulkHeaders }),
      });
      setBulkResult(res);
      reload();
    } catch (e) {
      const msg = e instanceof Error ? e.message : "";
      setMessage({
        type: "error",
        text:
          msg === "TOO_MANY_ROWS"
            ? t.portal.admissionsPage.bulkTooMany
            : msg === "HEADER_MISMATCH"
              ? t.portal.admissionsPage.bulkHeaderMismatch
              : t.portal.admissionsPage.bulkFailed,
      });
    } finally {
      setBulkSaving(false);
    }
  }

  async function handleSubmit() {
    setSaving(true);
    setMessage(null);
    try {
      await api("/api/portal/admissions", {
        method: "POST",
        body: JSON.stringify({
          ...form,
          applicationType,
          classItemId: form.classItemId ? Number(form.classItemId) : null,
          sessionItemId: form.sessionItemId ? Number(form.sessionItemId) : null,
          dob: form.dob || null,
        }),
      });
      setMessage({ type: "success", text: t.portal.admissionsPage.applicationCreated });
      setModalOpen(false);
      setForm(emptyForm);
      setWizardStep(0);
      reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    } finally {
      setSaving(false);
    }
  }

  async function decide(adm: Admission, action: "approve" | "reject") {
    if (action === "approve") {
      // roll is editable at approval (blank = auto-assign the next number)
      setApproveRoll("");
      setApproveAdm(adm);
      return;
    }
    if (!(await emsConfirm({ message: t.portal.admissionsPage.rejectConfirm, danger: true }))) return;
    setBusyId(adm.id);
    try {
      await api(`/api/portal/admissions/${adm.id}`, {
        method: "PATCH",
        body: JSON.stringify({ action }),
      });
      setMessage({ type: "success", text: t.portal.admissionsPage.rejected });
      reload();
    } catch {
      setMessage({ type: "error", text: t.common.tryAgain });
    } finally {
      setBusyId(null);
    }
  }

  /** Delete an application (pending / rejected / approved-without-student). */
  async function remove(adm: Admission) {
    if (!(await emsConfirm({ message: t.portal.admissionsPage.deleteConfirm, danger: true }))) return;
    setBusyId(adm.id);
    try {
      await api(`/api/portal/admissions/${adm.id}`, { method: "DELETE" });
      setMessage({ type: "success", text: t.portal.admissionsPage.deletedMsg });
      reload();
    } catch (e) {
      setMessage({
        type: "error",
        text:
          e instanceof Error && e.message === "LINKED_STUDENT"
            ? t.portal.admissionsPage.deleteBlocked
            : t.common.tryAgain,
      });
    } finally {
      setBusyId(null);
    }
  }

  async function confirmApprove() {
    if (!approveAdm) return;
    setApproving(true);
    try {
      const res = await api<{ student?: { roll: string } }>(`/api/portal/admissions/${approveAdm.id}`, {
        method: "PATCH",
        body: JSON.stringify({ action: "approve", roll: approveRoll.trim() || undefined }),
      });
      setMessage({
        type: "success",
        text: `${t.portal.admissionsPage.approved} ${res.student?.roll ?? ""}`,
      });
      setApproveAdm(null);
      reload();
    } catch (e) {
      setMessage({
        type: "error",
        text: e instanceof Error && e.message === "ROLL_EXISTS"
          ? t.portal.identityPage.rollExists
          : t.common.tryAgain,
      });
      if (e instanceof Error && e.message === "ROLL_EXISTS") setApproveAdm(null);
    } finally {
      setApproving(false);
    }
  }

  return (
    <div>
      <PageHeader
        title={t.portal.admissionsPage.title}
        subtitle={t.portal.admissionsPage.subtitle}
        action={
          <div className="d-flex gap-2 flex-wrap">
            <Button variant="secondary" onClick={() => { setBulkRows([]); setBulkResult(null); setBulkHeaderError(false); setBulkOpen(true); }}>
              <IconUpload className="svg-16" />
              <span className="ms-1">{t.portal.admissionsPage.bulkUpload}</span>
            </Button>
            <Button onClick={() => { setWizardStep(0); setModalOpen(true); }}>
              <IconClipboard className="svg-16" />
              <span className="ms-1">{t.portal.admissionsPage.newApplication}</span>
            </Button>
          </div>
        }
      />

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

      {loading ? (
        <PageLoader text={t.common.loading} />
      ) : !admissionsData || admissionsData.admissions.length === 0 ? (
        <div className="card">
          <EmptyState icon={<IconClipboard />} title={t.portal.admissionsPage.noApplications} />
        </div>
      ) : (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center gap-2">
            <div className="has-search" style={{ flexGrow: 1, maxWidth: 300, minWidth: 200 }}>
              <span className="fa fa-search form-control-feedback" />
              <input
                className="form-control"
                placeholder={t.portal.admissionsPage.searchApplications}
                value={search}
                onChange={(e) => setSearch(e.target.value)}
              />
            </div>
            <div style={{ maxWidth: 150 }}>
              <Select
                wrapperClassName="form-group mb-0"
                value={statusFilter}
                onChange={(e) => setStatusFilter(e.target.value)}
              >
                <option value="">{t.portal.admissionsPage.allStatuses}</option>
                {(["PENDING", "APPROVED", "REJECTED"] as const).map((s) => (
                  <option key={s} value={s}>
                    {t.status[s]}
                  </option>
                ))}
              </Select>
            </div>
            <div style={{ maxWidth: 150 }}>
              <Select
                wrapperClassName="form-group mb-0"
                value={classFilter}
                onChange={(e) => setClassFilter(e.target.value)}
              >
                <option value="">{t.portal.admissionsPage.allClasses}</option>
                {classes.map((c) => (
                  <option key={c.id} value={c.id}>
                    {locale === "bn" ? c.nameBn : c.name}
                  </option>
                ))}
              </Select>
            </div>
            <div style={{ maxWidth: 150 }}>
              <Select
                wrapperClassName="form-group mb-0"
                value={sessionFilter}
                onChange={(e) => setSessionFilter(e.target.value)}
              >
                <option value="">{t.portal.admissionsPage.allSessions}</option>
                {sessions.map((c) => (
                  <option key={c.id} value={c.id}>
                    {locale === "bn" ? c.nameBn : c.name}
                  </option>
                ))}
              </Select>
            </div>
          </div>
          <div className="table-responsive">
            <table className="table table-hover align-middle mb-0">
              <thead className="ems-table-head">
                <tr>
                  {sortTh(t.portal.admissionsPage.studentName, "studentName")}
                  {sortTh(t.portal.admissionsPage.appliedOn, "createdAt")}
                  {sortTh(t.portal.admissionsPage.applyClass, "className")}
                  {sortTh(t.portal.admissionsPage.guardianName, "guardianName")}
                  <th>{t.common.phone}</th>
                  {sortTh(t.portal.admissionsPage.feeCol, "feeAmount")}
                  {sortTh(t.common.status, "status")}
                  <th className="text-end">{t.common.actions}</th>
                </tr>
              </thead>
              <tbody>
                {admissions.length === 0 ? (
                  <tr>
                    <td colSpan={8} className="text-center text-muted py-4">
                      {t.portal.admissionsPage.noMatch}
                    </td>
                  </tr>
                ) : (
                admissionsPager.paged.map((adm) => (
                  <tr key={adm.id}>
                    <td>
                      <p className="mb-0 f-w-700">{adm.studentName}</p>
                      <p className="mb-0 ems-fs-11 text-muted">
                        <StatusBadge
                          status={adm.applicationType}
                          label={t.applicationTypes[adm.applicationType as keyof typeof t.applicationTypes] ?? adm.applicationType}
                        />
                      </p>
                    </td>
                    <td className="ems-fs-12 text-muted" style={{ whiteSpace: "nowrap" }}>
                      {formatDate(adm.createdAt, locale)}
                      {adm.decidedAt && (
                        <p className="mb-0 ems-fs-11">
                          {t.portal.admissionsPage.decidedOn}: {formatDate(adm.decidedAt, locale)}
                        </p>
                      )}
                    </td>
                    <td>
                      {adm.classItem ? (
                        <span className={`ems-type-pill ems-cat-CLASS`}>
                          {locale === "bn" ? adm.classItem.nameBn : adm.classItem.name}
                        </span>
                      ) : (
                        "—"
                      )}
                      {adm.sessionItem && (
                        <p className="mb-0 ems-fs-11 text-muted mt-1">
                          {t.categories.SESSION}: {locale === "bn" ? adm.sessionItem.nameBn : adm.sessionItem.name}
                        </p>
                      )}
                    </td>
                    <td>
                      <p className="mb-0 ems-fs-13 f-w-600">{adm.guardianName}</p>
                      {adm.guardianNameBn && (
                        <p className="mb-0 ems-fs-11 text-muted">{adm.guardianNameBn}</p>
                      )}
                      {adm.motherName && (
                        <p className="mb-0 ems-fs-11 text-muted">
                          {t.portal.studentsPage.motherName}: {adm.motherName}
                          {adm.motherNameBn ? ` (${adm.motherNameBn})` : ""}
                        </p>
                      )}
                      <p className="mb-0 ems-fs-11 text-muted">{adm.guardianPhone}</p>
                    </td>
                    <td className="ems-fs-12">{adm.email ?? "—"}</td>
                    <td>
                      {adm.feeAmount > 0 ? (
                        <>
                          <p className="mb-0 f-w-700">{formatBDT(adm.feeAmount, locale)}</p>
                          {adm.feeStatus === "PAID" ? (
                            <span className="badge badge-success">{t.portal.admissionsPage.feePaid}</span>
                          ) : adm.feeStatus === "WAIVED" ? (
                            <span className="badge badge-secondary">{t.portal.admissionsPage.feeWaived}</span>
                          ) : (
                            <span className="badge badge-danger">{t.portal.hrPage.dueLabel}</span>
                          )}
                          {adm.feeStatus === "PAID" && adm.feePaidAt && (
                            <p className="mb-0 ems-fs-11 text-muted">
                              {formatDate(adm.feePaidAt, locale)}
                              {adm.feeMethod
                                ? ` · ${t.methods[adm.feeMethod as keyof typeof t.methods] ?? adm.feeMethod}`
                                : ""}
                            </p>
                          )}
                        </>
                      ) : (
                        <span className="ems-fs-12 text-muted">—</span>
                      )}
                    </td>
                    <td>
                      <StatusBadge status={adm.status} label={t.status[adm.status as keyof typeof t.status]} />
                      {adm.student && (
                        <p className="mb-0 ems-fs-11 text-muted mt-1">
                          {t.portal.admissionsPage.roll}: <b>{adm.student.roll}</b>
                        </p>
                      )}
                    </td>
                    <td className="text-end">
                      <div className="d-inline-flex gap-1 align-items-center">
                        {adm.feeAmount > 0 && (
                          <button
                            className={`btn btn-sm ${adm.feeStatus === "DUE" ? "btn-soft-success" : "btn-soft-secondary"}`}
                            onClick={() => openFee(adm)}
                            type="button"
                            title={t.portal.admissionsPage.recordFee}
                          >
                            <IconWallet className="svg-16" />
                          </button>
                        )}
                        {adm.status === "PENDING" ? (
                          <>
                            <Button size="sm" variant="success" loading={busyId === adm.id} onClick={() => decide(adm, "approve")}>
                              <IconCheck className="svg-16" />
                              <span className="ms-1 d-none d-lg-inline">{t.central.requestsPage.approve}</span>
                            </Button>
                            <Button size="sm" variant="danger" loading={busyId === adm.id} onClick={() => decide(adm, "reject")}>
                              <IconX className="svg-16" />
                            </Button>
                          </>
                        ) : (
                          <span className="ems-fs-11 text-muted">—</span>
                        )}
                        <button
                          className="btn btn-sm btn-soft-danger"
                          onClick={() => remove(adm)}
                          type="button"
                          title={t.common.delete}
                        >
                          <IconTrash className="svg-16" />
                        </button>
                      </div>
                    </td>
                  </tr>
                ))
                )}
              </tbody>
            </table>
          </div>
          <Pagination {...admissionsPager} onPageChange={admissionsPager.setPage} onSizeChange={admissionsPager.setSize} />
        </div>
      )}

      {/* New application modal */}
      <Modal open={modalOpen} onClose={() => setModalOpen(false)} title={t.portal.admissionsPage.newApplication} wide>
        {/* Application type toggle: Rapid (basic) vs Detailed (step wizard) */}
        <div className="ems-channel-row d-flex gap-2 mb-3">
          {(["RAPID", "DETAILS"] as const).map((ty) => (
            <button
              key={ty}
              type="button"
              className={`ems-channel ${applicationType === ty ? "active" : ""}`}
              onClick={() => setApplicationType(ty)}
            >
              {ty === "RAPID" ? t.portal.admissionsPage.rapidType : t.portal.admissionsPage.detailsType}
            </button>
          ))}
        </div>
        {applicationType === "RAPID" ? (
          <form
            className="theme-form"
            onSubmit={(e) => {
              e.preventDefault();
              handleSubmit();
            }}
          >
            <div className="row">
              <div className="col-md-12">
                <Input
                  label={t.portal.admissionsPage.studentName}
                  required
                  value={form.studentName}
                  onChange={(e) => setForm({ ...form, studentName: e.target.value })}
                />
              </div>
              <div className="col-md-12">
                <Select
                  label={t.portal.admissionsPage.applyClass}
                  value={form.classItemId}
                  onChange={(e) => setForm({ ...form, classItemId: e.target.value })}
                >
                  <option value="">—</option>
                  {classes.map((c) => (
                    <option key={c.id} value={c.id}>
                      {locale === "bn" ? c.nameBn : c.name}
                    </option>
                  ))}
                </Select>
              </div>
              <div className="col-md-12">
                <Input
                  label={t.portal.admissionsPage.contactPhone}
                  required
                  value={form.guardianPhone}
                  onChange={(e) => setForm({ ...form, guardianPhone: e.target.value })}
                />
              </div>
            </div>
            <div className="d-flex justify-content-end gap-2 pt-2">
              <Button type="button" variant="secondary" onClick={() => setModalOpen(false)}>
                {t.common.cancel}
              </Button>
              <Button type="submit" loading={saving} disabled={!form.studentName.trim() || !form.guardianPhone.trim()}>
                {t.common.create}
              </Button>
            </div>
          </form>
        ) : (
          /* Detailed admission — the same step wizard as the public /apply page */
          <AdmissionFormWizard
            classes={classes}
            sessions={sessions}
            values={form}
            onChange={setForm}
            step={wizardStep}
            onStep={setWizardStep}
            submitLabel={t.common.create}
            submitting={saving}
            onSubmit={handleSubmit}
            onCancel={() => setModalOpen(false)}
          />
        )}
      </Modal>

      {/* Bulk CSV upload modal */}
      <Modal open={bulkOpen} onClose={() => setBulkOpen(false)} title={t.portal.admissionsPage.bulkUploadTitle} wide>
        <p className="ems-fs-12 text-muted mb-2">{t.portal.admissionsPage.bulkHint}</p>
        <div className="d-flex gap-2 flex-wrap mb-3">
          <label className="btn btn-sm btn-primary f-w-700 mb-0">
            <IconUpload className="svg-14" />
            <span className="ms-1">{t.portal.admissionsPage.bulkChooseFile}</span>
            <input
              type="file"
              accept=".csv,text/csv"
              className="d-none"
              onChange={(e) => { parseBulkFile(e.target.files?.[0]); e.target.value = ""; }}
            />
          </label>
          <button type="button" className="btn btn-sm btn-soft-secondary f-w-700" onClick={downloadBulkTemplate}>
            <IconDownload className="svg-14" />
            <span className="ms-1">{t.portal.admissionsPage.bulkDownloadTemplate}</span>
          </button>
        </div>

        {bulkHeaderError && (
          <Alert type="error" onClose={() => setBulkHeaderError(false)}>
            {t.portal.admissionsPage.bulkHeaderMismatch}
            <code className="d-block mt-1 f-w-700" style={{ fontSize: 11 }}>
              {BULK_ADMISSION_COLUMNS.join(", ")}
            </code>
          </Alert>
        )}

        {!bulkHeaderError && (
          <code className="ems-invoice-badge d-block mb-3" style={{ whiteSpace: "pre", fontSize: 11 }}>
{`studentName,guardianPhone,guardianName,studentNameBn,gender,dob,className,email,studentPhone,address,note
Karim Uddin,01712345678,Md. Rahim,,MALE,2010-05-14,Nine,karim@x.com,,Dhaka,
Fatema Akter,01812345678,,,,Eight,,,,,,,,`}
          </code>
        )}

        {bulkRows.length > 0 && !bulkResult && (
          <>
            <p className="ems-fs-12 mb-2">
              {t.portal.admissionsPage.bulkPreview}: <b>{bulkRows.length}</b> · {t.portal.admissionsPage.bulkValid}: <b>{bulkValidRows.length}</b>
            </p>
            <div className="table-responsive mb-3" style={{ maxHeight: 240, overflowY: "auto" }}>
              <table className="table table-sm table-hover align-middle mb-0">
                <thead className="ems-table-head">
                  <tr>
                    <th>#</th>
                    <th>{t.portal.admissionsPage.studentName}</th>
                    <th>{t.portal.admissionsPage.guardianPhone}</th>
                    <th>{t.portal.admissionsPage.applyClass}</th>
                    <th>{t.common.email}</th>
                    <th>{t.common.status}</th>
                  </tr>
                </thead>
                <tbody>
                  {bulkRows.slice(0, 50).map((r, i) => {
                    const ok = (r.studentName ?? "").trim() && (r.guardianPhone ?? "").trim();
                    return (
                      <tr key={i}>
                        <td>{i + 2}</td>
                        <td className="f-w-600">{(r.studentName ?? "").trim() || "—"}</td>
                        <td>{(r.guardianPhone ?? "").trim() || "—"}</td>
                        <td className="ems-fs-12">{(r.className ?? "").trim() || "—"}</td>
                        <td className="ems-fs-12">{(r.email ?? "").trim() || "—"}</td>
                        <td>
                          <span className={`badge ${ok ? "badge-success" : "badge-danger"}`}>
                            {ok ? "✓" : t.portal.admissionsPage.bulkRowInvalid}
                          </span>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
            <div className="d-flex justify-content-end gap-2">
              <Button variant="secondary" onClick={() => setBulkOpen(false)}>{t.common.cancel}</Button>
              <Button onClick={submitBulk} loading={bulkSaving} disabled={!bulkValidRows.length}>
                {t.portal.admissionsPage.bulkCreate.replace("{n}", String(bulkValidRows.length))}
              </Button>
            </div>
          </>
        )}

        {bulkResult && (
          <div>
            <div className="d-flex flex-wrap gap-2 mb-3">
              <span className="badge badge-success">{t.portal.admissionsPage.bulkCreated}: {bulkResult.created}</span>
              {bulkResult.failed > 0 && <span className="badge badge-danger">{t.portal.admissionsPage.bulkFailedRows}: {bulkResult.failed}</span>}
            </div>
            <div className="table-responsive mb-3" style={{ maxHeight: 240, overflowY: "auto" }}>
              <table className="table table-sm table-hover align-middle mb-0">
                <thead className="ems-table-head">
                  <tr>
                    <th>{t.portal.admissionsPage.bulkRow}</th>
                    <th>{t.portal.admissionsPage.studentName}</th>
                    <th>{t.common.status}</th>
                    <th>{t.portal.admissionsPage.bulkDetail}</th>
                  </tr>
                </thead>
                <tbody>
                  {bulkResult.results.map((r) => (
                    <tr key={r.row}>
                      <td>{r.row}</td>
                      <td className="f-w-600">{r.studentName}</td>
                      <td>
                        <span className={`badge ${r.ok ? "badge-success" : "badge-danger"}`}>{r.ok ? r.reference : "✗"}</span>
                      </td>
                      <td className="ems-fs-12 text-muted">
                        {r.ok ? t.portal.admissionsPage.applicationCreated : (t.portal.admissionsPage as unknown as Record<string, string>)["bulkErr" + r.error] ?? r.error}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            <div className="d-flex justify-content-end gap-2">
              <Button variant="secondary" onClick={() => { setBulkRows([]); setBulkResult(null); }}>
                {t.portal.admissionsPage.bulkAgain}
              </Button>
              <Button onClick={() => setBulkOpen(false)}>{t.common.close}</Button>
            </div>
          </div>
        )}
      </Modal>

      {/* Approve step — roll is optionally set here, blank keeps auto-assign */}
      <Modal open={!!approveAdm} onClose={() => setApproveAdm(null)} title={t.portal.admissionsPage.approveTitle}>
        {approveAdm && (
          <div>
            <p className="ems-fs-13 mb-3">
              {t.portal.admissionsPage.approveBody}: <span className="f-w-700">{approveAdm.studentName}</span>
            </p>
            {approveAdm.feeStatus === "DUE" && (
              <div className="mb-3">
                <Alert type="warning">{t.portal.admissionsPage.feeDueApproveHint}</Alert>
              </div>
            )}
            <Input
              label={t.portal.admissionsPage.roll}
              value={approveRoll}
              onChange={(e) => setApproveRoll(e.target.value)}
              placeholder={t.portal.identityPage.rollAutoHint}
            />
            <p className="ems-fs-12 text-muted">{t.portal.identityPage.rollAutoHint}</p>
            <div className="d-flex justify-content-end gap-2 pt-2">
              <Button type="button" variant="secondary" onClick={() => setApproveAdm(null)}>
                {t.common.cancel}
              </Button>
              <Button type="button" onClick={confirmApprove} loading={approving}>
                {t.central.requestsPage.approve}
              </Button>
            </div>
          </div>
        )}
      </Modal>

      {/* Application fee modal — record payment, waive, or set back to due */}
      <Modal
        open={feeAdm !== null}
        onClose={() => setFeeAdm(null)}
        title={`${t.portal.admissionsPage.feeModalTitle} — ${feeAdm?.studentName ?? ""}`}
      >
        {feeAdm && (
          <div>
            <div className="p-3 rounded mb-3 d-flex justify-content-between align-items-center" style={{ background: "rgba(36,105,92,0.07)" }}>
              <div>
                <p className="mb-0 f-w-700">{feeAdm.studentName}</p>
                <p className="mb-0 ems-fs-12 text-muted">
                  {t.portal.admissionsPage.feeCol} · {formatBDT(feeAdm.feeAmount, locale)}
                </p>
              </div>
              {feeAdm.feeStatus === "PAID" ? (
                <span className="badge badge-success">{t.portal.admissionsPage.feePaid}</span>
              ) : feeAdm.feeStatus === "WAIVED" ? (
                <span className="badge badge-secondary">{t.portal.admissionsPage.feeWaived}</span>
              ) : (
                <span className="badge badge-danger">{t.portal.hrPage.dueLabel}</span>
              )}
            </div>

            {feeAdm.feeStatus === "DUE" ? (
              <div>
                <Select
                  label={t.common.method}
                  value={feeMethod}
                  onChange={(e) => {
                    setFeeMethod(e.target.value);
                    setFeeDetails({});
                  }}
                >
                  {PAYMENT_METHODS.map((m) => (
                    <option key={m} value={m}>
                      {t.methods[m]}
                    </option>
                  ))}
                </Select>

                {/* method-specific transaction details — same fields as fee collections */}
                {feeMethod === "CASH" ? (
                  <p className="ems-fs-11 text-muted mb-3 px-1">{t.portal.feesPage.cfCashHint}</p>
                ) : (
                  <div className="p-3 mb-3 rounded" style={{ background: "rgba(36,105,92,0.05)" }}>
                    <p className="ems-fs-11 f-w-700 text-uppercase text-muted mb-2">{t.portal.feesPage.cfDetailsTitle}</p>
                    {MOBILE_METHODS.includes(feeMethod) && (
                      <div className="row g-2">
                        <div className="col-sm-6">
                          <Input
                            label={`${t.portal.feesPage.cfTxnId} *`}
                            value={feeDetails.txnId ?? ""}
                            placeholder="8N7A1B2C"
                            onChange={(e) => setFeeDetail("txnId", e.target.value)}
                          />
                        </div>
                        <div className="col-sm-6">
                          <Input
                            label={t.portal.feesPage.cfAccount}
                            value={feeDetails.account ?? ""}
                            placeholder="01XXXXXXXXX"
                            onChange={(e) => setFeeDetail("account", e.target.value)}
                          />
                        </div>
                      </div>
                    )}
                    {feeMethod === "BANK_TRANSFER" && (
                      <div className="row g-2">
                        <div className="col-sm-6">
                          <Input
                            label={`${t.portal.feesPage.cfBank} *`}
                            value={feeDetails.bankName ?? ""}
                            onChange={(e) => setFeeDetail("bankName", e.target.value)}
                          />
                        </div>
                        <div className="col-sm-6">
                          <Input
                            label={t.portal.feesPage.cfBranch}
                            value={feeDetails.branch ?? ""}
                            onChange={(e) => setFeeDetail("branch", e.target.value)}
                          />
                        </div>
                        <div className="col-sm-6">
                          <Input
                            label={`${t.portal.feesPage.cfRef} *`}
                            value={feeDetails.refNo ?? ""}
                            onChange={(e) => setFeeDetail("refNo", e.target.value)}
                          />
                        </div>
                        <div className="col-sm-6">
                          <Input
                            label={t.portal.feesPage.cfAcNo}
                            value={feeDetails.account ?? ""}
                            onChange={(e) => setFeeDetail("account", e.target.value)}
                          />
                        </div>
                      </div>
                    )}
                    {feeMethod === "CARD" && (
                      <div className="row g-2">
                        <div className="col-sm-4">
                          <Select
                            label={t.portal.feesPage.cfCardType}
                            value={feeDetails.cardType ?? ""}
                            onChange={(e) => setFeeDetail("cardType", e.target.value)}
                          >
                            <option value="">—</option>
                            <option value="credit">{t.portal.feesPage.cfCredit}</option>
                            <option value="debit">{t.portal.feesPage.cfDebit}</option>
                            <option value="prepaid">{t.portal.feesPage.cfPrepaid}</option>
                          </Select>
                        </div>
                        <div className="col-sm-4">
                          <Input
                            label={t.portal.feesPage.cfLast4}
                            value={feeDetails.last4 ?? ""}
                            placeholder="1234"
                            maxLength={4}
                            onChange={(e) => setFeeDetail("last4", e.target.value.replace(/\D/g, ""))}
                          />
                        </div>
                        <div className="col-sm-4">
                          <Input
                            label={`${t.portal.feesPage.cfApproval} *`}
                            value={feeDetails.refNo ?? ""}
                            onChange={(e) => setFeeDetail("refNo", e.target.value)}
                          />
                        </div>
                      </div>
                    )}
                  </div>
                )}
                <div className="d-flex justify-content-between gap-2 pt-2">
                  <Button variant="secondary" onClick={() => decideFee(feeAdm, "waive")} disabled={feeSaving}>
                    {t.portal.admissionsPage.feeWaive}
                  </Button>
                  <Button variant="success" onClick={() => decideFee(feeAdm, "mark-paid")} loading={feeSaving} disabled={feeMissing.length > 0}>
                    <IconWallet className="svg-16" />
                    <span className="ms-1">{t.portal.admissionsPage.feeMarkPaid}</span>
                  </Button>
                </div>
              </div>
            ) : (
              <div>
                {feeAdm.feeStatus === "PAID" ? (
                  <div className="mb-3">
                    <ReviewLine label={t.common.method} value={feeAdm.feeMethod ? t.methods[feeAdm.feeMethod as keyof typeof t.methods] ?? feeAdm.feeMethod : "—"} />
                    <ReviewLine label={t.common.date} value={feeAdm.feePaidAt ? formatDate(feeAdm.feePaidAt, locale) : "—"} />
                    <ReviewLine label={t.portal.feesPage.cfDetailsTitle} value={feeDetailsSummary(feeAdm.feeDetails) || "—"} />
                  </div>
                ) : (
                  <p className="ems-fs-13 text-muted mb-3">{t.portal.admissionsPage.feeWaivedNote}</p>
                )}
                <div className="d-flex justify-content-end gap-2">
                  <Button variant="secondary" onClick={() => setFeeAdm(null)}>
                    {t.common.cancel}
                  </Button>
                  <Button variant="danger" onClick={() => decideFee(feeAdm, "reopen")} disabled={feeSaving}>
                    {t.portal.admissionsPage.feeReopen}
                  </Button>
                </div>
              </div>
            )}
          </div>
        )}
      </Modal>
    </div>
  );
}

/** label + value line used inside the fee modal's paid summary */
function ReviewLine({ label, value }: { label: string; value: ReactNode }) {
  return (
    <div className="d-flex justify-content-between gap-3 py-1" style={{ borderBottom: "1px dashed #edf2f0" }}>
      <span className="ems-fs-12 text-muted text-nowrap">{label}</span>
      <span className="ems-fs-13 text-end f-w-600">{value}</span>
    </div>
  );
}
