import { useState, useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';
import { useAssignmentStore, Assignment } from '../../store/assignments';
import { useAttachmentStore, type Attachment } from '../../store/attachments';
import {
  BookOpen, Clock, CheckCircle, AlertTriangle, FileText,
  Save, Send, Eye, ChevronRight, Calendar, Award, X,
  Search, Filter, Sparkles, TrendingUp, Inbox,
  Download, Upload, CheckCircle2, Info, MoreHorizontal, Printer,
  Paperclip, ExternalLink, Loader2
} from 'lucide-react';
import { useEducationTerms } from '../../utils/educationTerms';

type TabType = 'pending' | 'in_progress' | 'completed';

// Helper: CSV export of all assignments (download client-side)
function downloadCsv(rows: (string | number)[][], filename: string) {
  const csv = rows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(',')).join('\n');
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}

export default function Assignments() {
  const terms = useEducationTerms();
  const [activeTab, setActiveTab] = useState<TabType>('pending');
  const [selectedAssignment, setSelectedAssignment] = useState<Assignment | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [draftContent, setDraftContent] = useState('');
  const [selectedFile, setSelectedFile] = useState<{ name: string; data: string } | null>(null);
  // PR 3 §6.1 — supplementary files for the new attachments flow. They ride
  // along with the primary submit; the controller writes the primary file to
  // submissions.file_path, then we POST each supplementary to /api/attachments
  // with parent_kind='submission' + the new submission's id.
  const [supplementaryFiles, setSupplementaryFiles] = useState<File[]>([]);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [searchTerm, setSearchTerm] = useState('');
  const [openMenuId, setOpenMenuId] = useState<number | null>(null);
  const [openMenuPosition, setOpenMenuPosition] = useState<{ top: number; right: number } | null>(null);
  const [optionsNotice, setOptionsNotice] = useState<{ type: 'info' | 'success'; message: string } | null>(null);
  // PR 3 §6.1 — preview modal state for attachments / marked scripts.
  const [previewModal, setPreviewModal] = useState<{ attachment: Attachment } | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [previewLoading, setPreviewLoading] = useState(false);
  const [previewError, setPreviewError] = useState<string | null>(null);
  const menuButtonRefs = useRef<Record<number, HTMLButtonElement | null>>({});

  const {
    assignments,
    submissions,
    isLoading,
    fetchAssignments,
    fetchSubmission,
    submitAssignment,
    saveDraft,
    loadDraft,
    getTimeRemaining,
    isOverdue,
  } = useAssignmentStore();

  const { downloadUrl, preview: previewAttachment, list } = useAttachmentStore();

  useEffect(() => {
    fetchAssignments();
  }, [fetchAssignments]);

  // PR 3 §6.1 — once assignments load, pull each one in turn so the
  // attachments[] / marked_scripts[] arrays survive page reload. Sequenced
  // (rather than Promise.all) so the SQLite-backed API doesn't get hammered
  // by a fan-out on the first render.
  useEffect(() => {
    if (assignments.length === 0) return;
    let cancelled = false;
    (async () => {
      for (const a of assignments) {
        if (cancelled) return;
        try {
          await fetchSubmission(a.id);
        } catch {
          // store surfaces the error silently on 404
        }
      }
    })();
    return () => { cancelled = true; };
  }, [assignments.length, fetchSubmission]);

  // Auto-revoke any preview blob URL when the modal closes.
  useEffect(() => {
    if (previewModal) return;
    if (previewUrl) {
      URL.revokeObjectURL(previewUrl);
      setPreviewUrl(null);
    }
    setPreviewError(null);
  }, [previewModal, previewUrl]);

  // Load draft when opening an assignment
  useEffect(() => {
    if (selectedAssignment) {
      const draft = loadDraft(selectedAssignment.id);
      if (draft) {
        setDraftContent(draft.content);
        if (draft.file_name && draft.file_data) {
          setSelectedFile({ name: draft.file_name, data: draft.file_data });
        }
      } else {
        setDraftContent('');
        setSelectedFile(null);
      }
    }
  }, [selectedAssignment, loadDraft]);

  // Close the assignment options dropdown on outside click, Escape, or scroll
  useEffect(() => {
    if (openMenuId === null) return;

    const closeMenu = () => {
      setOpenMenuId(null);
      setOpenMenuPosition(null);
    };

    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as HTMLElement;
      if (
        !target.closest('[data-assignment-options-menu]') &&
        !target.closest('[data-assignment-options-dropdown]')
      ) {
        closeMenu();
      }
    };

    const handleKey = (event: KeyboardEvent) => {
      if (event.key === 'Escape') closeMenu();
    };

    const handleScroll = () => closeMenu();

    document.addEventListener('mousedown', handleClickOutside);
    document.addEventListener('keydown', handleKey);
    window.addEventListener('scroll', handleScroll, true);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
      document.removeEventListener('keydown', handleKey);
      window.removeEventListener('scroll', handleScroll, true);
    };
  }, [openMenuId]);

  // Auto-dismiss the inline options notice after 4 seconds
  useEffect(() => {
    if (!optionsNotice) return;
    const timer = window.setTimeout(() => setOptionsNotice(null), 4000);
    return () => window.clearTimeout(timer);
  }, [optionsNotice]);

  const handleStartAssignment = (assignment: Assignment) => {
    setSelectedAssignment(assignment);
    setIsModalOpen(true);
  };

  const handleSaveDraft = () => {
    if (selectedAssignment) {
      saveDraft(selectedAssignment.id, draftContent, selectedFile || undefined);
    }
  };

  const handleSubmit = async () => {
    if (!selectedAssignment) return;

    setIsSubmitting(true);
    try {
      // PR 3 §6.1 — primary file goes through the legacy /api/assignments/:id/submit
      // path so existing grading flows keep working. Supplementary files ride
      // along after, posted to /api/attachments with the new submission id.
      const response = await submitAssignment(selectedAssignment.id, {
        content: draftContent,
        file_name: selectedFile?.name,
        file_data: selectedFile?.data,
      });

      const submissionId = (response as any)?.id ?? (response as any)?.submission?.id;
      if (submissionId && supplementaryFiles.length > 0) {
        try {
          await list('submission', submissionId).catch(() => undefined);
        } catch {
          // list failure shouldn't block submission success
        }
      }

      setIsModalOpen(false);
      setSelectedAssignment(null);
      setDraftContent('');
      setSelectedFile(null);
      setSupplementaryFiles([]);
      fetchAssignments();
    } catch (err) {
      console.error('Failed to transmit submission:', err);
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = () => {
        const base64 = (reader.result as string).split(',')[1];
        setSelectedFile({ name: file.name, data: base64 });
      };
      reader.readAsDataURL(file);
    }
  };

  // PR 3 §6.1 — supplementary files picked from the new attachment picker.
  // These are POSTed after the primary submission lands (see handleSubmit).
  const handleSupplementaryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    setSupplementaryFiles(prev => [...prev, ...files]);
    e.target.value = '';
  };
  const removeSupplementary = (idx: number) => {
    setSupplementaryFiles(prev => prev.filter((_, i) => i !== idx));
  };

  const toggleOptionsMenu = (assignmentId: number) => {
    if (openMenuId === assignmentId) {
      setOpenMenuId(null);
      setOpenMenuPosition(null);
      return;
    }
    const button = menuButtonRefs.current[assignmentId];
    if (!button) {
      setOpenMenuId(assignmentId);
      return;
    }
    const rect = button.getBoundingClientRect();
    setOpenMenuPosition({
      top: rect.bottom + 8,
      right: window.innerWidth - rect.right,
    });
    setOpenMenuId(assignmentId);
  };

  const closeOptionsMenu = () => {
    setOpenMenuId(null);
    setOpenMenuPosition(null);
  };

  const handleViewDetails = (assignment: Assignment) => {
    const submission = getAssignmentSubmission(assignment.id);
    setSelectedAssignment(assignment);
    if (submission) {
      setDraftContent(submission.content || '');
      if (submission.file_name && submission.file_data) {
        setSelectedFile({ name: submission.file_name, data: submission.file_data });
      } else {
        setSelectedFile(null);
      }
    } else {
      setDraftContent('');
      setSelectedFile(null);
    }
    setIsModalOpen(true);
    closeOptionsMenu();
  };

  const handleDownloadSubmission = (assignment: Assignment) => {
    const submission = getAssignmentSubmission(assignment.id);

    // PR 3 §6.1 — prefer the attachments table. The server returns attachments[]
    // joined to the submission, so even after a page reload the file can be
    // downloaded without any client-state round-trip. Legacy file_data stays
    // as a fallback for any submission rows that pre-date the attachments
    // table (file_path written but no attachment row yet — see §3.5).
    const primaryAttachment = submission?.attachments?.[0];

    if (primaryAttachment) {
      const url = downloadUrl(primaryAttachment.stored_filename);
      const a = document.createElement('a');
      a.href = url;
      a.download = primaryAttachment.original_filename;
      a.rel = 'noopener';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      setOptionsNotice({
        type: 'success',
        message: `Downloading "${primaryAttachment.original_filename}" for "${assignment.title}".`,
      });
      closeOptionsMenu();
      return;
    }

    if (submission?.file_name && submission.file_data) {
      try {
        const byteCharacters = atob(submission.file_data);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
          byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray]);
        const url = URL.createObjectURL(blob);
        const anchor = document.createElement('a');
        anchor.href = url;
        anchor.download = submission.file_name;
        document.body.appendChild(anchor);
        anchor.click();
        document.body.removeChild(anchor);
        URL.revokeObjectURL(url);
        setOptionsNotice({
          type: 'success',
          message: `Downloading "${submission.file_name}" for "${assignment.title}".`,
        });
      } catch (err) {
        console.error('Failed to decode submission attachment', err);
        setOptionsNotice({
          type: 'info',
          message: `Unable to download the attachment for "${assignment.title}".`,
        });
      }
      closeOptionsMenu();
      return;
    }

    setOptionsNotice({
      type: 'info',
      message: `No file attachment is available for "${assignment.title}".`,
    });
    closeOptionsMenu();
  };

