"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { useLang } from "@/context/LanguageContext";
import {
  Button,
  EmptyState,
  PageHeader,
  Select,
} from "@/components/ui";
import { api, useApi } from "@/lib/client";
import { formatDate } from "@/lib/utils";
import { IconAward, IconChevronDown, IconFile, IconUsers } from "@/components/icons";
import {
  COLOR_PRESETS,
  DEFAULT_ACCENT,
  DEFAULT_PRIMARY,
  brandFooter as brandFooterOf,
  esc,
  instLogoChip,
  mix,
  openPrintWindow as openPrintDocument,
  pageWrap,
} from "@/lib/print";
import {
  DEFAULT_ID_FIELDS,
  idCardHtml as buildIdCardHtml,
  idCardPrintHtml,
  type DesignId,
  type IdFieldKey,
} from "@/lib/id-card";
import { admitCardHtml as buildAdmitCardHtml, type AdmitVariant } from "@/lib/admit-card";
import QRCodeStyling from "qr-code-styling";

/* ---------------- types ---------------- */

interface StudentRow {
  id: number;
  roll: string;
  registrationNo: string | null;
  cardNo: string | null;
  cardValidTill: string | null;
  bloodGroup: string | null;
  name: string;
  nameBn: string | null;
  gender: string;
  dob: string | null;
  guardianName: string;
  guardianPhone: string;
  address: string | null;
  status: string;
  createdAt: string;
  className: string | null;
  classNameBn: string | null;
  sectionName: string | null;
  sectionNameBn: string | null;
  shiftName: string | null;
  shiftNameBn: string | null;
  groupName: string | null;
  groupNameBn: string | null;
}

interface InstitutionInfo {
  name: string;
  email: string;
  phone: string;
  address: string;
  logo: string | null;
  website: string | null;
}

interface ExamMini {
  id: number;
  name: string;
  className: string | null;
  sectionName: string | null;
  startDate: string;
  endDate: string | null;
}

interface PaperMini {
  id: number;
  subjectName: string | null;
  subjectNameBn: string | null;
  date: string;
  startTime: string;
  endTime: string;
  roomName: string | null;
  roomNameBn: string | null;
  fullMarks: number;
  totalPassMarks: number | null;
}

type Tab = "profile" | "admit" | "seat" | "testimonial" | "transcript" | "idcard";

interface TranscriptRow {
  subjectName: string | null;
  subjectNameBn: string | null;
  date: string;
  fullMarks: number;
  passMarks: number | null;
  obtained: number;
}

interface TranscriptResult {
  exam: { id: number; name: string; className: string | null; sectionName: string | null; startDate: string; endDate: string | null };
  rows: TranscriptRow[];
}

/* ---------------- document color theme (constants live in lib/print.ts) ---------------- */

/** Bangladeshi national grading scale */
function gradeOf(pct: number): string {
  if (pct >= 80) return "A+";
  if (pct >= 70) return "A";
  if (pct >= 60) return "A-";
  if (pct >= 50) return "B";
  if (pct >= 40) return "C";
  if (pct >= 33) return "D";
  return "F";
}

/* ---------------- multi-select dropdown (theme-styled) ---------------- */

