import React, { useCallback, useEffect, useRef, useState } from 'react';
import {
  apiExam,
  staffFetch,
  type ExamPaper,
  type ExamRoomStudent,
  type ExamSubmission,
  type StudentItem,
} from '../api';
import { useGitHubConnection } from '../hooks/useGitHubConnection';
import { navigate, pushNav } from '../navigation';
import { BASE_APP_SUGGESTIONS, DEFAULT_EXAM_ALLOWED_APPS } from '../constants';
import { openStaffChat } from '../chatEvents';
import { AppPoolModal } from './AppPoolModal';
import { AppTemplatePickerModal } from './AppTemplatePickerModal';
import { mergeKeywordCSV } from '../utils/appKeywords';
import { ExamPaperPdfModal } from './ExamPaperPdfModal';
import { NavHistoryBar } from './NavHistoryBar';
import { StudentDetailModal } from './StudentDetailModal';
import { fmtTime, localInputToISO, toLocalInput } from './ExamsTab';
import { ExamGridProctor } from './ExamGridProctor';
import { WorkspaceSeatingChart } from './WorkspaceSeatingChart';
import { onStudentPresence } from '../hooks/useStaffChatSocket';
import { ViolationsPanel } from './ViolationsPanel';

const EXAM_APP_SUGGESTIONS = [...new Set([...BASE_APP_SUGGESTIONS, 'msedge', 'edge', 'acrobat', 'foxit'])];

interface Props {
  examId: number;
  onBack: () => void;
}

function statusBadge(displayStatus: string) {
  switch (displayStatus) {
    case 'draft': return { text: 'Tạm thời', cls: 'badge-muted' };
    case 'ready': return { text: 'Sẵn sàng', cls: 'badge-info' };
    case 'active': return { text: 'Đang thi', cls: 'badge-success' };
    case 'cancelled': return { text: 'Đã hủy', cls: 'badge-warning' };
    case 'ended': return { text: 'Kết thúc', cls: 'badge-muted' };
    default: return { text: displayStatus, cls: 'badge-muted' };
  }
}

function statusColor(displayStatus: string) {
  switch (displayStatus) {
    case 'active': return 'var(--success)';
    case 'ready': return 'var(--accent)';
    case 'cancelled': return 'var(--danger)';
    default: return 'var(--text-muted)';
  }
}