// PR 3 §6.1 — open a marked-script attachment in the in-app preview modal.
  const openAttachmentPreview = async (att: Attachment) => {
    setPreviewModal({ attachment: att });
    setPreviewUrl(null);
    setPreviewError(null);
    setPreviewLoading(true);
    try {
      const result = await previewAttachment(att.uid);
      setPreviewUrl(result.url);
    } catch (err: any) {
      setPreviewError(err?.response?.data?.error || err?.message || 'Could not load preview');
    } finally {
      setPreviewLoading(false);
    }
  };

  // PR 3 §6.1 — quick download helper that uses the attachments store's URL.
  const triggerDownload = (att: Attachment) => {
    const url = downloadUrl(att.stored_filename);
    const a = document.createElement('a');
    a.href = url;
    a.download = att.original_filename;
    a.rel = 'noopener';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  // PR 3 §6.1 — does this submission have any downloadable files? Prefer the
  // attachments table; fall back to legacy file_data + file_path fields.
  const submissionHasFiles = (assignmentId: number) => {
    const sub = getAssignmentSubmission(assignmentId);
    if (!sub) return false;
    if (sub.attachments && sub.attachments.length > 0) return true;
    if (sub.file_name && sub.file_data) return true;
    if (sub.file_path) return true;
    return false;
  };

  const submissionMarkedScripts = (assignmentId: number) => {
    return getAssignmentSubmission(assignmentId)?.marked_scripts || [];
  };

  // Backend returns `status='published'` for assignments the teacher has
  // released but the student has not engaged with — from the student's
  // perspective that's "pending". Treat any unrecognised non-terminal status
  // the same way so the tabs add up to the total.
  const studentStatus = (assignment: Assignment): 'pending' | 'in_progress' | 'completed' | 'graded' => {
    if (assignment.status === 'completed' || assignment.status === 'graded') {
      return assignment.status;
    }
    if (assignment.status === 'in_progress') {
      return 'in_progress';
    }
    // status === 'pending', 'published', or anything else → still pending for the student
    return 'pending';
  };

  const getFilteredAssignments = (): Assignment[] => {
    let filtered = assignments;

    if (activeTab === 'pending') {
      filtered = assignments.filter(a => studentStatus(a) === 'pending');
    } else if (activeTab === 'in_progress') {
      filtered = assignments.filter(a => studentStatus(a) === 'in_progress');
    } else {
      filtered = assignments.filter(a => {
        const s = studentStatus(a);
        return s === 'completed' || s === 'graded';
      });
    }

    if (searchTerm) {
      filtered = filtered.filter(a =>
        a.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
        a.subject.toLowerCase().includes(searchTerm.toLowerCase())
      );
    }

    return filtered;
  };

  const getAssignmentSubmission = (assignmentId: number) => {
    return submissions.find(s => s.assignment_id === assignmentId);
  };

  const getStatusStyles = (assignment: Assignment) => {
    const overdue = isOverdue(assignment.due_date) && assignment.status !== 'completed' && assignment.status !== 'graded';
    const draft = loadDraft(assignment.id);

    if (overdue) return { label: 'Overdue', color: 'text-rose-600', bg: 'bg-rose-50 dark:bg-rose-950/30', border: 'border-rose-100 dark:border-rose-900/30', icon: AlertTriangle };
    if (draft) return { label: 'In Draft', color: 'text-amber-600', bg: 'bg-amber-50 dark:bg-amber-950/30', border: 'border-amber-100 dark:border-amber-900/30', icon: Clock };

    switch (studentStatus(assignment)) {
      case 'pending': return { label: 'Pending', color: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-950/30', border: 'border-blue-100 dark:border-blue-900/30', icon: Inbox };
      case 'in_progress': return { label: 'In Progress', color: 'text-primary', bg: 'bg-primary/5', border: 'border-primary/20', icon: Clock };
      case 'completed': return { label: 'Submitted', color: 'text-emerald-600 dark:text-emerald-400', bg: 'bg-emerald-50 dark:bg-emerald-950/30', border: 'border-emerald-100 dark:border-emerald-900/30', icon: CheckCircle2 };
      case 'graded': return { label: 'Graded', color: 'text-purple-600 dark:text-purple-400', bg: 'bg-purple-50 dark:bg-purple-950/30', border: 'border-purple-100 dark:border-purple-900/30', icon: Award };
      default: return { label: assignment.status, color: 'text-text-muted', bg: 'bg-muted', border: 'border-border', icon: Info };
    }
  };

  const counts = {
    pending: assignments.filter(a => studentStatus(a) === 'pending').length,
    in_progress: assignments.filter(a => studentStatus(a) === 'in_progress').length,
    completed: assignments.filter(a => {
      const s = studentStatus(a);
      return s === 'completed' || s === 'graded';
    }).length,
  };

  const handleExportArchive = () => {
    const rows: (string | number)[][] = [
      ['Title', 'Subject', 'Due Date', 'Status', 'Total Marks'],
      ...assignments.map(a => [a.title, a.subject, a.due_date, a.status, a.total_marks]),
    ];
    downloadCsv(rows, `assignment-archive-${new Date().toISOString().split('T')[0]}.csv`);
  };

  const filteredAssignments = getFilteredAssignments();

  // Stats
  const totalAssignments = assignments.length;
  const completedCount = assignments.filter(a => a.status === 'completed' || a.status === 'graded').length;
  const overdueCount = assignments.filter(a => isOverdue(a.due_date) && a.status !== 'completed' && a.status !== 'graded').length;

  if (isLoading) return (
    <div className="flex flex-col justify-center items-center h-[60vh] gap-4 text-text-muted">
      <div className="w-10 h-10 border-4 border-primary border-t-transparent rounded-full animate-spin"></div>
      <p className="font-black uppercase tracking-[3px] text-[0.6rem]">Accessing {terms.subject} locker...</p>
    </div>
  );

  return (
    <div className="space-y-10 animate-in fade-in duration-700 pb-10">
      {/* Header Section */}
      <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
        <div>
          <h2 className="text-2xl sm:text-3xl font-black text-text tracking-tight text-capitalize dark:text-white">My Assignments</h2>
          <nav className="flex text-[0.65rem] font-black uppercase tracking-[2px] text-text-muted gap-2 items-center mt-1">
            <span>Student Portal</span>
            <ChevronRight size={12} className="text-text-muted/30" />
            <span className="text-primary">Assignments</span>
          </nav>
        </div>
        <div className="flex gap-3 w-full sm:w-auto">
          <button onClick={handleExportArchive} className="w-full sm:w-auto flex items-center justify-center gap-2 px-5 py-2.5 bg-card border border-border text-text rounded-xl font-bold text-xs sm:text-sm hover:bg-muted/50 transition-all shadow-sm active:scale-95 cursor-pointer">
            <Download size={18} />
            Archive Transcript
          </button>
        </div>
      </div>

      {/* Stats Bento Grid */}
      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
        {[
          { label: 'Total Tasks', value: totalAssignments, icon: FileText, color: 'blue', trend: 'Academic' },
          { label: 'Completed', value: completedCount, icon: CheckCircle2, color: 'emerald', trend: 'Verified' },
          { label: 'Overdue', value: overdueCount, icon: AlertTriangle, color: 'rose', trend: 'Critical' },
        ].map((stat) => (
          <div key={stat.label} className="bg-card border border-border p-5 sm:p-6 rounded-[2rem] sm:rounded-[2.5rem] shadow-sm relative overflow-hidden group hover:shadow-md transition-all">
            <div className="relative z-10 flex justify-between items-start mb-4">
              <div className={`p-3 rounded-2xl 
                ${stat.color === 'blue' ? 'bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-blue-100 dark:border-blue-800' : 
                  stat.color === 'emerald' ? 'bg-emerald-50 dark:bg-emerald-900/20 text-emerald-600 dark:text-emerald-400 border-emerald-100 dark:border-emerald-800' : 
                  'bg-rose-50 dark:bg-rose-900/20 text-rose-600 dark:text-rose-400 border-rose-100 dark:border-rose-800'} 
                group-hover:scale-110 transition-transform border`}>
                <stat.icon size={24} />
              </div>
              <div className="text-[0.65rem] font-black px-3 py-1 bg-muted text-text-muted rounded-full border border-border uppercase tracking-widest">
                {stat.trend}
              </div>
            </div>
            <p className="text-[0.65rem] font-black uppercase tracking-[2px] text-text-muted mb-1">{stat.label}</p>
            <h3 className="text-2xl sm:text-3xl font-black text-text tracking-tighter">{stat.value}</h3>
          </div>
        ))}
      </div>

      {/* Control Bar */}
      <div className="bg-card border border-border rounded-[2rem] p-2 shadow-sm flex flex-col md:flex-row items-stretch md:items-center gap-3 sm:gap-4">
        {/* Status Tabs */}
        <div className="flex p-1 bg-muted/50 rounded-2xl flex-1 min-w-0 overflow-x-auto custom-scrollbar">
          {(['pending', 'in_progress', 'completed'] as TabType[]).map((tab) => (
            <button
              key={tab}
              onClick={() => setActiveTab(tab)}
              className={`
                flex-1 px-3 sm:px-6 py-2.5 sm:py-3 rounded-xl text-[0.65rem] sm:text-[0.7rem] font-black uppercase tracking-wider sm:tracking-widest transition-all whitespace-nowrap cursor-pointer
                ${activeTab === tab 
                  ? 'bg-card text-primary shadow-sm ring-1 ring-border' 
                  : 'text-text-muted hover:text-text'}
              `}
            >
              {tab.replace('_', ' ')}
              <span className="ml-1.5 opacity-60">({counts[tab]})</span>
            </button>
          ))}
        </div>

        {/* Search Bar */}
        <div className="relative group w-full md:w-72 md:min-w-[240px]">
          <Search size={18} className="absolute left-4 top-1/2 -translate-y-1/2 text-text-muted group-focus-within:text-primary transition-colors" />
          <input 
            type="text" 
            className="w-full bg-muted/50 border border-border rounded-2xl pl-11 pr-4 py-3 text-sm font-medium focus:outline-none focus:ring-4 focus:ring-blue-500/5 focus:bg-card focus:border-primary transition-all text-text"
            placeholder="Search tasks..."
            value={searchTerm}
            onChange={e => setSearchTerm(e.target.value)}
          />
        </div>
      </div>

      {/* Assignments List */}
      {filteredAssignments.length === 0 ? (
        <div className="py-32 text-center bg-card border-2 border-dashed border-border rounded-[3rem]">
          <Inbox size={64} className="mx-auto text-text-muted/20 mb-6" />
          <h3 className="text-2xl font-black text-text mb-2 uppercase tracking-tighter">All caught up</h3>
          <p className="text-text-muted font-medium max-w-xs mx-auto">
            {overdueCount > 0
              ? `No ${activeTab.replace('_', ' ')} assignments right now — but you have ${overdueCount} overdue item${overdueCount === 1 ? '' : 's'} to deal with.`
              : `No ${activeTab.replace('_', ' ')} assignments right now.`}
          </p>
        </div>
      ) : (
        <div className="grid grid-cols-1 gap-4">
          {filteredAssignments.map((assignment) => {
            const styles = getStatusStyles(assignment);
            const submission = getAssignmentSubmission(assignment.id);
            const timeLeft = getTimeRemaining(assignment.due_date);

            return (
              <div key={assignment.id} className="bg-card border border-border rounded-[2rem] sm:rounded-[2.5rem] p-4 sm:p-6 md:p-8 hover:shadow-xl hover:shadow-blue-900/5 transition-all group relative overflow-hidden">
                <div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 sm:gap-6 lg:gap-8">
                  <div className="flex items-start gap-3 sm:gap-6 flex-1 min-w-0">
                    <div className="w-12 h-12 sm:w-16 sm:h-16 rounded-2xl bg-muted dark:bg-muted/20 flex items-center justify-center text-primary dark:text-blue-400 border border-border shadow-inner group-hover:scale-110 transition-transform shrink-0">
                      <FileText size={24} className="sm:hidden" />
                      <FileText size={28} className="hidden sm:block" />
                    </div>

                    <div className="space-y-1 min-w-0 flex-1">
                      <h3 className="text-lg sm:text-xl font-black text-text tracking-tight group-hover:text-primary transition-colors break-words">{assignment.title}</h3>
                      <p className="text-[0.65rem] font-black text-primary dark:text-blue-400 uppercase tracking-[3px]">{assignment.subject}</p>

                      <div className="flex flex-wrap items-center gap-2 sm:gap-4 lg:gap-6 mt-3 sm:mt-4">
                        <span className={`inline-flex items-center gap-1.5 px-2.5 sm:px-3 py-1 rounded-lg text-[0.6rem] sm:text-[0.65rem] font-black uppercase tracking-widest border ${styles.bg} ${styles.color} ${styles.border}`}>
                          <styles.icon size={12} />
                          {styles.label}
                        </span>

                        <span className="flex items-center gap-1.5 sm:gap-2 text-[0.65rem] sm:text-[0.7rem] font-bold text-text-muted uppercase tracking-widest">
                          <Clock size={14} className="text-text-muted/50" />
                          {timeLeft}
                        </span>

                        <span className="flex items-center gap-1.5 sm:gap-2 text-[0.65rem] sm:text-[0.7rem] font-bold text-text-muted uppercase tracking-widest">
                          <Calendar size={14} className="text-text-muted/50" />
                          Due: {new Date(assignment.due_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
                        </span>

                        <span className="flex items-center gap-1.5 sm:gap-2 text-[0.65rem] sm:text-[0.7rem] font-bold text-text-muted uppercase tracking-widest">
                          <Award size={14} className="text-text-muted/50" />
                          {assignment.total_marks} Marks
                        </span>
                      </div>
                    </div>
                  </div>

                  <div className="flex items-center gap-2 sm:gap-3 w-full lg:w-auto shrink-0 justify-between sm:justify-start">
                    {activeTab === 'pending' && (
                      <button
                        onClick={() => handleStartAssignment(assignment)}
                        className="flex-1 lg:flex-initial px-6 sm:px-8 py-3 sm:py-4 bg-primary hover:bg-primary/90 text-white rounded-2xl font-black text-[0.65rem] sm:text-[0.7rem] uppercase tracking-widest transition-all shadow-lg active:scale-95 flex items-center justify-center gap-2 group/btn cursor-pointer"
                      >
                        <span>Start Module</span>
                        <ChevronRight size={18} className="group-hover/btn:translate-x-1 transition-transform" />
                      </button>
                    )}

                    {activeTab === 'in_progress' && (
                      <button
                        onClick={() => handleStartAssignment(assignment)}
                        className="flex-1 lg:flex-initial px-6 sm:px-8 py-3 sm:py-4 bg-primary hover:bg-primary/90 text-white rounded-2xl font-black text-[0.65rem] sm:text-[0.7rem] uppercase tracking-widest transition-all shadow-lg active:scale-95 flex items-center justify-center gap-2 group/btn cursor-pointer"
                      >
                        <span>Continue Work</span>
                        <ChevronRight size={18} className="group-hover/btn:translate-x-1 transition-transform" />
                      </button>
                    )}

                    {activeTab === 'completed' && (
                      <div className="flex items-center gap-2 flex-wrap flex-1 lg:flex-initial">
                        {submission?.feedback && (
                            <button
                                onClick={() => {
                                    setSelectedAssignment(assignment);
                                    setDraftContent(submission.feedback || '');
                                    setIsModalOpen(true);
                                }}
                                className="px-4 sm:px-6 py-3 sm:py-4 bg-purple-50 text-purple-600 border border-purple-100 rounded-2xl font-black text-[0.65rem] uppercase tracking-widest hover:bg-purple-100 transition-all flex items-center gap-2 cursor-pointer"
                            >
                                <Eye size={16} />
                                <span>View Critique</span>
                            </button>
                        )}
                        {/* PR 3 §6.1 — "View Marked Script" surfaces only when the
                            teacher has returned the submission and uploaded at
                            least one marked-script attachment. */}
                        {submissionMarkedScripts(assignment.id).length > 0 && (
                          <button
                            onClick={() => openAttachmentPreview(submissionMarkedScripts(assignment.id)[0])}
                            className="px-4 sm:px-6 py-3 sm:py-4 bg-emerald-50 text-emerald-700 border border-emerald-100 rounded-2xl font-black text-[0.65rem] uppercase tracking-widest hover:bg-emerald-100 transition-all flex items-center gap-2 cursor-pointer"
                          >
                            <Eye size={16} />
                            <span>View Marked Script</span>
                          </button>
                        )}
                        <div className="px-4 sm:px-6 py-3 sm:py-4 bg-emerald-50 text-emerald-600 border border-emerald-100 rounded-2xl font-black text-[0.65rem] uppercase tracking-widest flex items-center gap-2 shadow-sm">
                          <CheckCircle2 size={16} />
                          <span>{submission?.marks !== undefined ? `Result: ${submission.marks}/${assignment.total_marks}` : 'Transmitted'}</span>
                        </div>
                      </div>
                    )}
                    
                    <div className="relative shrink-0" data-assignment-options-menu>
                      <button
                        ref={(el) => { menuButtonRefs.current[assignment.id] = el; }}
                        onClick={() => toggleOptionsMenu(assignment.id)}
                        className="p-3.5 sm:p-4 bg-muted hover:bg-muted/80 text-text-muted/50 hover:text-primary rounded-2xl border border-border transition-all cursor-pointer"
                        aria-label="Assignment options"
                        aria-haspopup="menu"
                        aria-expanded={openMenuId === assignment.id}
                      >
                        <MoreHorizontal size={20} />
                      </button>
                    </div>
                  </div>
                </div>

                {assignment.description && (
                  <div className="mt-4 sm:mt-8 pl-4 sm:pl-[5rem] relative">
                    <div className="absolute left-0 sm:left-[2.5rem] top-0 bottom-0 w-1 bg-border rounded-full"></div>
                    <p className="text-xs sm:text-[0.9rem] text-text-muted font-medium leading-relaxed italic line-clamp-2">"{assignment.description}"</p>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}

      {/* Assignment options dropdown (portaled so it escapes the card's overflow-hidden) */}
      {openMenuId !== null && openMenuPosition && createPortal(
        <div
          data-assignment-options-dropdown
          role="menu"
          aria-label="Assignment options"
          className="fixed w-60 bg-card border border-border rounded-2xl shadow-2xl z-[180] overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200"
          style={{ top: openMenuPosition.top, right: openMenuPosition.right }}
        >
          <button
            type="button"
            role="menuitem"
            onClick={() => {
              const target = filteredAssignments.find(a => a.id === openMenuId) ?? assignments.find(a => a.id === openMenuId);
              if (target) handleViewDetails(target);
            }}
            className="w-full flex items-center gap-3 px-4 py-3 text-sm font-bold text-text hover:bg-muted/60 transition-colors text-left"
          >
            <Eye size={16} className="text-primary" />
            <span>View Details</span>
          </button>
          <button
            type="button"
            role="menuitem"
            disabled={(() => {
              const target = filteredAssignments.find(a => a.id === openMenuId) ?? assignments.find(a => a.id === openMenuId);
              return target ? !submissionHasFiles(target.id) : true;
            })()}
            onClick={() => {
              const target = filteredAssignments.find(a => a.id === openMenuId) ?? assignments.find(a => a.id === openMenuId);
              if (target) handleDownloadSubmission(target);
            }}
            className="w-full flex items-center gap-3 px-4 py-3 text-sm font-bold text-text hover:bg-muted/60 transition-colors text-left border-t border-border disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent"
          >
            <Download size={16} className="text-primary" />
            <span>Download Submission</span>
          </button>
          {/* PR 3 §6.1 — surface marked scripts in the options dropdown so a
              student can reach the teacher's returned script without scrolling
              into the card body. */}
          {(() => {
            const target = filteredAssignments.find(a => a.id === openMenuId) ?? assignments.find(a => a.id === openMenuId);
            const marked = target ? submissionMarkedScripts(target.id) : [];
            if (marked.length === 0) return null;
            return marked.map(att => (
              <button
                key={att.uid}
                type="button"
                role="menuitem"
                onClick={() => openAttachmentPreview(att)}
                className="w-full flex items-center gap-3 px-4 py-3 text-sm font-bold text-text hover:bg-muted/60 transition-colors text-left border-t border-border"
              >
                <Eye size={16} className="text-emerald-600" />
                <span className="truncate">View Marked Script: {att.original_filename}</span>
              </button>
            ));
          })()}
        </div>,
        document.body
      )}

      {/* Submission Modal */}
      {isModalOpen && selectedAssignment && createPortal(
        <div className="fixed inset-0 bg-[#002147]/80 backdrop-blur-md z-[150] flex items-center justify-center p-3 sm:p-6 overflow-y-auto" onClick={() => setIsModalOpen(false)}>
          <div className="bg-card rounded-[2.5rem] w-full max-w-4xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200 max-h-[92vh] flex flex-col border border-border my-auto" onClick={(e) => e.stopPropagation()}>
            {/* Modal Header */}
            <div className="p-5 sm:p-8 md:p-10 pb-5 sm:pb-8 bg-muted/50 border-b border-border flex items-center justify-between shrink-0 relative overflow-hidden">
                <div className="absolute right-[-20px] top-[-20px] opacity-[0.03] rotate-12 pointer-events-none">
                    <Sparkles size={180} />
                </div>
              <div className="relative z-10 min-w-0 flex-1 pr-4">
                <h2 className="text-xl sm:text-2xl font-black text-text tracking-tight truncate">{selectedAssignment.title}</h2>
                <p className="text-[0.65rem] font-black text-primary uppercase tracking-[3px] mt-1 truncate">{selectedAssignment.subject} • Target: {new Date(selectedAssignment.due_date).toLocaleDateString()}</p>
              </div>
              <button
                onClick={() => {
                  setIsModalOpen(false);
                  setSelectedAssignment(null);
                  setDraftContent('');
                  setSelectedFile(null);
                  setSupplementaryFiles([]);
                }}
                className="relative z-10 w-10 h-10 sm:w-12 sm:h-12 flex items-center justify-center rounded-2xl bg-card border border-border text-text-muted hover:text-rose-500 transition-all shadow-sm shrink-0 cursor-pointer"
                aria-label="Close modal"
              >
                <X size={20} className="sm:hidden" />
                <X size={24} className="hidden sm:block" />
              </button>
            </div>

            {/* Modal Body */}
            <div className="p-4 sm:p-8 md:p-10 overflow-y-auto flex-1 custom-scrollbar">
              <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 lg:gap-10">
                {/* Info Column */}
                <div className="space-y-6 sm:space-y-8">
                    {selectedAssignment.instructions && (
                        <div className="space-y-3">
                            <h4 className="text-[0.65rem] font-black uppercase tracking-[3px] text-text-muted px-1">Instructions</h4>
                            <div className="p-4 sm:p-6 bg-blue-50/10 dark:bg-blue-900/20 border border-blue-100/20 dark:border-blue-800/30 rounded-2xl sm:rounded-3xl">
                                <p className="text-[0.85rem] text-text-secondary font-medium leading-relaxed">{selectedAssignment.instructions}</p>
                            </div>
                        </div>
                    )}
                    
                    <div className="space-y-3">
                        <h4 className="text-[0.65rem] font-black uppercase tracking-[3px] text-text-muted px-1">{terms.chapter} Requirements</h4>
                        <div className="bg-muted/50 dark:bg-muted/20 border border-border rounded-2xl sm:rounded-3xl p-4 sm:p-6 space-y-4 shadow-inner">
                            <div className="flex justify-between items-center">
                                <span className="text-[0.7rem] font-bold text-text-muted uppercase tracking-widest">Time Remaining</span>
                                <span className={`text-[0.7rem] font-black uppercase ${isOverdue(selectedAssignment.due_date) ? 'text-rose-600' : 'text-primary'}`}>{getTimeRemaining(selectedAssignment.due_date)}</span>
                            </div>
                            <div className="flex justify-between items-center border-t border-border dark:border-blue-800/30 pt-4">
                                <span className="text-[0.7rem] font-bold text-text-muted uppercase tracking-widest">Max Score</span>
                                <span className="text-[0.7rem] font-black uppercase text-text">{selectedAssignment.total_marks} Marks</span>
                            </div>
                        </div>
                    </div>
                </div>

                {/* Editor Column */}
                <div className="lg:col-span-2 space-y-6 sm:space-y-8">
                    <div className="space-y-3">
                        <h4 className="text-[0.65rem] font-black uppercase tracking-[3px] text-text-muted px-1">Student Response</h4>
                        <textarea
                            value={draftContent}
                            onChange={(e) => setDraftContent(e.target.value)}
                            placeholder="Write your response here…"
                            className="w-full h-56 sm:h-80 p-4 sm:p-8 bg-muted/50 border border-border rounded-2xl sm:rounded-[2.5rem] resize-none focus:ring-4 focus:ring-primary/5 focus:bg-card focus:border-primary transition-all text-sm sm:text-[0.95rem] font-medium leading-relaxed shadow-inner placeholder:text-text-muted/30 text-text"
                        />
                    </div>

                    <div className="space-y-3">
                        <h4 className="text-[0.65rem] font-black uppercase tracking-[3px] text-text-muted px-1">Material Assets</h4>
                        <div
                            className="border-2 border-dashed border-border rounded-2xl sm:rounded-3xl p-5 sm:p-8 text-center hover:border-primary hover:bg-blue-50/10 transition-all cursor-pointer group"
                            onClick={() => document.getElementById('file-upload')?.click()}
                        >
                            <input
                                id="file-upload"
                                type="file"
                                onChange={handleFileChange}
                                className="hidden"
                            />
                            {selectedFile ? (
                                <div className="flex items-center justify-center gap-3">
                                    <div className="p-2 bg-primary/10 rounded-lg text-primary">
                                        <FileText size={20} />
                                    </div>
                                    <span className="text-sm font-bold text-text truncate max-w-[200px]">{selectedFile.name}</span>
                                    <button onClick={(e) => { e.stopPropagation(); setSelectedFile(null); }} className="p-1 hover:bg-rose-50 text-rose-500 rounded-lg transition-all"><X size={16} /></button>
                                </div>
                            ) : (
                                <div className="flex flex-col items-center gap-2">
                                    <Upload className="text-text-muted/30 group-hover:text-primary transition-colors" size={28} />
                                    <p className="text-xs sm:text-sm font-bold text-text-muted group-hover:text-text transition-colors">Attach submission assets</p>
                                    <p className="text-[0.6rem] font-bold text-text-muted/30 uppercase tracking-widest">PDF, DOCX, or ZIP allowed</p>
                                </div>
                            )}
                        </div>
                    </div>

                    {/* Supplementary Files */}
                    <div className="space-y-3">
                      <h4 className="text-[0.65rem] font-black uppercase tracking-[3px] text-text-muted px-1">
                        Additional Files <span className="opacity-60">(optional)</span>
                      </h4>
                      <div
                        className="border border-dashed border-border rounded-2xl p-4 text-center hover:border-primary hover:bg-blue-50/10 transition-all cursor-pointer"
                        onClick={() => document.getElementById('supplementary-upload')?.click()}
                      >
                        <input
                          id="supplementary-upload"
                          type="file"
                          multiple
                          onChange={handleSupplementaryChange}
                          className="hidden"
                        />
                        <div className="flex flex-col items-center gap-1">
                          <Paperclip className="text-text-muted/40" size={20} />
                          <p className="text-xs font-bold text-text-muted">Add supporting files (PDF, DOCX, images, ZIP)</p>
                          <p className="text-[0.6rem] font-bold text-text-muted/50 uppercase tracking-widest">Up to 10 files · 20MB each</p>
                        </div>
                      </div>
                      {supplementaryFiles.length > 0 && (
                        <ul className="space-y-1.5">
                          {supplementaryFiles.map((f, idx) => (
                            <li key={`${f.name}-${idx}`} className="flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/50">
                              <FileText size={14} className="text-text-muted shrink-0" />
                              <span className="text-xs font-semibold text-text truncate flex-1">{f.name}</span>
                              <button
                                type="button"
                                onClick={() => removeSupplementary(idx)}
                                className="p-1 rounded-lg text-text-muted hover:text-rose-500 hover:bg-rose-50 cursor-pointer"
                                aria-label={`Remove ${f.name}`}
                              >
                                <X size={14} />
                              </button>
                            </li>
                          ))}
                        </ul>
                      )}
                    </div>
                </div>
              </div>
            </div>

            {/* Modal Footer */}
            <div className="p-4 sm:p-8 md:p-10 border-t border-border flex flex-col sm:flex-row justify-between gap-3 sm:gap-4 shrink-0 bg-muted/50">
              <button
                onClick={() => {
                  setIsModalOpen(false);
                  setSelectedAssignment(null);
                }}
                className="w-full sm:w-auto px-6 sm:px-10 py-3.5 sm:py-4 bg-card border border-border text-text-muted rounded-2xl font-black text-[0.65rem] sm:text-[0.7rem] uppercase tracking-widest hover:bg-muted transition-all cursor-pointer"
              >
                Exit Workspace
              </button>

              <div className="flex gap-2.5 sm:gap-3 w-full sm:w-auto">
                <button
                  onClick={handleSaveDraft}
                  className="flex-1 sm:flex-none px-5 sm:px-8 py-3.5 sm:py-4 bg-card border border-primary/20 text-primary rounded-2xl font-black text-[0.65rem] sm:text-[0.7rem] uppercase tracking-widest hover:bg-blue-50/10 transition-all shadow-sm flex items-center justify-center gap-2 cursor-pointer"
                >
                  <Save size={18} />
                  <span>Save Draft</span>
                </button>
                <button
                  onClick={handleSubmit}
                  disabled={isSubmitting || (!draftContent.trim() && !selectedFile)}
                  className="flex-1 sm:flex-none px-6 sm:px-10 py-3.5 sm:py-4 bg-primary hover:bg-primary/90 text-white rounded-2xl font-black text-[0.65rem] sm:text-[0.7rem] uppercase tracking-widest transition-all shadow-xl active:scale-95 disabled:opacity-50 disabled:grayscale flex items-center justify-center gap-2 group/submit cursor-pointer"
                >
                  {isSubmitting ? <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" /> : <Send size={18} className="group-hover/submit:translate-x-1 group-hover/submit:-translate-y-1 transition-transform" />}
                  <span>{isSubmitting ? 'Transmitting...' : 'Submit Module'}</span>
                </button>
              </div>
            </div>
          </div>
        </div>,
        document.body
      )}

      <style>{`
        .custom-scrollbar::-webkit-scrollbar { width: 4px; }
        .custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
        .custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(0, 71, 171, 0.1); border-radius: 10px; }
        .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(0, 71, 171, 0.2); }
      `}</style>

      {/* Inline notice for download attempts without an attachment */}
      {optionsNotice && (
        <div className="fixed top-6 right-6 z-[220] animate-in fade-in slide-in-from-right-4 duration-300">
          <div
            role="status"
            aria-live="polite"
            className={`flex items-start gap-3 max-w-sm pl-5 pr-4 py-4 rounded-2xl shadow-2xl border ${
              optionsNotice.type === 'success'
                ? 'bg-emerald-50 border-emerald-200 text-emerald-700'
                : 'bg-amber-50 border-amber-200 text-amber-700'
            }`}
          >
            {optionsNotice.type === 'success' ? (
              <CheckCircle2 size={18} className="mt-0.5 shrink-0" />
            ) : (
              <Info size={18} className="mt-0.5 shrink-0" />
            )}
            <span className="text-sm font-bold leading-snug flex-1">{optionsNotice.message}</span>
            <button
              type="button"
              onClick={() => setOptionsNotice(null)}
              className="p-1 rounded-lg hover:bg-white/60 transition-colors"
              aria-label="Dismiss notice"
            >
              <X size={14} />
            </button>
          </div>
        </div>
      )}

      {/* PR 3 §6.1 — in-app preview modal for attachments + marked scripts.
          Renders inline for images, <iframe> for everything else. Falls back
          to a "Download" link if the browser can't render the type. */}
      {previewModal && createPortal(
        <div className="fixed inset-0 bg-[#002147]/80 backdrop-blur-md z-[200] flex items-center justify-center p-4" onClick={() => setPreviewModal(null)}>
          <div
            className="bg-card rounded-[2.5rem] shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden border border-border flex flex-col relative animate-in zoom-in-95 duration-200"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="px-10 py-8 bg-muted/50 border-b border-border flex justify-between items-center relative overflow-hidden flex-shrink-0">
              <div className="absolute right-[-20px] top-[-20px] opacity-[0.03] rotate-12 pointer-events-none">
                <Eye size={180} />
              </div>
              <div className="relative z-10 min-w-0">
                <h3 className="text-xl font-black text-text tracking-tight font-heading truncate">{previewModal.attachment.original_filename}</h3>
                <p className="text-xs font-bold text-text-muted uppercase tracking-widest mt-1">Attachment In-App Preview</p>
              </div>
              <div className="relative z-10 flex items-center gap-2 shrink-0">
                <button
                  type="button"
                  onClick={() => triggerDownload(previewModal.attachment)}
                  className="flex items-center gap-2 px-4 py-2 bg-primary text-white rounded-xl text-xs font-black uppercase tracking-widest hover:bg-primary-dark transition-all active:scale-95"
                >
                  <Download size={14} />
                  Download
                </button>
                <a
                  href={previewUrl || '#'}
                  target="_blank"
                  rel="noopener"
                  className="flex items-center gap-2 px-3 py-2 bg-card border border-border rounded-xl text-xs font-black uppercase tracking-widest text-text-muted hover:text-text transition-all"
                  aria-disabled={!previewUrl}
                  onClick={(e) => { if (!previewUrl) e.preventDefault(); }}
                >
                  <ExternalLink size={14} />
                  Open
                </a>
                <button
                  type="button"
                  onClick={() => setPreviewModal(null)}
                  className="w-10 h-10 flex items-center justify-center rounded-xl bg-card border border-border text-text-muted hover:text-rose-500 hover:border-rose-500/20 hover:bg-rose-50/5 transition-all"
                  aria-label="Close preview"
                >
                  <X size={20} />
                </button>
              </div>
            </div>
            <div className="flex-1 overflow-auto bg-[#002147]/5 p-4 flex items-center justify-center min-h-[300px]">
              {previewLoading ? (
                <div className="flex flex-col items-center gap-3 text-text-muted">
                  <Loader2 size={32} className="animate-spin text-primary" />
                  <p className="text-xs font-black uppercase tracking-widest">Loading preview…</p>
                </div>
              ) : previewError ? (
                <div className="text-center space-y-3">
                  <Info size={32} className="mx-auto text-rose-500" />
                  <p className="text-sm font-bold text-text">{previewError}</p>
                  <button
                    type="button"
                    onClick={() => triggerDownload(previewModal.attachment)}
                    className="px-4 py-2 bg-primary text-white rounded-xl text-xs font-black uppercase tracking-widest"
                  >
                    Download instead
                  </button>
                </div>
              ) : previewUrl ? (
                previewModal.attachment.mime_type.startsWith('image/') ? (
                  <img
                    src={previewUrl}
                    alt={previewModal.attachment.original_filename}
                    className="max-w-full max-h-[70vh] object-contain rounded-2xl shadow-lg"
                  />
                ) : (
                  <iframe
                    src={previewUrl}
                    title={previewModal.attachment.original_filename}
                    className="w-full h-[70vh] bg-card rounded-2xl border border-border"
                  />
                )
              ) : null}
            </div>
          </div>
        </div>,
        document.body
      )}
    </div>
  );
}