function MultiSelect({
  options,
  value,
  onChange,
  placeholder,
  allLabel,
  countLabel,
}: {
  options: { value: string; label: string }[];
  value: string[];
  onChange: (v: string[]) => void;
  placeholder: string;
  allLabel: string;
  countLabel: (n: number) => string;
}) {
  const { t } = useLang();
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState("");
  const rootRef = useRef<HTMLDivElement>(null);
  const searchRef = useRef<HTMLInputElement>(null);

  const searchable = options.length >= 8;
  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return options;
    return options.filter((o) => o.label.toLowerCase().includes(q));
  }, [options, query]);

  const all = options.length > 0 && value.length === options.length;
  const btnLabel = all ? `${allLabel} (${options.length})` : value.length > 0 ? countLabel(value.length) : `— ${placeholder} —`;

  useEffect(() => {
    if (!open) return;
    const onDoc = (e: MouseEvent) => {
      if (rootRef.current && !rootRef.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [open]);

  useEffect(() => {
    if (open && searchable) searchRef.current?.focus();
  }, [open, searchable]);

  return (
    <div className={`ems-select ${open ? "open" : ""}`} ref={rootRef}>
      <button
        type="button"
        className="form-control ems-select-btn"
        aria-haspopup="listbox"
        aria-expanded={open}
        onClick={() => {
          if (!open) setQuery("");
          setOpen((o) => !o);
        }}
      >
        <span className="ems-select-label" title={btnLabel}>{btnLabel}</span>
        <IconChevronDown className="svg-16 ems-select-caret" />
      </button>
      {open && (
        <div className="ems-select-menu ems-select-list" role="listbox">
          {searchable && (
            <div className="ems-select-search">
              <input
                ref={searchRef}
                type="text"
                className="form-control form-control-sm"
                placeholder={t.common.search}
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                onMouseDown={(e) => e.stopPropagation()}
              />
            </div>
          )}
          <div>
            <div
              className={`ems-select-item ${all ? "selected" : ""}`}
              role="option"
              aria-selected={all}
              onMouseDown={(e) => e.preventDefault()}
              onClick={() => onChange(all ? [] : options.map((o) => o.value))}
            >
              <input type="checkbox" className="form-check-input mt-0 me-2" checked={all} readOnly />
              <span className="ems-select-label"><b>{allLabel}</b></span>
            </div>
            {filtered.map((o) => (
              <div
                key={o.value}
                className={`ems-select-item ${value.includes(o.value) ? "selected" : ""}`}
                role="option"
                aria-selected={value.includes(o.value)}
                onMouseDown={(e) => e.preventDefault()}
                onClick={() =>
                  onChange(value.includes(o.value) ? value.filter((x) => x !== o.value) : [...value, o.value])
                }
              >
                <input type="checkbox" className="form-check-input mt-0 me-2" checked={value.includes(o.value)} readOnly />
                <span className="ems-select-label">{o.label}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

export default function PortalCertificatesPage() {
  const { t, locale } = useLang();
  const [tab, setTab] = useState<Tab>("profile");

  const studentsData = useApi<{ students: StudentRow[] }>("/api/portal/students");
  const students = studentsData.data?.students ?? [];
  const instData = useApi<{ institution: InstitutionInfo }>("/api/portal/profile");
  const inst = instData.data?.institution ?? null;
  const examsData = useApi<{ exams: ExamMini[] }>("/api/portal/exams");

  /* ---------- document color theme (applies to every printable doc) ---------- */
  const [primary, setPrimary] = useState(DEFAULT_PRIMARY);
  const [accent, setAccent] = useState(DEFAULT_ACCENT);
  const dark = useMemo(() => mix(primary, "#000000", 0.34), [primary]); // heading text
  const tint = useMemo(() => mix(primary, "#ffffff", 0.88), [primary]); // light band background
  const accentDark = useMemo(() => mix(accent, "#000000", 0.42), [accent]);
  const accentTint = useMemo(() => mix(accent, "#ffffff", 0.88), [accent]);

  // profile selection (single student)
  const [studentId, setStudentId] = useState("");

  // academic transcript — one student's marks across every exam
  const transcriptData = useApi<{ results: TranscriptResult[] }>(
    studentId ? `/api/portal/students/${studentId}/marks` : null,
  );

  // admit card — one exam, many students
  const [admitExamId, setAdmitExamId] = useState("");
  const [admitStudentIds, setAdmitStudentIds] = useState<string[]>([]);
  const [admitVariant, setAdmitVariant] = useState<AdmitVariant>("minimal");
  const admitStudents = useApi<{ students: { id: number; roll: string; name: string; nameBn: string | null; photoUrl: string | null }[] }>(
    admitExamId ? `/api/portal/exams/${admitExamId}/students` : null,
  );
  const admitPapers = useApi<{ schedules: PaperMini[] }>(
    admitExamId ? `/api/portal/exams/${admitExamId}/schedules` : null,
  );
  // verification QR per selected student: minted codes → rendered data-URLs
  const [admitQrs, setAdmitQrs] = useState<Record<number, { url: string; code: string }>>({});
  const admitExamName = (examsData.data?.exams ?? []).find((e) => String(e.id) === admitExamId)?.name ?? null;
  useEffect(() => {
    if (!admitExamId || admitStudentIds.length === 0) {
      setAdmitQrs({});
      return;
    }
    let cancelled = false;
    // QR dots follow the live accent — a darkened shade keeps it scannable
    const qrInk = mix(primary, "#000000", 0.34);
    (async () => {
      try {
        const res = await api<{ codes: { studentId: number; code: string }[] }>("/api/portal/verify-codes", {
          method: "POST",
          body: JSON.stringify({
            kind: "ADMIT_CARD",
            refKey: `exam:${admitExamId}`,
            label: admitExamName,
            rows: admitStudentIds.map((id) => ({ studentId: Number(id) })),
          }),
        });
        const qr = new QRCodeStyling({
          width: 240,
          height: 240,
          margin: 8,
          data: "VERIFY",
          backgroundOptions: { color: "#ffffff" },
          dotsOptions: { type: "rounded", color: qrInk },
          cornersSquareOptions: { type: "extra-rounded", color: qrInk },
        });
        const map: Record<number, { url: string; code: string }> = {};
        for (const c of res.codes) {
          qr.update({ data: c.code });
          const blob = (await qr.getRawData("png")) as Blob | null;
          if (blob) map[c.studentId] = { url: URL.createObjectURL(blob), code: c.code };
        }
        if (!cancelled) setAdmitQrs(map);
      } catch {
        if (!cancelled) setAdmitQrs({});
      }
    })();
    return () => {
      cancelled = true;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [admitExamId, admitStudentIds, primary]);

  // seat plan — many exams, fetched on demand and cached per exam
  // pattern: "list" = the room roster table, "card" = one card per student
  // cards layout: "grid" = 4 cards per printed page, "single" = one per page
  const [seatExamIds, setSeatExamIds] = useState<string[]>([]);
  const [seatPattern, setSeatPattern] = useState<"list" | "card">("list");
  const [seatCardLayout, setSeatCardLayout] = useState<"grid" | "single">("grid");
  type SeatData = { students: { id: number; roll: string; name: string; nameBn: string | null; photoUrl: string | null }[]; schedules: PaperMini[] } | null;
  const [seatCache, setSeatCache] = useState<Record<string, SeatData>>({});
  const seatFetched = useRef<Set<string>>(new Set());
  useEffect(() => {
    let cancelled = false;
    (async () => {
      for (const id of seatExamIds) {
        if (seatFetched.current.has(id)) continue;
        seatFetched.current.add(id);
        try {
          const [st, sc] = await Promise.all([
            api<{ students: { id: number; roll: string; name: string; nameBn: string | null; photoUrl: string | null }[] }>(`/api/portal/exams/${id}/students`),
            api<{ schedules: PaperMini[] }>(`/api/portal/exams/${id}/schedules`),
          ]);
          if (!cancelled) setSeatCache((c) => ({ ...c, [id]: { students: st.students, schedules: sc.schedules } }));
        } catch {
          if (!cancelled) setSeatCache((c) => ({ ...c, [id]: null }));
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [seatExamIds]);

  // id card — many students
  const [idStudentIds, setIdStudentIds] = useState<string[]>([]);
  const [design, setDesign] = useState<DesignId>(1);
  const [idFields, setIdFields] = useState<Record<IdFieldKey, boolean>>({ ...DEFAULT_ID_FIELDS });

  const student = students.find((s) => String(s.id) === studentId) ?? null;
  const admitExam = examsData.data?.exams.find((e) => String(e.id) === admitExamId) ?? null;
  const admitSelected = (admitStudents.data?.students ?? []).filter((s) => admitStudentIds.includes(String(s.id)));
  const seatSelected = (examsData.data?.exams ?? []).filter((e) => seatExamIds.includes(String(e.id)));
  const seatLoading = seatExamIds.some((id) => seatCache[id] === undefined);
  const idSelected = students.filter((s) => idStudentIds.includes(String(s.id)));

  const L = t.portal.certificatesPage;
  const name = (s: { name?: string | null; nameBn?: string | null }) =>
    locale === "bn" && s.nameBn ? s.nameBn : s.name ?? "—";
  /** institution logo chip, sized to match the surrounding header (px = outer box size) */
  const instLogo = (px: number) => instLogoChip(inst?.logo ?? null, px);

  /** branding footer appended to every printable document — static (non-animated)
      Increlog logo rendered right before the credit line */
  const brandFooter = brandFooterOf(t.landing.poweredBy);

  /* ---------------- shared print window ---------------- */

  function openPrintWindow(title: string, bodyHtml: string) {
    openPrintDocument(title, locale, bodyHtml);
  }

  /* ---------------- 1. student profile ---------------- */

  function profileHtml(): string {
    if (!student || !inst) return "";
    const row = (k: string, v: string | null) => `<tr><th>${esc(k)}</th><td>${esc(v) || "—"}</td></tr>`;
    return `
<div class="doc" style="border:1px solid #d9e1ec;border-radius:10px;overflow:hidden">
  <div style="background:${tint};border-bottom:2px solid ${primary};padding:14px 22px;display:flex;align-items:center;gap:12px">
    ${instLogo(44)}
    <div>
      <p style="margin:0;font-size:18px;font-weight:700;color:${dark}">${esc(inst.name)}</p>
      <p style="margin:0;font-size:11px;color:#5b6b79">${esc(inst.address)}</p>
    </div>
    <span style="margin-left:auto;font-size:11px;font-weight:700;color:${primary};border:1px solid ${primary}66;border-radius:8px;padding:4px 10px;background:#fff">${esc(L.studentProfile)}</span>
  </div>
  <div style="display:flex;gap:22px;padding:22px">
    <div style="flex:0 0 auto;text-align:center">
      <div style="width:120px;height:120px;border-radius:14px;background:${tint};color:${primary};display:flex;align-items:center;justify-content:center;font-size:34px;font-weight:700;border:2px solid ${primary}33">
        ${esc(student.name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase())}
      </div>
      <p style="margin:8px 0 0;font-weight:700;font-size:14px">${esc(name(student))}</p>
      <p style="margin:0;font-size:12px;color:#718096">${esc(L.fRoll)}: ${esc(student.roll)}</p>
    </div>
    <div style="flex:1">
      <table style="width:100%;font-size:12.5px">
        <tr>
          <td style="vertical-align:top;width:50%">
            <table style="width:100%">
              ${row(L.fRoll, student.roll)}
              ${row(L.fClass, student.className)}
              ${row(L.fSection, student.sectionName)}
              ${row(L.fShift, student.shiftName)}
              ${row(L.fGroup, student.groupName)}
              ${row(L.fGender, student.gender === "MALE" ? L.male : student.gender === "FEMALE" ? L.female : student.gender)}
            </table>
          </td>
          <td style="vertical-align:top;width:50%">
            <table style="width:100%">
              ${row(L.fDob, student.dob ? formatDate(student.dob, locale) : "")}
              ${row(L.fGuardian, student.guardianName)}
              ${row(L.fPhone, student.guardianPhone)}
              ${row(L.fAddress, student.address)}
              ${row(L.fId, String(student.id))}
            </table>
          </td>
        </tr>
      </table>
    </div>
  </div>
  <div style="display:flex;justify-content:space-between;padding:0 22px 18px;font-size:11px;color:#718096">
    <span>${esc(L.signature)}: ____________________</span>
    <span>${esc(inst.name)}</span>
  </div>
  ${brandFooter}
</div>`;
  }

  /* ---------------- 2. admit card (builder shared with the member side) ---------------- */

  function admitHtml(st: { id: number; roll: string; name: string; nameBn: string | null; photoUrl?: string | null }): string {
    const qr = admitQrs[st.id];
    return buildAdmitCardHtml({
      inst,
      exam: admitExam,
      papers: admitPapers.data?.schedules ?? [],
      student: { ...st, photoUrl: st.photoUrl ?? null, verifyCode: qr?.code ?? null },
      qrDataUrl: qr?.url ?? null,
      variant: admitVariant,
      primary,
      labels: { ...L, verifyScan: L.verifyScan },
      locale,
      poweredBy: t.landing.poweredBy,
    });
  }

  /* ---------------- 3. exam seat plan ---------------- */

  /** deterministic room allocation: students in roll order fill rooms in sequence */
  function roomsFor(papers: PaperMini[], roster: { roll: string; name: string; photoUrl?: string | null }[]) {
    const roomNames = [...new Set(papers.map((p) => (locale === "bn" && p.roomNameBn ? p.roomNameBn : p.roomName)).filter(Boolean))] as string[];
    const names = roomNames.length > 0 ? roomNames : ["Hall"];
    if (roster.length === 0) return names.map((n) => ({ name: n, seats: [] }));
    const per = Math.ceil(roster.length / names.length);
    return names.map((roomName, i) => {
      const chunk = roster.slice(i * per, (i + 1) * per);
      return { name: roomName, seats: chunk.map((s, j) => ({ seat: j + 1, roll: s.roll, name: s.name, photoUrl: s.photoUrl ?? null })) };
    });
  }

  function seatPlanHtml(exam: ExamMini): string {
    if (!inst) return "";
    const data = seatCache[String(exam.id)];
    if (!data) return "";
    const roster = data.students.map((s) => ({ roll: s.roll, name: name(s) }));
    const rooms = roomsFor(data.schedules, roster);
    const roomBlock = (room: { name: string; seats: { seat: number; roll: string; name: string; photoUrl?: string | null }[] }) => `
<div style="border:1px solid #d9e1ec;border-radius:10px;margin-top:14px;overflow:hidden">
  <div style="background:${tint};padding:7px 14px;font-weight:700;font-size:12.5px;display:flex;justify-content:space-between;color:${dark}">
    <span>${esc(L.roomLabel)}: ${esc(room.name)}</span>
    <span style="color:#5b6b79;font-weight:600">${esc(L.seatNo)} 1–${room.seats.length} (${room.seats.length})</span>
  </div>
  <table style="width:100%;font-size:11.5px">
    ${room.seats
      .map(
        (s, i) =>
          `${i % 3 === 0 ? "<tr>" : ""}<td style="border:1px solid #eef2f7;padding:4px 8px;width:33.33%"><b>#${s.seat}</b> · ${esc(s.roll)} — ${esc(s.name)}</td>${i % 3 === 2 || i === room.seats.length - 1 ? "</tr>" : ""}`,
      )
      .join("")}
  </table>
</div>`;
    return `
<div class="doc">
  <div style="display:flex;align-items:center;gap:12px;background:${tint};border:1px solid ${primary}44;border-bottom:2px solid ${primary};border-radius:10px;padding:12px 18px">
    ${instLogo(44)}
    <div>
      <p style="margin:0;font-size:17px;font-weight:700;color:${dark}">${esc(inst.name)}</p>
      <p style="margin:0;font-size:11px;color:#5b6b79">${esc(L.seatPlan)} — ${esc(exam.name)}${exam.className ? ` · ${esc(exam.className)}` : ""}${exam.sectionName ? ` · ${esc(exam.sectionName)}` : ""} · ${formatDate(exam.startDate, locale)}${exam.endDate ? ` → ${formatDate(exam.endDate, locale)}` : ""}</p>
    </div>
    <span style="margin-left:auto;font-size:11.5px;color:#5b6b79">${esc(L.studentsLabel)}: <b style="color:${dark}">${roster.length}</b> · ${esc(L.roomsLabel)}: <b style="color:${dark}">${rooms.length}</b></span>
  </div>
  ${rooms.map(roomBlock).join("")}
  ${brandFooter}
</div>`;
  }

  /** seat card — one per student: institution header + exam & student body */
  function seatCardHtml(
    exam: ExamMini,
    room: string,
    seat: { seat: number; roll: string; name: string; photoUrl?: string | null },
    className: string,
    sectionName: string,
  ): string {
    if (!inst) return "";
    const photo = seat.photoUrl
      ? `<img src="${seat.photoUrl}" alt="" style="width:56px;height:56px;border-radius:50%;object-fit:cover;border:2px solid ${primary}55;flex-shrink:0"/>`
      : `<span style="width:56px;height:56px;border-radius:50%;background:${tint};color:${primary};display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;flex-shrink:0">${esc(String(seat.name).slice(0, 2).toUpperCase())}</span>`;
    return `
<div style="border:1.5px solid ${primary};border-radius:12px;overflow:hidden;width:100%;max-width:${seatCardLayout === "single" ? "560px" : "100%"};margin:0 auto;box-sizing:border-box">
  <div style="background:${tint};border-bottom:2px solid ${primary};padding:8px 14px;display:flex;align-items:center;gap:10px">
    ${instLogo(40)}
    <div style="min-width:0">
      <p style="margin:0;font-size:14.5px;font-weight:800;color:${dark};white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${esc(inst.name)}</p>
      <p style="margin:0;font-size:9.5px;color:#5b6b79;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${esc(inst.address)}${inst.phone ? " · " + esc(inst.phone) : ""}</p>
    </div>
    <span style="margin-left:auto;font-size:9px;font-weight:800;letter-spacing:.6px;color:${primary};white-space:nowrap">EXAM SEAT CARD</span>
  </div>
  <div style="padding:10px 14px">
    <div style="display:flex;gap:6px;align-items:center;background:rgba(36,105,92,.05);border:1px solid ${primary}33;border-radius:8px;padding:6px 10px;margin-bottom:8px">
      <span style="font-size:10.5px;font-weight:800;color:${dark}">${esc(exam.name)}</span>
      ${className ? `<span style="font-size:10px;color:#5b6b79">· ${esc(className)}</span>` : ""}
      ${sectionName ? `<span style="font-size:10px;color:#5b6b79">· ${esc(sectionName)}</span>` : ""}
      <span style="margin-left:auto;font-size:10px;color:#5b6b79">${esc(formatDate(exam.startDate, locale))}${exam.endDate ? " → " + esc(formatDate(exam.endDate, locale)) : ""}</span>
    </div>
    <div style="display:flex;gap:10px;align-items:stretch">
      <div style="display:flex;align-items:center;justify-content:center;padding:2px">${photo}</div>
      <div style="flex:1;border:1px solid #e2e8f0;border-radius:8px;padding:6px 10px">
        <p style="margin:0 0 2px;font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#8a97a8">ROLL</p>
        <p style="margin:0;font-size:16px;font-weight:800;color:${dark}">${esc(seat.roll)}</p>
        <p style="margin:4px 0 0;font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#8a97a8">NAME</p>
        <p style="margin:0;font-size:12px;font-weight:700">${esc(seat.name)}</p>
      </div>
      <div style="width:34%;border:1.5px dashed ${primary}66;border-radius:8px;padding:6px 10px;text-align:center;display:flex;flex-direction:column;justify-content:center">
        <p style="margin:0 0 2px;font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#8a97a8">${esc(L.roomLabel)}</p>
        <p style="margin:0;font-size:12.5px;font-weight:800;color:${dark}">${esc(room)}</p>
        <p style="margin:6px 0 2px;font-size:8.5px;font-weight:700;letter-spacing:.5px;color:#8a97a8">${esc(L.seatNo)}</p>
        <p style="margin:0;font-size:20px;font-weight:800;color:${primary}">${seat.seat}</p>
      </div>
    </div>
  </div>
</div>`;
  }

  /** all cards for the selected exams, laid out per the print layout */
  function seatCardsHtml(): string {
    const cards: string[] = [];
    for (const exam of seatSelected) {
      const data = seatCache[String(exam.id)];
      if (!data) continue;
      const roster = data.students.map((st) => ({ roll: st.roll, name: name(st), photoUrl: st.photoUrl ?? null }));
      const rooms = roomsFor(data.schedules, roster);
      for (const room of rooms) {
        for (const seat of room.seats) {
          cards.push(
            seatCardHtml(
              exam,
              room.name,
              seat,
              exam.className ?? "",
              exam.sectionName ?? "",
            ),
          );
        }
      }
    }
    if (seatCardLayout === "single") {
      // one card per printed page
      return cards.map((c) => `<div style="page-break-after:always">${c}</div>`).join("");
    }
    // 2×2 grid of cards per page
    return `<div style="display:flex;flex-wrap:wrap;gap:6mm">
      ${cards.map((c) => `<div style="width:calc(50% - 3mm);page-break-inside:avoid">${c}</div>`).join("")}
    </div>`;
  }

  /* ---------------- 4. testimonial ---------------- */

  function testimonialHtml(): string {
    if (!student || !inst) return "";
    const today = formatDate(new Date().toISOString(), locale);
    const tpl = student.gender === "FEMALE" ? L.tBody1Daughter : L.tBody1Son;
    const body1 = tpl
      .replace("{name}", `<b>${esc(name(student))}</b>`)
      .replace("{guardian}", `<b>${esc(student.guardianName)}</b>`)
      .replace("{roll}", `<b>${esc(student.roll)}</b>`)
      .replace("{since}", `<b>${esc(formatDate(student.createdAt, locale))}</b>`);
    const body2 = L.tBody2.replace("{name}", esc(name(student)));
    return `
<div class="doc" style="border:1px solid #d9e1ec;border-radius:10px;overflow:hidden">
  <div style="background:${tint};border-bottom:2px solid ${primary};padding:14px 22px;display:flex;align-items:center;gap:12px">
    ${instLogo(44)}
    <div>
      <p style="margin:0;font-size:18px;font-weight:700;color:${dark}">${esc(inst.name)}</p>
      <p style="margin:0;font-size:11px;color:#5b6b79">${esc(inst.address)}</p>
    </div>
    <span style="margin-left:auto;font-size:11px;font-weight:700;color:${primary};border:1px solid ${primary}66;border-radius:8px;padding:4px 10px;background:#fff">${esc(L.studentTestimonial)}</span>
  </div>
  <div style="padding:22px 26px 10px">
    <p style="margin:0 0 4px;text-align:center;font-size:16px;font-weight:700;color:${dark};letter-spacing:2px">${esc(L.studentTestimonial).toUpperCase()}</p>
    <p style="margin:0 0 16px;text-align:center;font-size:10.5px;color:#718096;letter-spacing:1px">${esc(inst.name)}</p>
    <div style="display:flex;justify-content:space-between;font-size:11px;color:#5b6b79;margin-bottom:16px">
      <span>${esc(L.refLabel)}: <b>EMS-TR-${String(student.id).padStart(5, "0")}</b></span>
      <span>${esc(L.dateOfIssue)}: <b>${today}</b></span>
    </div>
    <p style="margin:0 0 12px;font-size:12.5px;line-height:1.8;text-align:justify">${body1}</p>
    <p style="margin:0 0 12px;font-size:12.5px;line-height:1.8;text-align:justify">${body2}</p>
    <p style="margin:0 0 24px;font-size:12.5px;line-height:1.8;text-align:justify">${esc(L.tBody3)}</p>
    <div style="display:flex;justify-content:space-between;font-size:11.5px;color:#5b6b79">
      <span>${esc(L.classTeacher)}: ____________________</span>
      <span>${esc(L.headOfInstitution)}: ____________________</span>
    </div>
  </div>
  ${brandFooter}
</div>`;
  }

  /* ---------------- 5. academic transcript ---------------- */

  function transcriptHtml(): string {
    if (!student || !inst) return "";
    const results = transcriptData.data?.results ?? [];
    const info = (k: string, v: string) => `<div style="flex:1"><p style="margin:0;font-size:10.5px;color:#718096">${esc(k)}</p><p style="margin:0;font-size:13px;font-weight:700">${esc(v) || "—"}</p></div>`;
    const examBlock = (r: TranscriptResult) => {
      const totalFull = r.rows.reduce((s, x) => s + x.fullMarks, 0);
      const totalGot = r.rows.reduce((s, x) => s + x.obtained, 0);
      const pct = totalFull > 0 ? (totalGot / totalFull) * 100 : 0;
      return `
<div style="margin-top:14px;border:1px solid #d9e1ec;border-radius:10px;overflow:hidden">
  <div style="background:${tint};padding:7px 14px;font-weight:700;font-size:12.5px;display:flex;justify-content:space-between;color:${dark}">
    <span>${esc(r.exam.name)}${r.exam.className ? ` — ${esc(r.exam.className)}` : ""}${r.exam.sectionName ? ` · ${esc(r.exam.sectionName)}` : ""}</span>
    <span style="color:#5b6b79;font-weight:600">${formatDate(r.exam.startDate, locale)}${r.exam.endDate ? ` → ${formatDate(r.exam.endDate, locale)}` : ""}</span>
  </div>
  <table style="width:100%;font-size:11.5px">
    <thead><tr style="background:#fafcfd">
      <th style="border:1px solid #e6edef;padding:5px 9px;text-align:left">${esc(L.subjectLabel)}</th>
      <th style="border:1px solid #e6edef;padding:5px 9px;text-align:center">${esc(L.fullMarksLabel)}</th>
      <th style="border:1px solid #e6edef;padding:5px 9px;text-align:center">${esc(L.passMarksLabel)}</th>
      <th style="border:1px solid #e6edef;padding:5px 9px;text-align:center">${esc(L.obtainedLabel)}</th>
      <th style="border:1px solid #e6edef;padding:5px 9px;text-align:center">${esc(L.gradeLabel)}</th>
    </tr></thead>
    <tbody>
      ${r.rows
        .map(
          (x) => `<tr>
        <td style="border:1px solid #e6edef;padding:5px 9px">${esc(locale === "bn" && x.subjectNameBn ? x.subjectNameBn : x.subjectName ?? "—")}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center">${x.fullMarks}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center">${x.passMarks ?? "—"}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center;font-weight:600">${x.obtained}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center;font-weight:700;color:${primary}">${gradeOf(totalFull > 0 ? (x.obtained / x.fullMarks) * 100 : 0)}</td>
      </tr>`,
        )
        .join("")}
      <tr style="background:${tint}">
        <td style="border:1px solid #e6edef;padding:5px 9px;font-weight:700;color:${dark}">${esc(L.totalLabel)}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center;font-weight:700">${totalFull}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px"></td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center;font-weight:700">${totalGot}</td>
        <td style="border:1px solid #e6edef;padding:5px 9px;text-align:center;font-weight:700;color:${primary}">${gradeOf(pct)}</td>
      </tr>
    </tbody>
  </table>
</div>`;
    };
    return `
<div class="doc">
  <div style="display:flex;align-items:center;gap:12px;background:${tint};border:1px solid ${primary}44;border-bottom:2px solid ${primary};border-radius:10px;padding:12px 18px">
    ${instLogo(44)}
    <div>
      <p style="margin:0;font-size:17px;font-weight:700;color:${dark}">${esc(inst.name)}</p>
      <p style="margin:0;font-size:11px;color:#5b6b79">${esc(L.academicTranscript)}</p>
    </div>
    <span style="margin-left:auto;font-size:11px;font-weight:700;color:${primary};border:1px solid ${primary}66;border-radius:8px;padding:4px 10px;background:#fff">${esc(L.academicTranscript)}</span>
  </div>
  <div style="display:flex;flex-wrap:wrap;gap:14px;border:1px solid #e6edef;border-radius:10px;padding:12px 14px;background:#fafcfd;margin-top:14px">
    ${info(L.fRoll, student.roll)}
    ${info(L.fName, name(student))}
    ${info(L.fClass, student.className ?? "")}
    ${info(L.fSection, student.sectionName ?? "")}
    ${info(L.fDob, student.dob ? formatDate(student.dob, locale) : "")}
  </div>
  ${results.length > 0 ? results.map(examBlock).join("") : `<p style="margin:16px 0;text-align:center;font-size:12px;color:#718096">${esc(L.noResultsLabel)}</p>`}
  ${brandFooter}
</div>`;
  }

  /* ---------------- 6. ID card (4 designs, configurable fields — builder shared with the member side) ---------------- */

  function idCardHtml(st: StudentRow): string {
    return buildIdCardHtml({
      st,
      inst,
      design,
      primary,
      accent,
      fields: idFields,
      labels: L,
      locale,
    });
  }

  /* ---------------- render ---------------- */

  const tabs: { id: Tab; label: string }[] = [
    { id: "profile", label: L.tabProfile },
    { id: "admit", label: L.tabAdmit },
    { id: "seat", label: L.tabSeat },
    { id: "testimonial", label: L.tabTestimonial },
    { id: "transcript", label: L.tabTranscript },
    { id: "idcard", label: L.tabId },
  ];

  const studentOptions = students.map((s) => (
    <option key={s.id} value={s.id}>{s.roll} — {name(s)}</option>
  ));
  const studentMultiOptions = students.map((s) => ({ value: String(s.id), label: `${s.roll} — ${name(s)}` }));
  const examMultiOptions = (examsData.data?.exams ?? []).map((e) => ({
    value: String(e.id),
    label: `${e.name}${e.className ? ` · ${e.className}` : ""}${e.sectionName ? ` · ${e.sectionName}` : ""}`,
  }));
  const countLabel = (n: number) => L.selectedLabel.replace("{n}", String(n));

  const previewBox = (html: string, max = 780) => (
    <div className="card-body" style={{ background: "#f4f7fa", overflowX: "auto" }}>
      <div style={{ width: max, margin: "0 auto", background: "#fff", borderRadius: 10, padding: 14, boxShadow: "0 4px 14px rgba(0,0,0,.06)" }} dangerouslySetInnerHTML={{ __html: html }} />
    </div>
  );

  const printBtn = (label: string, onClick: () => void, disabled: boolean) => (
    <Button size="sm" disabled={disabled} onClick={onClick}>
      <IconFile className="svg-16" />
      <span className="ms-1">{label}</span>
    </Button>
  );

  const designChoices: { id: DesignId; label: string }[] = [
    { id: 1, label: L.designClassic },
    { id: 2, label: L.designModern },
    { id: 3, label: L.designSide },
    { id: 4, label: L.designFrame },
  ];

  const fieldChoices: { key: IdFieldKey; label: string }[] = [
    { key: "roll", label: L.fRoll },
    { key: "className", label: L.fClass },
    { key: "section", label: L.fSection },
    { key: "shift", label: L.fShift },
    { key: "group", label: L.fGroup },
    { key: "dob", label: L.fDob },
    { key: "gender", label: L.fGender },
    { key: "guardian", label: L.fGuardian },
    { key: "phone", label: L.fPhone },
    { key: "address", label: L.fAddress },
    { key: "studentId", label: L.fId },
    { key: "cardNo", label: L.fCardNo },
    { key: "registrationNo", label: L.fRegistration },
    { key: "bloodGroup", label: L.fBlood },
    { key: "validTill", label: L.fValidTill },
  ];

  const multiSelectProps = {
    allLabel: L.allLabel,
    countLabel,
  };

  return (
    <div>
      <PageHeader title={L.title} subtitle={L.subtitle} />

      <ul className="nav nav-tabs mb-3">
        {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>

      {/* ---------- color theme picker (applies to every document) ---------- */}
      <div className="d-flex flex-wrap align-items-center gap-2 mb-4">
        <span className="f-w-700 ems-fs-13">{L.colorsLabel}</span>
        {COLOR_PRESETS.map((p, i) => {
          const active = primary === p.primary && accent === p.accent;
          return (
            <button
              key={i}
              type="button"
              title={`${L.primaryLabel} ${p.primary} · ${L.accentLabel} ${p.accent}`}
              className="btn btn-sm p-0"
              style={{
                width: 26,
                height: 26,
                borderRadius: "50%",
                background: `linear-gradient(135deg, ${p.primary} 0 50%, ${p.accent} 50% 100%)`,
                border: active ? "2px solid #2c323f" : "1px solid #d9e1ec",
              }}
              onClick={() => {
                setPrimary(p.primary);
                setAccent(p.accent);
              }}
            />
          );
        })}
        <div style={{ width: 1, height: 22, background: "#e6edef" }} />
        <label className="d-inline-flex align-items-center gap-1 ems-fs-13 f-w-600 mb-0">
          {L.primaryLabel}
          <input
            type="color"
            value={primary}
            onChange={(e) => setPrimary(e.target.value)}
            style={{ width: 32, height: 26, padding: 0, border: "1px solid #d9e1ec", borderRadius: 6, background: "#fff", cursor: "pointer" }}
          />
        </label>
        <label className="d-inline-flex align-items-center gap-1 ems-fs-13 f-w-600 mb-0">
          {L.accentLabel}
          <input
            type="color"
            value={accent}
            onChange={(e) => setAccent(e.target.value)}
            style={{ width: 32, height: 26, padding: 0, border: "1px solid #d9e1ec", borderRadius: 6, background: "#fff", cursor: "pointer" }}
          />
        </label>
      </div>

      {/* ---------- Student profile ---------- */}
      {tab === "profile" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div style={{ maxWidth: 320 }}>
              <Select wrapperClassName="form-group mb-0" value={studentId} onChange={(e) => setStudentId(e.target.value)}>
                <option value="">— {L.selectStudent} —</option>
                {studentOptions}
              </Select>
            </div>
            {printBtn(L.print, () => openPrintWindow(L.studentProfile, profileHtml()), !student)}
          </div>
          {students.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.noStudents} /></div>
          ) : !student ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.selectStudent} /></div>
          ) : (
            previewBox(profileHtml())
          )}
        </div>
      )}

      {/* ---------- Admit card ---------- */}
      {tab === "admit" && (
        <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={{ maxWidth: 260 }}>
                <Select wrapperClassName="form-group mb-0" value={admitExamId} onChange={(e) => { setAdmitExamId(e.target.value); setAdmitStudentIds([]); }}>
                  <option value="">— {L.selectExam} —</option>
                  {(examsData.data?.exams ?? []).map((e) => (
                    <option key={e.id} value={e.id}>{e.name}{e.className ? ` · ${e.className}` : ""}{e.sectionName ? ` · ${e.sectionName}` : ""}</option>
                  ))}
                </Select>
              </div>
              {admitExamId && (
                <div style={{ minWidth: 240 }}>
                  <MultiSelect
                    {...multiSelectProps}
                    options={(admitStudents.data?.students ?? []).map((s) => ({ value: String(s.id), label: `${s.roll} — ${name(s)}` }))}
                    value={admitStudentIds}
                    onChange={setAdmitStudentIds}
                    placeholder={L.selectStudents}
                  />
                </div>
              )}
              <div className="btn-group">
                {(["minimal", "detailed"] as AdmitVariant[]).map((v) => (
                  <button key={v} type="button" className={`btn btn-sm ${admitVariant === v ? "btn-primary" : "btn-outline-primary"}`} onClick={() => setAdmitVariant(v)}>
                    {v === "minimal" ? L.variantMinimal : L.variantDetailed}
                  </button>
                ))}
              </div>
            </div>
            {printBtn(
              L.print,
              () => openPrintWindow(L.admitCard, admitSelected.map((s, i) => pageWrap(admitHtml(s), i, admitSelected.length)).join("")),
              !admitExamId || admitSelected.length === 0,
            )}
          </div>
          {!admitExamId || admitSelected.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconAward />} title={!admitExamId ? L.selectExam : L.selectStudents} /></div>
          ) : (
            previewBox(admitSelected.map((s) => admitHtml(s)).join(`<div style="height:16px"></div>`))
          )}
        </div>
      )}

      {/* ---------- Seat plan ---------- */}
      {tab === "seat" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div style={{ minWidth: 280 }}>
              <MultiSelect
                {...multiSelectProps}
                options={examMultiOptions}
                value={seatExamIds}
                onChange={setSeatExamIds}
                placeholder={L.selectExams}
              />
            </div>
            <div className="d-flex flex-wrap align-items-center gap-2">
              <div className="btn-group btn-group-sm">
                <button
                  type="button"
                  className={`btn f-w-700 ${seatPattern === "list" ? "btn-primary" : "btn-outline-secondary"}`}
                  onClick={() => setSeatPattern("list")}
                >
                  {L.seatPatternList}
                </button>
                <button
                  type="button"
                  className={`btn f-w-700 ${seatPattern === "card" ? "btn-primary" : "btn-outline-secondary"}`}
                  onClick={() => setSeatPattern("card")}
                >
                  {L.seatPatternCard}
                </button>
              </div>
              {seatPattern === "card" && (
                <div className="btn-group btn-group-sm">
                  <button
                    type="button"
                    className={`btn f-w-700 ${seatCardLayout === "grid" ? "btn-success" : "btn-outline-secondary"}`}
                    onClick={() => setSeatCardLayout("grid")}
                    title={L.seatLayoutGridHint}
                  >
                    {L.seatLayoutGrid}
                  </button>
                  <button
                    type="button"
                    className={`btn f-w-700 ${seatCardLayout === "single" ? "btn-success" : "btn-outline-secondary"}`}
                    onClick={() => setSeatCardLayout("single")}
                    title={L.seatLayoutSingleHint}
                  >
                    {L.seatLayoutSingle}
                  </button>
                </div>
              )}
              {printBtn(
                L.print,
                () =>
                  seatPattern === "card"
                    ? openPrintWindow(L.seatPlan, seatCardsHtml())
                    : openPrintWindow(
                        L.seatPlan,
                        seatSelected.map((e, i) => pageWrap(seatPlanHtml(e), i, seatSelected.length)).join(""),
                      ),
                seatSelected.length === 0 || seatLoading,
              )}
            </div>
          </div>
          {seatSelected.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconAward />} title={L.selectExams} /></div>
          ) : seatPattern === "card" ? (
            previewBox(seatCardsHtml())
          ) : (
            previewBox(seatSelected.map((e) => seatPlanHtml(e)).filter(Boolean).join(`<div style="height:22px"></div>`))
          )}
        </div>
      )}

      {/* ---------- Testimonial ---------- */}
      {tab === "testimonial" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div style={{ maxWidth: 320 }}>
              <Select wrapperClassName="form-group mb-0" value={studentId} onChange={(e) => setStudentId(e.target.value)}>
                <option value="">— {L.selectStudent} —</option>
                {studentOptions}
              </Select>
            </div>
            {printBtn(L.print, () => openPrintWindow(L.studentTestimonial, testimonialHtml()), !student)}
          </div>
          {students.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.noStudents} /></div>
          ) : !student ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.selectStudent} /></div>
          ) : (
            previewBox(testimonialHtml())
          )}
        </div>
      )}

      {/* ---------- Academic transcript ---------- */}
      {tab === "transcript" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div style={{ maxWidth: 320 }}>
              <Select wrapperClassName="form-group mb-0" value={studentId} onChange={(e) => setStudentId(e.target.value)}>
                <option value="">— {L.selectStudent} —</option>
                {studentOptions}
              </Select>
            </div>
            {printBtn(L.print, () => openPrintWindow(L.academicTranscript, transcriptHtml()), !student)}
          </div>
          {students.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.noStudents} /></div>
          ) : !student ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.selectStudent} /></div>
          ) : (
            previewBox(transcriptHtml())
          )}
        </div>
      )}

      {/* ---------- ID card ---------- */}
      {tab === "idcard" && (
        <div className="card">
          <div className="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
            <div style={{ minWidth: 280 }}>
              <MultiSelect
                {...multiSelectProps}
                options={studentMultiOptions}
                value={idStudentIds}
                onChange={setIdStudentIds}
                placeholder={L.selectStudents}
              />
            </div>
            {printBtn(
              L.print,
              () => {
                openPrintWindow(
                  L.studentIdCard,
                  idCardPrintHtml(idSelected.map((s) => idCardHtml(s)), design, t.landing.poweredBy),
                );
              },
              idSelected.length === 0,
            )}
          </div>
          {idSelected.length === 0 ? (
            <div className="card-body"><EmptyState icon={<IconUsers />} title={L.selectStudents} /></div>
          ) : (
            <div className="card-body">
              <div className="row g-3">
                <div className="col-lg-7">
                  <p className="f-w-700 ems-fs-13 mb-2">{L.designLabel}</p>
                  <div className="d-flex flex-wrap gap-2 mb-3">
                    {designChoices.map((d) => (
                      <button
                        key={d.id}
                        type="button"
                        className={`btn btn-sm ${design === d.id ? "btn-primary" : "btn-outline-primary"}`}
                        onClick={() => setDesign(d.id)}
                      >
                        {d.id}. {d.label}
                      </button>
                    ))}
                  </div>
                  <p className="f-w-700 ems-fs-13 mb-2">{L.fieldsLabel}</p>
                  <div className="row g-2">
                    {fieldChoices.map((f) => (
                      <div className="col-6 col-md-4" key={f.key}>
                        <div className="form-check">
                          <input
                            className="form-check-input"
                            type="checkbox"
                            id={`idfield-${f.key}`}
                            checked={idFields[f.key]}
                            onChange={(e) => setIdFields({ ...idFields, [f.key]: e.target.checked })}
                          />
                          <label className="form-check-label f-w-600 ems-fs-13" htmlFor={`idfield-${f.key}`}>
                            {f.label}
                          </label>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
                <div className="col-lg-5">
                  <p className="f-w-700 ems-fs-13 mb-2">{L.previewLabel}</p>
                  <div className="d-flex flex-wrap justify-content-center align-items-start gap-3" style={{ background: "#f4f7fa", borderRadius: 10, padding: 20, minHeight: 380 }}>
                    {idSelected.map((s) => (
                      <div key={s.id} dangerouslySetInnerHTML={{ __html: idCardHtml(s) }} />
                    ))}
                  </div>
                  <p className="text-center ems-fs-11 text-muted mb-0 mt-2">{t.landing.poweredBy}</p>
                </div>
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}