export const ExamRoomWorkspace: React.FC<Props> = ({ examId, onBack }) => {
  const [roomName, setRoomName] = useState('');
  const [start, setStart] = useState('');
  const [end, setEnd] = useState('');
  const [allowedApps, setAllowedApps] = useState(DEFAULT_EXAM_ALLOWED_APPS);
  const [quizUrl, setQuizUrl] = useState('');
  const [gitPublishUrl, setGitPublishUrl] = useState('');
  const [bundlingSubs, setBundlingSubs] = useState(false);
  const [publishingGit, setPublishingGit] = useState(false);
  const [publishingGitStudents, setPublishingGitStudents] = useState(false);
  const gh = useGitHubConnection();
  const [editable, setEditable] = useState(false);
  const [prepEditable, setPrepEditable] = useState(false);
  const [displayStatus, setDisplayStatus] = useState('draft');
  const [searchMode, setSearchMode] = useState<'single' | 'bulk'>('single');
  const [bulkInput, setBulkInput] = useState('');
  const [canPublish, setCanPublish] = useState(false);
  const [canUnpublish, setCanUnpublish] = useState(false);
  const [canCancel, setCanCancel] = useState(false);
  const [papers, setPapers] = useState<ExamPaper[]>([]);
  const [students, setStudents] = useState<ExamRoomStudent[]>([]);
  const [submissions, setSubmissions] = useState<ExamSubmission[]>([]);
  const [searchQ, setSearchQ] = useState('');
  const [searchHits, setSearchHits] = useState<{ studentRkId: number; fullName: string; studentCode: string; email: string; classCodes: string }[]>([]);
  const [selectedPickerRkIds, setSelectedPickerRkIds] = useState<number[]>([]);
  const [paperTitle, setPaperTitle] = useState('');
  const [packagePdf, setPackagePdf] = useState<File | null>(null);
  const [packageResources, setPackageResources] = useState<File[]>([]);
  const [creatingPackage, setCreatingPackage] = useState(false);
  const [sendSchedule, setSendSchedule] = useState('');
  const [msg, setMsg] = useState('');
  const [err, setErr] = useState('');
  const [appPoolOpen, setAppPoolOpen] = useState(false);
  const [templatePickerOpen, setTemplatePickerOpen] = useState(false);
  const [studentPickerOpen, setStudentPickerOpen] = useState(false);
  const [saving, setSaving] = useState(false);
  const [loading, setLoading] = useState(true);
  const [onlineIds, setOnlineIds] = useState<number[]>([]);
  const [searchQuery, setSearchQuery] = useState('');
  const [selectedStudent, setSelectedStudent] = useState<StudentItem | null>(null);
  const [activeSubTab, setActiveSubTab] = useState<'roster' | 'detail' | 'submissions' | 'grid' | 'violations'>('roster');
  const [configOpen, setConfigOpen] = useState(false);
  const [configTab, setConfigTab] = useState<'info' | 'apps' | 'papers'>('info');
  const [papersModalOpen, setPapersModalOpen] = useState(false);
  const [pdfViewer, setPdfViewer] = useState<{ title: string; bytes: Uint8Array | null } | null>(null);
  const [pdfLoading, setPdfLoading] = useState(false);
  const [inlineResource, setInlineResource] = useState<{ url: string; title: string; isImage: boolean } | null>(null);

  const canEditApps = displayStatus === 'draft' || displayStatus === 'ready' || displayStatus === 'active';
  const anyPaperSent = students.some((s) => s.paperSentAt);
  const canModifyPapers = prepEditable || (!anyPaperSent && (displayStatus === 'draft' || displayStatus === 'ready' || displayStatus === 'active'));

  const load = useCallback(async () => {
    setLoading(true);
    try {
      const data = await apiExam.get(examId);
      setRoomName(data.room.name);
      setStart(toLocalInput(data.room.startTime));
      setEnd(toLocalInput(data.room.endTime));
      setAllowedApps(data.room.allowedApps || DEFAULT_EXAM_ALLOWED_APPS);
      setQuizUrl(data.room.quizUrl || '');
      setGitPublishUrl(data.room.gitPublishUrl || '');
      setEditable(data.editable);
      setPrepEditable(data.prepEditable ?? data.editable);
      setDisplayStatus(data.displayStatus);
      setCanPublish(data.canPublish);
      setCanUnpublish(data.canUnpublish);
      setCanCancel(data.canCancel);
      setPapers(data.papers);
      setStudents(data.students);
      pushNav({
        kind: 'exam',
        tab: 'exams',
        examId,
        label: data.room.name || `Phòng thi ${examId}`,
      });
      const subs = await apiExam.listSubmissions(examId);
      setSubmissions(subs.data);
    } finally {
      setLoading(false);
    }
  }, [examId]);

  useEffect(() => { load().catch(console.error); }, [load]);

  const closePdfViewer = useCallback(() => {
    setPdfViewer(null);
    setPdfLoading(false);
  }, []);

  const closeInlineResource = useCallback(() => {
    setInlineResource((prev) => {
      if (prev?.url) URL.revokeObjectURL(prev.url);
      return null;
    });
  }, []);

  const inlineResourceRef = useRef(inlineResource);
  inlineResourceRef.current = inlineResource;

  useEffect(() => () => {
    if (inlineResourceRef.current?.url) URL.revokeObjectURL(inlineResourceRef.current.url);
  }, []);

  const fetchOnline = useCallback(async () => {
    try {
      const res = await apiExam.fetchOnlineStudents(examId);
      setOnlineIds(res.onlineStudentIds || []);
    } catch (e) {
      console.error(e);
    }
  }, [examId]);

  useEffect(() => {
    fetchOnline();
    const t = setInterval(fetchOnline, 8000);
    return () => clearInterval(t);
  }, [fetchOnline]);

  useEffect(() => {
    const roster = new Set(students.map((s) => s.studentRkId));
    return onStudentPresence(({ studentId, online }) => {
      if (!roster.has(studentId)) return;
      setOnlineIds((prev) => {
        const has = prev.includes(studentId);
        if (online && !has) return [...prev, studentId];
        if (!online && has) return prev.filter((id) => id !== studentId);
        return prev;
      });
    });
  }, [students]);

  useEffect(() => {
    if (!studentPickerOpen || !searchQ.trim()) {
      if (!studentPickerOpen) setSearchHits([]);
      return;
    }
    const t = setTimeout(() => {
      apiExam.searchStudents(searchQ.trim()).then((r) => setSearchHits(r.data)).catch(console.error);
    }, 300);
    return () => clearTimeout(t);
  }, [searchQ, studentPickerOpen]);

  const addAppKeyword = (kw: string) => {
    const trimmed = kw.trim().toLowerCase();
    if (!trimmed) return;
    setAllowedApps((prev) => {
      const parts = prev.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean);
      if (parts.includes(trimmed)) return prev;
      return prev ? `${prev}, ${trimmed}` : trimmed;
    });
  };

  const applyTemplate = (keywords: string) => {
    setAllowedApps((prev) => mergeKeywordCSV(prev, keywords));
  };

  const saveRoom = async () => {
    setErr(''); setMsg('');
    setSaving(true);
    try {
      const payload: any = {};
      if (editable) {
        payload.name = roomName.trim();
        payload.startTime = localInputToISO(start);
        payload.endTime = localInputToISO(end);
        payload.allowedApps = allowedApps;
        payload.quizUrl = quizUrl.trim();
      } else {
        payload.allowedApps = allowedApps;
      }
      await apiExam.update(examId, payload);
      setMsg(editable ? 'Đã lưu cấu hình phòng thi' : 'Đã cập nhật danh sách ứng dụng được phép');
      await load();
    } catch (e: any) {
      setErr(e?.message || 'Lỗi');
    } finally {
      setSaving(false);
    }
  };

  const addStudent = async (studentRkId: number) => {
    await apiExam.addStudents(examId, [studentRkId]);
    await load();
    await fetchOnline();
  };

  const handleBulkSearch = async () => {
    const q = bulkInput.trim();
    if (!q) return;
    try {
      const res = await apiExam.searchStudents(q);
      setSearchHits(res.data);
      // Auto-select all unadded hits
      const enrolledIds = new Set(students.map((s) => s.studentRkId));
      const unaddedHits = res.data.filter((h) => !enrolledIds.has(h.studentRkId));
      setSelectedPickerRkIds(unaddedHits.map((h) => h.studentRkId));
    } catch (e: any) {
      alert(e?.message || 'Tìm kiếm thất bại');
    }
  };

  const handleSwitchTab = (mode: 'single' | 'bulk') => {
    setSearchMode(mode);
    setSearchHits([]);
    setSelectedPickerRkIds([]);
    setSearchQ('');
    setBulkInput('');
  };

  const toggleHitSelection = (studentRkId: number) => {
    setSelectedPickerRkIds((prev) =>
      prev.includes(studentRkId) ? prev.filter((id) => id !== studentRkId) : [...prev, studentRkId]
    );
  };

  const addSelectedStudents = async () => {
    if (selectedPickerRkIds.length === 0) return;
    try {
      await apiExam.addStudents(examId, selectedPickerRkIds);
      setSelectedPickerRkIds([]);
      setStudentPickerOpen(false);
      await load();
      await fetchOnline();
    } catch (e: any) {
      alert(e?.message || 'Thêm sinh viên thất bại');
    }
  };

  const toggleSelectAllHits = () => {
    const enrolledIds = new Set(students.map((s) => s.studentRkId));
    const unaddedHits = searchHits.filter((h) => !enrolledIds.has(h.studentRkId));
    const allUnaddedIds = unaddedHits.map((h) => h.studentRkId);
    if (allUnaddedIds.length === 0) return;

    const areAllSelected = allUnaddedIds.every((id) => selectedPickerRkIds.includes(id));
    if (areAllSelected) {
      setSelectedPickerRkIds((prev) => prev.filter((id) => !allUnaddedIds.includes(id)));
    } else {
      setSelectedPickerRkIds((prev) => Array.from(new Set([...prev, ...allUnaddedIds])));
    }
  };

  const createPackage = async () => {
    if (!packagePdf) {
      setErr('Chọn file PDF cho gói đề');
      return;
    }
    setErr('');
    setCreatingPackage(true);
    try {
      const title = paperTitle.trim() || packagePdf.name.replace(/\.pdf$/i, '') || 'Gói đề';
      const resCount = packageResources.length;
      await apiExam.uploadPackage(examId, title, packagePdf, packageResources);
      setPaperTitle('');
      setPackagePdf(null);
      setPackageResources([]);
      setMsg(`Đã tạo gói đề "${title}" (1 PDF${resCount ? ` + ${resCount} tài nguyên` : ''})`);
      await load();
    } catch (e: any) {
      setErr(e?.message || 'Tạo gói đề thất bại');
    } finally {
      setCreatingPackage(false);
    }
  };

  const uploadResource = async (paperId: number, e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    if (!files.length) return;
    await apiExam.uploadPackageResources(examId, paperId, files);
    e.target.value = '';
    await load();
  };

  const assignRandomPackages = async () => {
    const res = await apiExam.assignRandom(examId);
    setMsg(`Đã chia ${res.assigned} sinh viên vào ${res.packages} gói đề`);
    await load();
  };

  const sendPapers = async (scheduled: boolean) => {
    const payload = scheduled && sendSchedule
      ? { scheduledAt: localInputToISO(sendSchedule) }
      : undefined;
    const res = await apiExam.sendPapers(examId, payload);
    setMsg(`Đã gửi ${res.sent} gói đề${res.scheduled ? `, hẹn ${res.scheduled}` : ''}`);
    await load();
  };

  const downloadSub = async (subId: number, fileName: string) => {
    const res = await staffFetch(`/exam-rooms/${examId}/submissions/${subId}/download`);
    if (!res.ok) return;
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = fileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  const viewPaperPdf = async (paperId: number, title?: string) => {
    setErr('');
    const paperTitle = title || papers.find((p) => p.id === paperId)?.title || 'Đề thi';
    setPapersModalOpen(false);
    setPdfLoading(true);
    setPdfViewer({ title: paperTitle, bytes: null });
    try {
      const buf = await apiExam.fetchPaperPdfBytes(examId, paperId);
      setPdfViewer({ title: paperTitle, bytes: new Uint8Array(buf) });
    } catch (e: any) {
      closePdfViewer();
      setErr(e?.message || 'Không mở được đề PDF');
    } finally {
      setPdfLoading(false);
    }
  };

  const viewPaperResource = async (paperId: number, resourceId: number, fileName: string) => {
    setErr('');
    if (/\.pdf$/i.test(fileName)) {
      setPapersModalOpen(false);
      setPdfLoading(true);
      setPdfViewer({ title: fileName, bytes: null });
      try {
        const buf = await apiExam.fetchPaperResourceBytes(examId, paperId, resourceId);
        setPdfViewer({ title: fileName, bytes: new Uint8Array(buf) });
      } catch (e: any) {
        closePdfViewer();
        setErr(e?.message || 'Không mở được PDF');
      } finally {
        setPdfLoading(false);
      }
      return;
    }
    closeInlineResource();
    try {
      const buf = await apiExam.fetchPaperResourceBytes(examId, paperId, resourceId);
      const isImage = /\.(png|jpe?g|gif|webp)$/i.test(fileName);
      const mime = /\.png$/i.test(fileName) ? 'image/png'
        : /\.jpe?g$/i.test(fileName) ? 'image/jpeg'
        : /\.gif$/i.test(fileName) ? 'image/gif'
        : 'image/webp';
      const url = URL.createObjectURL(new Blob([buf], { type: mime }));
      setInlineResource({ url, title: fileName, isImage });
    } catch (e: any) {
      setErr(e?.message || 'Không mở được tài nguyên');
    }
  };

  const downloadPaperResource = async (paperId: number, resourceId: number, fileName: string) => {
    setErr('');
    try {
      await apiExam.downloadPaperFile(examId, paperId, { resourceId, fileName });
    } catch (e: any) {
      setErr(e?.message || 'Không tải được tài nguyên');
    }
  };

  const downloadAllSubs = async () => {
    setErr(''); setMsg('');
    setBundlingSubs(true);
    try {
      await apiExam.downloadAllSubmissions(examId, roomName || `phong_thi_${examId}`);
      setMsg('Đã tải ZIP gộp — giải nén sẽ thấy từng folder bài làm theo sinh viên.');
    } catch (e: any) {
      setErr(e?.message || 'Tải thất bại');
    } finally {
      setBundlingSubs(false);
    }
  };

  const publishToGit = async () => {
    if (!gh.connected) {
      setErr('Kết nối GitHub trong Tài khoản của tôi (sidebar dưới cùng) trước khi đẩy.');
      return;
    }
    setErr(''); setMsg('');
    setPublishingGit(true);
    try {
      const res = await apiExam.publishSubmissionsGit(examId);
      const openUrl = res.openUrl || res.gitPublishUrl || res.url;
      setGitPublishUrl(openUrl);
      setMsg(res.message || 'Đã đẩy bài nộp lên GitHub');
      if (openUrl) window.open(openUrl, '_blank', 'noopener,noreferrer');
    } catch (e: any) {
      setErr(e?.message || 'Đẩy Git thất bại');
    } finally {
      setPublishingGit(false);
    }
  };

  const publishToGitStudents = async () => {
    if (!gh.connected) {
      setErr('Kết nối GitHub trong Tài khoản của tôi trước khi đẩy.');
      return;
    }
    setErr(''); setMsg('');
    setPublishingGitStudents(true);
    try {
      const res = await apiExam.publishSubmissionsGitStudents(examId);
      setMsg(res.message || 'Đã tạo repo từng sinh viên');
      if (res.openUrl) window.open(res.openUrl, '_blank', 'noopener,noreferrer');
      const subs = await apiExam.listSubmissions(examId);
      setSubmissions(subs.data);
    } catch (e: any) {
      setErr(e?.message || 'Đẩy Git thất bại');
    } finally {
      setPublishingGitStudents(false);
    }
  };

  const handlePublish = async () => {
    if (!quizUrl.trim()) {
      const ok = confirm(
        'Phòng thi chưa có link trắc nghiệm.\n\nBạn có chắc chắn muốn đẩy phòng thi không có phần thi trắc nghiệm không?'
      );
      if (!ok) return;
    }
    setErr(''); setMsg('');
    try {
      await apiExam.publish(examId);
      setMsg('Đã đẩy phòng thi — hệ thống sẽ mở khi tới giờ');
      await load();
    } catch (e: any) {
      setErr(e?.message || 'Lỗi');
    }
  };

  const handleUnpublish = async () => {
    if (!confirm('Thu hồi lệnh đẩy? Phòng thi quay về trạng thái tạm thời.')) return;
    setErr(''); setMsg('');
    try {
      await apiExam.unpublish(examId);
      setMsg('Đã thu hồi — phòng thi ở trạng thái tạm thời');
      await load();
    } catch (e: any) {
      setErr(e?.message || 'Lỗi');
    }
  };

  const handleCancel = async () => {
    if (!confirm('Hủy phòng thi ngay? Sinh viên sẽ thoát chế độ thi.')) return;
    setErr(''); setMsg('');
    try {
      await apiExam.cancel(examId);
      setMsg('Đã hủy phòng thi');
      await load();
    } catch (e: any) {
      setErr(e?.message || 'Lỗi');
    }
  };

  const filteredStudents = students.filter((s) => {
    const term = searchQuery.toLowerCase().trim();
    if (!term) return true;
    return (
      s.fullName.toLowerCase().includes(term) ||
      s.studentCode.toLowerCase().includes(term) ||
      s.email.toLowerCase().includes(term) ||
      (s.paperTitle || '').toLowerCase().includes(term)
    );
  });

  const toStudentItem = (s: ExamRoomStudent): StudentItem => ({
    id: s.id,
    rkId: s.studentRkId,
    studentCode: s.studentCode,
    fullName: s.fullName,
    email: s.email,
    avatar: s.avatar,
  });

  const openStudentChat = (s: ExamRoomStudent) => {
    openStaffChat({
      studentRkId: s.studentRkId,
      fullName: s.fullName,
      studentCode: s.studentCode,
      email: s.email,
    });
  };

  if (loading && !roomName) {
    return (
      <div className="loading-screen">
        <div className="sync-spinner" style={{ width: '36px', height: '36px', borderWidth: '3px' }} />
        <div style={{ fontWeight: 600, fontSize: '0.95rem', color: 'var(--text-secondary)' }}>
          Đang tải không gian làm việc phòng thi...
        </div>
      </div>
    );
  }

  const badge = statusBadge(displayStatus);
  const enrolledIds = new Set(students.map((s) => s.studentRkId));
  const onlineCount = students.filter((s) => onlineIds.includes(s.studentRkId)).length;

  return (
    <div className="workspace-layout workspace-embedded">
      <NavHistoryBar classLabel={roomName || 'Phòng thi'} onBack={onBack} />

      {(msg || err) && (
        <div className={`alert-banner ${err ? 'alert-error' : 'alert-success'}`}>{err || msg}</div>
      )}

      <div className="workspace-header">
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
            <div className="brand-logo" style={{ width: 44, height: 44, fontSize: '1.1rem' }}>📝</div>
            <div>
              <h1 className="workspace-class-title">{roomName || 'Phòng thi'}</h1>
              <div className="class-badge-container">
                <span className={`badge ${badge.cls}`}>{badge.text}</span>
                {start && end && (
                  <span className="badge badge-muted">
                    {fmtTime(localInputToISO(start))} → {fmtTime(localInputToISO(end))}
                  </span>
                )}
                <span className="badge badge-info">ID: {examId}</span>
                <span className="badge badge-muted">{papers.length} gói đề · {submissions.length} bài nộp</span>
              </div>
            </div>
          </div>
        </div>

        <div style={{ display: 'flex', alignItems: 'center', gap: '1.25rem', flexWrap: 'wrap' }}>
          <div className="status-panel">
            <div style={{ display: 'flex', flexDirection: 'column' }}>
              <span style={{ fontSize: '0.72rem', color: 'var(--text-muted)', fontWeight: 600, textTransform: 'uppercase' }}>
                Trạng thái thi
              </span>
              <span style={{ fontSize: '0.875rem', fontWeight: 700, color: statusColor(displayStatus) }}>
                {badge.text}
              </span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
              {canPublish && (
                <button type="button" className="btn btn-primary btn-sm" onClick={handlePublish}>Đẩy phòng thi</button>
              )}
              {canUnpublish && (
                <button type="button" className="btn btn-secondary btn-sm" onClick={handleUnpublish}>Thu hồi</button>
              )}
              {canCancel && (
                <button type="button" className="btn btn-secondary btn-sm learning-btn-danger" onClick={handleCancel}>Hủy</button>
              )}
              {papers.length > 0 && (
                <button type="button" className="btn btn-secondary btn-sm" onClick={() => setPapersModalOpen(true)}>
                  📄 Xem đề
                </button>
              )}
            </div>
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
            <div style={{ fontSize: '0.72rem', color: 'var(--text-muted)', fontWeight: 600, textTransform: 'uppercase' }}>
              Online / Sĩ số
            </div>
            <div className="workspace-stat-value">
              <span style={{ color: 'var(--success)' }}>{onlineCount}</span>
              <span style={{ color: 'var(--text-muted)', fontSize: '0.9rem', fontWeight: 500 }}> / {students.length} SV</span>
            </div>
          </div>
        </div>
      </div>

      <div className="workspace-main">
        {configOpen && (
          <div className="workspace-drawer-backdrop" onClick={() => setConfigOpen(false)} aria-hidden />
        )}

        <aside className={`workspace-config-drawer ${configOpen ? 'open' : ''}`}>
          <div className="workspace-drawer-header">
            <strong>Cấu hình phòng thi</strong>
            <button type="button" className="btn btn-secondary workspace-drawer-close" onClick={() => setConfigOpen(false)} aria-label="Đóng">×</button>
          </div>
          <div className="workspace-drawer-tabs">
            <button type="button" className={`workspace-drawer-tab ${configTab === 'info' ? 'active' : ''}`} onClick={() => setConfigTab('info')}>
              Thông tin
            </button>
            <button type="button" className={`workspace-drawer-tab ${configTab === 'apps' ? 'active' : ''}`} onClick={() => setConfigTab('apps')}>
              Ứng dụng
            </button>
            <button type="button" className={`workspace-drawer-tab ${configTab === 'papers' ? 'active' : ''}`} onClick={() => setConfigTab('papers')}>
              Gói đề & gửi
            </button>
          </div>
          <div className="workspace-drawer-body">
            {configTab === 'info' && (
              <div className="config-card config-card-flat">
                <div className="card-header-title">Thông tin phòng thi</div>
                <p className="config-card-desc">
                  {editable ? 'Chỉnh sửa được khi phòng ở trạng thái tạm thời.' : 'Phòng đã khóa chỉnh sửa.'}
                </p>
                <label className="login-field">
                  <span>Tên phòng thi</span>
                  <input value={roomName} onChange={(e) => setRoomName(e.target.value)} disabled={!editable} />
                </label>
                <label className="login-field">
                  <span>Link trắc nghiệm</span>
                  <input value={quizUrl} onChange={(e) => setQuizUrl(e.target.value)} placeholder="https://..." disabled={!editable} />
                </label>
                <div className="exam-git-settings">
                  <p className="exam-git-status-line">
                    GitHub:{' '}
                    {gh.connected ? (
                      <strong>@{gh.githubLogin}</strong>
                    ) : (
                      <>
                        <span className="exam-git-not-connected">chưa kết nối</span>
                        {' — '}
                        <button type="button" className="link-btn" onClick={() => navigate('profile')}>
                          Kết nối trong Tài khoản
                        </button>
                      </>
                    )}
                  </p>
                  <p className="exam-git-hint">
                    Bấm <strong>Đẩy lên Git</strong> sẽ tự tạo repo theo tên phòng thi — mỗi sinh viên một folder, giống ZIP tải về.
                  </p>
                  {gitPublishUrl && (
                    <p className="exam-git-last">
                      Repo gần nhất:{' '}
                      <a href={gitPublishUrl} target="_blank" rel="noreferrer">{gitPublishUrl}</a>
                    </p>
                  )}
                </div>
                <label className="login-field">
                  <span>Bắt đầu</span>
                  <input type="datetime-local" value={start} onChange={(e) => setStart(e.target.value)} disabled={!editable} />
                </label>
                <label className="login-field">
                  <span>Kết thúc</span>
                  <input type="datetime-local" value={end} onChange={(e) => setEnd(e.target.value)} disabled={!editable} />
                </label>
                {editable && (
                  <button type="button" className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={saving} onClick={saveRoom}>
                    {saving ? 'Đang lưu...' : 'Lưu thông tin'}
                  </button>
                )}
              </div>
            )}

            {configTab === 'apps' && (
              <div className="config-card config-card-flat">
                <div className="card-header-title">Ứng dụng được phép (khi thi)</div>
                <p className="config-card-desc">
                  Từ khóa tiến trình hoặc tiêu đề được phép (ngăn cách bằng dấu phẩy).
                </p>
                {!editable && canEditApps && (
                  <p className="config-card-desc" style={{ color: 'var(--success)', fontWeight: 600, display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
                    <span>💡</span> Phòng thi đã đẩy hoặc đang diễn ra, bạn vẫn có thể thay đổi danh sách ứng dụng được phép bên dưới.
                  </p>
                )}
                <textarea
                  className="app-textarea"
                  placeholder="chrome, msedge, acrobat, wails, simple_care, client"
                  value={allowedApps}
                  onChange={(e) => setAllowedApps(e.target.value)}
                  disabled={!canEditApps}
                />
                {canEditApps && (
                  <>
                    <div className="config-suggestions">
                      <span className="config-suggestions-label">Gợi ý nhanh:</span>
                      <div className="config-suggestions-list">
                        {EXAM_APP_SUGGESTIONS.map((kw) => (
                          <span key={kw} className="app-suggestion-badge" onClick={() => addAppKeyword(kw)}>
                            + {kw}
                          </span>
                        ))}
                      </div>
                    </div>
                    <div className="app-pool-open-row">
                      <div>
                        <span className="config-suggestions-label">Kho & khung ứng dụng</span>
                        <p className="discovered-apps-hint" style={{ margin: '0.25rem 0 0' }}>
                          Thêm từng app trong kho hoặc áp dụng cả bộ keyword đã lưu.
                        </p>
                      </div>
                      <div className="app-pool-open-actions">
                        <button type="button" className="btn btn-secondary app-pool-open-btn" onClick={() => setTemplatePickerOpen(true)}>
                          Chọn khung
                        </button>
                        <button type="button" className="btn btn-secondary app-pool-open-btn" onClick={() => setAppPoolOpen(true)}>
                          Mở kho
                        </button>
                      </div>
                    </div>
                    <button type="button" className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={saving} onClick={saveRoom}>
                      {saving ? 'Đang lưu...' : 'Lưu cấu hình apps'}
                    </button>
                  </>
                )}
              </div>
            )}

            {configTab === 'papers' && (
              <div className="config-card config-card-flat">
                <div className="card-header-title">Gói đề & gửi cho sinh viên</div>
                <p className="config-card-desc">
                  Mỗi gói đề gồm <strong>1 file PDF</strong> (đề chính) và <strong>tài nguyên kèm</strong> (ảnh, zip, doc…). Chia ngẫu nhiên sẽ gán cả gói cho từng sinh viên.
                </p>
                {!canModifyPapers && (
                  <p className="config-card-desc" style={{ color: 'var(--danger)' }}>
                    Không thể thay đổi gói đề — đề thi đã được gửi cho sinh viên hoặc phòng thi đã kết thúc.
                  </p>
                )}
                {canModifyPapers && (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: '0.65rem', marginBottom: '0.75rem' }}>
                    <input
                      className="search-input"
                      placeholder="Tên gói đề (VD: Gói A)"
                      value={paperTitle}
                      onChange={(e) => setPaperTitle(e.target.value)}
                    />
                    <label className="login-field">
                      <span>PDF đề chính (bắt buộc)</span>
                      <input
                        type="file"
                        accept=".pdf"
                        onChange={(e) => setPackagePdf(e.target.files?.[0] || null)}
                      />
                    </label>
                    <label className="login-field">
                      <span>Tài nguyên kèm (tùy chọn, many files)</span>
                      <input
                        type="file"
                        multiple
                        onChange={(e) => setPackageResources(Array.from(e.target.files || []))}
                      />
                    </label>
                    {packageResources.length > 0 && (
                      <p className="config-card-desc" style={{ margin: 0 }}>
                        {packageResources.length} tài nguyên: {packageResources.map((f) => f.name).join(', ')}
                      </p>
                    )}
                    <button
                      type="button"
                      className="btn btn-primary"
                      style={{ width: '100%' }}
                      disabled={creatingPackage || !packagePdf}
                      onClick={createPackage}
                    >
                      {creatingPackage ? 'Đang tạo...' : 'Tạo gói đề'}
                    </button>
                  </div>
                )}
                {papers.length === 0 ? (
                  <p className="config-card-desc">Chưa có gói đề. Tạo gói gồm PDF + tài nguyên.</p>
                ) : (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: '0.65rem' }}>
                    {papers.map((p) => (
                      <div key={p.id} style={{ border: '1px solid var(--border-light)', borderRadius: '8px', padding: '0.75rem', background: 'var(--bg-subtle)' }}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', gap: '0.5rem', alignItems: 'flex-start', flexWrap: 'wrap' }}>
                          <strong>{p.title}</strong>
                          <div style={{ display: 'flex', gap: '0.35rem', alignItems: 'center' }}>
                            <button type="button" className="btn btn-ghost btn-sm" onClick={() => viewPaperPdf(p.id)}>Xem PDF</button>
                            <span className="badge badge-muted">Gói đề</span>
                          </div>
                        </div>
                        <ul className="config-card-desc" style={{ margin: '0.5rem 0 0', paddingLeft: '1.1rem' }}>
                          <li>📄 main.pdf (đề chính)</li>
                          {(p.resources || []).length === 0 ? (
                            <li style={{ color: 'var(--text-muted)' }}>Chưa có tài nguyên kèm</li>
                          ) : (
                            (p.resources || []).map((r) => (
                              <li key={r.id}>
                                📎 {r.fileName}{' '}
                                <button type="button" className="link-btn" onClick={() => downloadPaperResource(p.id, r.id, r.fileName)}>
                                  Tải
                                </button>
                              </li>
                            ))
                          )}
                        </ul>
                        {canModifyPapers && (
                          <div style={{ display: 'flex', gap: '0.35rem', marginTop: '0.5rem', flexWrap: 'wrap' }}>
                            <label className="btn btn-ghost btn-sm" style={{ cursor: 'pointer' }}>
                              + Tài nguyên
                              <input type="file" multiple hidden onChange={(e) => uploadResource(p.id, e)} />
                            </label>
                            <button type="button" className="btn btn-ghost btn-sm learning-btn-danger" onClick={() => apiExam.deletePaper(examId, p.id).then(load)}>
                              Xóa gói
                            </button>
                          </div>
                        )}
                      </div>
                    ))}
                  </div>
                )}
                {papers.length > 0 && students.length > 0 && canModifyPapers && (
                  <button type="button" className="btn btn-secondary" style={{ width: '100%', marginTop: '0.65rem' }} onClick={assignRandomPackages}>
                    Chia gói đề ngẫu nhiên
                  </button>
                )}
                <div className="divider" style={{ opacity: 0.3 }} />
                <p className="config-card-desc">Gửi gói đề sau khi đẩy phòng thi và chia gói.</p>
                {(displayStatus === 'ready' || displayStatus === 'active') ? (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                    <button type="button" className="btn btn-primary" onClick={() => sendPapers(false)}>Gửi gói đề ngay</button>
                    <input type="datetime-local" className="search-input" value={sendSchedule} onChange={(e) => setSendSchedule(e.target.value)} />
                    <button type="button" className="btn btn-secondary" disabled={!sendSchedule} onClick={() => sendPapers(true)}>Hẹn giờ gửi</button>
                  </div>
                ) : (
                  <p className="config-card-desc">Đẩy phòng thi trước khi gửi gói đề.</p>
                )}
              </div>
            )}
          </div>
        </aside>

        <div className="right-panel workspace-content-panel">
          <div className="workspace-panel-toolbar">
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '1rem' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: '0.65rem', flexWrap: 'wrap' }}>
                <button
                  type="button"
                  className={`btn btn-secondary workspace-config-toggle ${configOpen ? 'active' : ''}`}
                  onClick={() => setConfigOpen((v) => !v)}
                >
                  {configOpen ? 'Ẩn cấu hình' : 'Cấu hình phòng thi'}
                </button>
                <div className="tab-btn-group">
                  <button
                    type="button"
                    className={`tab-sub-btn ${activeSubTab === 'roster' ? 'active' : ''}`}
                    onClick={() => setActiveSubTab('roster')}
                  >
                    Sơ đồ sinh viên
                  </button>
                  <button
                    type="button"
                    className={`tab-sub-btn ${activeSubTab === 'grid' ? 'active' : ''}`}
                    onClick={() => setActiveSubTab('grid')}
                  >
                    Giám sát camera 🖥️
                  </button>
                  <button
                    type="button"
                    className={`tab-sub-btn ${activeSubTab === 'detail' ? 'active' : ''}`}
                    onClick={() => setActiveSubTab('detail')}
                  >
                    Chi tiết thi
                  </button>
                  <button
                    type="button"
                    className={`tab-sub-btn ${activeSubTab === 'submissions' ? 'active' : ''}`}
                    onClick={() => setActiveSubTab('submissions')}
                  >
                    Bài nộp ({submissions.length})
                  </button>
                  <button
                    type="button"
                    className={`tab-sub-btn ${activeSubTab === 'violations' ? 'active' : ''}`}
                    onClick={() => setActiveSubTab('violations')}
                  >
                    Vi phạm
                  </button>
                </div>
              </div>

              <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap' }}>
                {activeSubTab !== 'submissions' && activeSubTab !== 'grid' && activeSubTab !== 'violations' && (
                  <div className="search-input-wrapper">
                    <input
                      type="text"
                      className="search-input"
                      placeholder="Tìm sinh viên..."
                      value={searchQuery}
                      onChange={(e) => setSearchQuery(e.target.value)}
                    />
                    <span className="search-icon">🔍</span>
                  </div>
                )}
                {prepEditable && (
                  <button
                    type="button"
                    className="btn btn-primary btn-sm"
                    onClick={() => { setStudentPickerOpen(true); setSearchQ(''); setSearchHits([]); setSelectedPickerRkIds([]); }}
                  >
                    + Thêm sinh viên
                  </button>
                )}
                {papers.length > 0 && (
                  <button type="button" className="btn btn-secondary btn-sm" onClick={() => setPapersModalOpen(true)}>
                    📄 Xem đề ({papers.length})
                  </button>
                )}
              </div>
            </div>
            <div className="divider" style={{ opacity: 0.3, margin: '0.25rem 0' }} />
          </div>

          <div className="workspace-panel-body workspace-panel-fill">
            {activeSubTab === 'roster' ? (
              <WorkspaceSeatingChart
                workspaceId={`exam-${examId}`}
                students={students.map((s) => ({
                  rkId: s.studentRkId,
                  fullName: s.fullName,
                  studentCode: s.studentCode,
                  avatar: s.avatar,
                  paperTitle: s.paperTitle,
                }))}
                onlineIds={onlineIds}
                onStudentClick={(rkId) => {
                  const orig = students.find((s) => s.studentRkId === rkId);
                  if (orig) setSelectedStudent(toStudentItem(orig));
                }}
              />
            ) : activeSubTab === 'grid' ? (
              <ExamGridProctor
                students={students}
                onlineIds={onlineIds}
                onSelectStudent={(s) => setSelectedStudent(toStudentItem(s))}
              />
            ) : activeSubTab === 'detail' ? (
              <div className="session-logs-panel">
                <div className="attendance-table-scroll table-wrapper" style={{ border: 'none' }}>
                  {students.length === 0 ? (
                    <div className="empty-state" style={{ minHeight: '300px' }}>
                      <div className="empty-state-icon">📋</div>
                      <h2>Chưa có sinh viên</h2>
                      <p>Thêm sinh viên để xem chi tiết đề gán và trạng thái nộp bài.</p>
                    </div>
                  ) : (
                    <table className="data-table">
                      <thead>
                        <tr>
                          <th>Sinh viên</th>
                          <th>Mã SV</th>
                          <th>Online</th>
                          <th>Gói đề</th>
                          <th>Gửi gói</th>
                          <th>Nộp bài</th>
                          <th style={{ textAlign: 'right' }}>Thao tác</th>
                        </tr>
                      </thead>
                      <tbody>
                        {filteredStudents.map((s) => {
                          const isOnline = onlineIds.includes(s.studentRkId);
                          return (
                            <tr key={s.id}>
                              <td>
                                <div style={{ fontWeight: 600, color: 'var(--text-primary)' }}>{s.fullName}</div>
                                <div style={{ fontSize: '0.75rem', color: 'var(--text-secondary)' }}>{s.email}</div>
                              </td>
                              <td><code>{s.studentCode}</code></td>
                              <td style={{ color: isOnline ? 'var(--success)' : 'var(--text-muted)', fontWeight: 700 }}>
                                {isOnline ? 'Online' : 'Offline'}
                              </td>
                              <td>
                                {s.paperTitle || '—'}
                                {s.assignedPaperId && (
                                  <button
                                    type="button"
                                    className="link-btn"
                                    style={{ marginLeft: '0.35rem' }}
                                    onClick={() => viewPaperPdf(s.assignedPaperId!)}
                                  >
                                    Xem đề
                                  </button>
                                )}
                              </td>
                              <td>{s.paperSentAt ? fmtTime(s.paperSentAt) : s.paperScheduledAt ? `Hẹn ${fmtTime(s.paperScheduledAt)}` : '—'}</td>
                              <td>{s.submitted ? <span className="badge badge-success">Đã nộp</span> : '—'}</td>
                              <td style={{ textAlign: 'right', whiteSpace: 'nowrap' }}>
                                <button type="button" className="btn btn-primary btn-sm" onClick={() => setSelectedStudent(toStudentItem(s))}>
                                  Giám sát
                                </button>
                                <button type="button" className="btn btn-secondary btn-sm" style={{ marginLeft: '0.35rem' }} onClick={() => openStudentChat(s)}>
                                  Nhắn tin
                                </button>
                                {prepEditable && (
                                  <button
                                    type="button"
                                    className="btn btn-secondary btn-sm learning-btn-danger"
                                    style={{ marginLeft: '0.35rem' }}
                                    onClick={() => apiExam.removeStudent(examId, s.studentRkId).then(load)}
                                  >
                                    Xóa
                                  </button>
                                )}
                              </td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  )}
                </div>
              </div>
            ) : activeSubTab === 'violations' ? (
              <ViolationsPanel mode="exam" examId={examId} />
            ) : (
              <div className="session-logs-panel">
                {submissions.length > 0 && (
                  <div className="exam-submissions-toolbar">
                    {papers.length > 0 && (
                      <button
                        type="button"
                        className="btn btn-secondary btn-sm"
                        onClick={() => setPapersModalOpen(true)}
                      >
                        📄 Xem đề phòng thi
                      </button>
                    )}
                    <button
                      type="button"
                      className="btn btn-primary btn-sm"
                      disabled={bundlingSubs}
                      onClick={downloadAllSubs}
                    >
                      {bundlingSubs ? 'Đang gói ZIP...' : '📦 Tải tất cả (ZIP gộp)'}
                    </button>
                    <button
                      type="button"
                      className="btn btn-secondary btn-sm"
                      disabled={publishingGit || !gh.connected}
                      onClick={publishToGit}
                      title="1 repo — mỗi sinh viên một folder"
                    >
                      {publishingGit ? 'Đang đẩy...' : '🔗 Git gộp (1 repo)'}
                    </button>
                    <button
                      type="button"
                      className="btn btn-secondary btn-sm"
                      disabled={publishingGitStudents || !gh.connected}
                      onClick={publishToGitStudents}
                      title="Mỗi sinh viên một repo GitHub riêng"
                    >
                      {publishingGitStudents ? 'Đang tạo repo...' : '👤 Git từng SV'}
                    </button>
                    {!gh.connected && (
                      <span className="exam-submissions-toolbar-hint">
                        <button type="button" className="link-btn" onClick={() => navigate('profile')}>
                          Kết nối GitHub
                        </button>
                        {' '}trong Tài khoản của tôi trước khi đẩy.
                      </span>
                    )}
                    <span className="exam-submissions-toolbar-hint">
                      <strong>Git gộp</strong>: 1 repo, folder từng SV · <strong>Git từng SV</strong>: mỗi người 1 repo.
                    </span>
                  </div>
                )}
                <div className="attendance-table-scroll table-wrapper" style={{ border: 'none' }}>
                  {submissions.length === 0 ? (
                    <div className="empty-state" style={{ minHeight: '300px' }}>
                      <div className="empty-state-icon">📦</div>
                      <h2>Chưa có bài nộp</h2>
                      <p>Sinh viên nộp bài qua app Simple Care sẽ hiện tại đây.</p>
                    </div>
                  ) : (
                    <table className="data-table">
                      <thead>
                        <tr>
                          <th>Sinh viên</th>
                          <th>File</th>
                          <th>Git</th>
                          <th>Thời gian</th>
                          <th style={{ textAlign: 'right' }}>Tải</th>
                        </tr>
                      </thead>
                      <tbody>
                        {submissions.map((s) => (
                          <tr key={s.id}>
                            <td>
                              <div style={{ fontWeight: 600 }}>{s.fullName}</div>
                              <div style={{ fontSize: '0.75rem', color: 'var(--text-secondary)' }}>{s.studentCode}</div>
                            </td>
                            <td style={{ fontSize: '0.85rem' }}>{s.fileName}</td>
                            <td style={{ fontSize: '0.8rem' }}>
                              {s.gitPublishUrl ? (
                                <a href={s.gitPublishUrl} target="_blank" rel="noreferrer">Repo</a>
                              ) : '—'}
                            </td>
                            <td style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>{fmtTime(s.createdAt)}</td>
                            <td style={{ textAlign: 'right' }}>
                              <button type="button" className="btn btn-secondary btn-sm" onClick={() => downloadSub(s.id, s.fileName)}>Tải</button>
                            </td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  )}
                </div>
              </div>
            )}
          </div>
        </div>
      </div>

      {pdfViewer && (
        <ExamPaperPdfModal
          title={pdfViewer.title}
          bytes={pdfViewer.bytes}
          loading={pdfLoading}
          onClose={closePdfViewer}
        />
      )}

      {inlineResource && (
        <div className="modal-overlay exam-pdf-viewer-overlay" onClick={closeInlineResource}>
          <div className="exam-pdf-viewer-modal exam-inline-resource-modal" onClick={(e) => e.stopPropagation()}>
            <div className="modal-header exam-pdf-viewer-header">
              <h2 className="modal-title">{inlineResource.title}</h2>
              <button type="button" className="modal-close-btn" onClick={closeInlineResource} aria-label="Đóng">×</button>
            </div>
            <div className="exam-inline-resource-body">
              {inlineResource.isImage ? (
                <img src={inlineResource.url} alt={inlineResource.title} className="exam-inline-resource-image" />
              ) : (
                <iframe src={inlineResource.url} title={inlineResource.title} className="exam-pdf-viewer-frame" />
              )}
            </div>
          </div>
        </div>
      )}

      {papersModalOpen && (
        <div className="modal-overlay" onClick={() => setPapersModalOpen(false)}>
          <div className="modal-container exam-papers-view-modal" onClick={(e) => e.stopPropagation()}>
            <div className="modal-header">
              <div>
                <h2 className="modal-title">Đề thi — {roomName}</h2>
                <p className="exam-student-modal-desc">{papers.length} gói đề trong phòng thi</p>
              </div>
              <button type="button" className="modal-close-btn" onClick={() => setPapersModalOpen(false)} aria-label="Đóng">×</button>
            </div>
            <div className="exam-papers-view-body">
              {papers.map((p) => (
                <div key={p.id} className="exam-paper-view-card">
                  <div className="exam-paper-view-head">
                    <strong>{p.title}</strong>
                    <button type="button" className="btn btn-primary btn-sm" onClick={(e) => { e.preventDefault(); void viewPaperPdf(p.id); }}>
                      Xem PDF đề
                    </button>
                  </div>
                  <ul className="exam-paper-view-resources">
                    <li>📄 Đề chính (PDF)</li>
                    {(p.resources || []).length === 0 ? (
                      <li className="text-muted">Không có tài nguyên kèm</li>
                    ) : (
                      (p.resources || []).map((r) => (
                        <li key={r.id}>
                          📎 {r.fileName}
                          <span className="exam-paper-view-actions">
                            {/\.(pdf|png|jpe?g|gif|webp|txt)$/i.test(r.fileName) && (
                              <button type="button" className="link-btn" onClick={() => viewPaperResource(p.id, r.id, r.fileName)}>
                                Xem
                              </button>
                            )}
                            <button type="button" className="link-btn" onClick={() => downloadPaperResource(p.id, r.id, r.fileName)}>
                              Tải
                            </button>
                          </span>
                        </li>
                      ))
                    )}
                  </ul>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}

      {selectedStudent && (
        <StudentDetailModal
          student={selectedStudent}
          isOnline={onlineIds.includes(selectedStudent.rkId)}
          onClose={() => setSelectedStudent(null)}
        />
      )}

      {studentPickerOpen && (
        <div className="modal-overlay exam-student-overlay" onClick={() => setStudentPickerOpen(false)}>
          <div className="modal-container exam-student-modal" onClick={(e) => e.stopPropagation()}>
            <div className="modal-header exam-student-modal-header">
              <div>
                <h2 className="modal-title">Thêm sinh viên</h2>
                <p className="exam-student-modal-desc">Tìm theo mã lớp, họ tên, mã sinh viên</p>
              </div>
              <button type="button" className="modal-close-btn" onClick={() => setStudentPickerOpen(false)} aria-label="Đóng">
                ×
              </button>
            </div>
            <div className="exam-student-modal-body">
              <div className="tab-btn-group" style={{ marginBottom: '1rem' }}>
                <button
                  type="button"
                  className={`tab-sub-btn ${searchMode === 'single' ? 'active' : ''}`}
                  onClick={() => handleSwitchTab('single')}
                >
                  Tìm từng bạn
                </button>
                <button
                  type="button"
                  className={`tab-sub-btn ${searchMode === 'bulk' ? 'active' : ''}`}
                  onClick={() => handleSwitchTab('bulk')}
                >
                  Tìm hàng loạt
                </button>
              </div>

              {searchMode === 'single' ? (
                <div className="exam-student-modal-search search-input-wrap" style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
                  <input
                    type="search"
                    className="search-input"
                    placeholder="Tìm sinh viên..."
                    value={searchQ}
                    onChange={(e) => {
                      setSearchQ(e.target.value);
                      setSelectedPickerRkIds([]);
                    }}
                    autoFocus
                    style={{ flex: 1 }}
                  />
                  {searchQ.trim() !== '' && searchHits.some((h) => !enrolledIds.has(h.studentRkId)) && (
                    <button
                      type="button"
                      className="btn btn-secondary btn-sm"
                      onClick={toggleSelectAllHits}
                      style={{ whiteSpace: 'nowrap' }}
                    >
                      {searchHits.filter((h) => !enrolledIds.has(h.studentRkId)).every((h) => selectedPickerRkIds.includes(h.studentRkId))
                        ? 'Bỏ chọn tất cả'
                        : 'Chọn tất cả'}
                    </button>
                  )}
                </div>
              ) : (
                <div className="exam-student-modal-search" style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
                  <textarea
                    className="search-input"
                    placeholder="Nhập hoặc paste danh sách sinh viên tại đây (Họ tên, mã sinh viên, email...).&#10;Mỗi dòng một bạn, hoặc ngăn cách bằng dấu phẩy (,) hoặc chấm phẩy (;)."
                    value={bulkInput}
                    onChange={(e) => setBulkInput(e.target.value)}
                    style={{
                      width: '100%',
                      height: '100px',
                      resize: 'vertical',
                      padding: '0.5rem 0.75rem',
                      fontFamily: 'inherit',
                      fontSize: '0.9rem',
                      lineHeight: '1.4'
                    }}
                    autoFocus
                  />
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: '0.25rem' }}>
                    <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>
                      Mẹo: Có thể copy một cột Excel dán trực tiếp vào đây.
                    </span>
                    <div style={{ display: 'flex', gap: '0.5rem' }}>
                      {searchHits.length > 0 && searchHits.some((h) => !enrolledIds.has(h.studentRkId)) && (
                        <button
                          type="button"
                          className="btn btn-secondary btn-sm"
                          onClick={toggleSelectAllHits}
                        >
                          {searchHits.filter((h) => !enrolledIds.has(h.studentRkId)).every((h) => selectedPickerRkIds.includes(h.studentRkId))
                            ? 'Bỏ chọn tất cả'
                            : 'Chọn tất cả'}
                        </button>
                      )}
                      <button
                        type="button"
                        className="btn btn-primary btn-sm"
                        onClick={handleBulkSearch}
                        disabled={!bulkInput.trim()}
                      >
                        Tìm hàng loạt
                      </button>
                    </div>
                  </div>
                </div>
              )}

              <div className="exam-student-hits">
                {searchMode === 'single' && searchQ.trim() === '' ? (
                  <div className="exam-student-hits-empty">
                    <span>🔍</span>
                    <p>Gõ ít nhất vài ký tự để tìm sinh viên.</p>
                  </div>
                ) : searchMode === 'bulk' && searchHits.length === 0 ? (
                  <div className="exam-student-hits-empty">
                    <span>📋</span>
                    <p>Paste danh sách và bấm "Tìm hàng loạt" để hiển thị kết quả.</p>
                  </div>
                ) : searchHits.length === 0 ? (
                  <div className="exam-student-hits-empty">
                    <span>📭</span>
                    <p>Không tìm thấy sinh viên phù hợp.</p>
                  </div>
                ) : (
                  searchHits.map((h) => {
                    const added = enrolledIds.has(h.studentRkId);
                    const isSelected = selectedPickerRkIds.includes(h.studentRkId);
                    const initials = h.fullName
                      .split(/\s+/)
                      .filter(Boolean)
                      .slice(-2)
                      .map((w) => w[0]?.toUpperCase() || '')
                      .join('');
                    return (
                      <div
                        key={h.studentRkId}
                        className={`exam-student-hit ${added ? 'exam-student-hit--added' : ''} ${isSelected ? 'exam-student-hit--selected' : ''}`}
                        style={{ cursor: added ? 'default' : 'pointer' }}
                        onClick={() => {
                          if (!added) toggleHitSelection(h.studentRkId);
                        }}
                      >
                        <div
                          style={{ display: 'flex', alignItems: 'center' }}
                          onClick={(e) => e.stopPropagation()}
                        >
                          <input
                            type="checkbox"
                            checked={added || isSelected}
                            disabled={added}
                            onChange={() => {
                              if (!added) toggleHitSelection(h.studentRkId);
                            }}
                          />
                        </div>
                        <div className="exam-student-hit-avatar" aria-hidden="true">{initials || '?'}</div>
                        <div className="exam-student-hit-info">
                          <strong>{h.fullName}</strong>
                          <div className="exam-student-hit-meta">
                            Mã SV: <code>{h.studentCode}</code>
                            {h.classCodes ? <> · Lớp: {h.classCodes}</> : null}
                            {h.email ? <> · {h.email}</> : null}
                          </div>
                        </div>
                        <button
                          type="button"
                          className={`btn btn-sm ${added ? 'btn-secondary' : 'btn-primary'}`}
                          disabled={added}
                          onClick={(e) => {
                            e.stopPropagation();
                            addStudent(h.studentRkId);
                          }}
                        >
                          {added ? '✓ Đã thêm' : '+ Thêm'}
                        </button>
                      </div>
                    );
                  })
                )}
              </div>
            </div>
            <div className="modal-footer exam-student-modal-footer" style={{
              display: 'flex',
              justifyContent: 'flex-end',
              gap: '0.75rem',
              padding: '0.85rem 1.25rem',
              borderTop: '1px solid var(--border-color, #dee2e6)',
              background: 'var(--bg-subtle, #f8f9fa)',
              borderRadius: '0 0 var(--radius-lg) var(--radius-lg)'
            }}>
              <button
                type="button"
                className="btn btn-secondary"
                onClick={() => setStudentPickerOpen(false)}
              >
                Đóng
              </button>
              <button
                type="button"
                className="btn btn-primary"
                disabled={selectedPickerRkIds.length === 0}
                onClick={addSelectedStudents}
              >
                Thêm {selectedPickerRkIds.length > 0 ? `(${selectedPickerRkIds.length})` : ''} sinh viên đã chọn
              </button>
            </div>
          </div>
        </div>
      )}

      <AppPoolModal
        open={appPoolOpen}
        onClose={() => setAppPoolOpen(false)}
        onSelect={addAppKeyword}
        allowedApps={allowedApps}
      />
      <AppTemplatePickerModal
        open={templatePickerOpen}
        onClose={() => setTemplatePickerOpen(false)}
        onSelect={applyTemplate}
        allowedApps={allowedApps}
      />
    </div>
  );
};
