const { useState, useEffect, useRef } = React;

const K3_STORAGE_KEY = 'k3Filters';

const FormatK3 = () => {
  const [students, setStudents] = useState([]);
  const [courses, setCourses] = useState([]);
  const [exams, setExams] = useState([]);
  const [batches, setBatches] = useState([]);
  const [loading, setLoading] = useState(true);
  const [initDone, setInitDone] = useState(false);
  const [allCourses, setAllCourses] = useState([]);
  
  const [selectedCourse, setSelectedCourse] = useState('');
  const [courseSearchQuery, setCourseSearchQuery] = useState('');
  const [selectedExam, setSelectedExam] = useState('');
  const [selectedBatch, setSelectedBatch] = useState('');
  const [sessionMax, setSessionMax] = useState(25);
  
  // Lab plan data - practicals derived from lab plan
  const [labPlans, setLabPlans] = useState([]);
  const [hasLabPlan, setHasLabPlan] = useState(false);
  
  // Grid marks: { [studentId]: { [practicalNo]: markValue } }
  const [gridMarks, setGridMarks] = useState({});

  // Column widths for sticky positioning
  const COL_ROLL_W = 50;
  const COL_ENROLL_W = 130;
  const COL_SEAT_W = 55;
  const COL_NAME_W = 175;
  const STICKY_TOTAL = COL_ROLL_W + COL_ENROLL_W + COL_SEAT_W + COL_NAME_W;

  useEffect(() => {
    Promise.all([
      fetch('/api/courses').then(res => res.json()),
      fetch('/api/exams').then(res => res.json()),
      fetch('/api/batches').then(res => res.json())
    ]).then(([courseData, examData, batchData]) => {
      setAllCourses(courseData);
      setExams(examData);
      setBatches(batchData);
      const saved = (() => { try { return JSON.parse(localStorage.getItem(K3_STORAGE_KEY)) || {}; } catch(e) { return {}; } })();
      const sExam = (saved.exam && examData.find(e => String(e.id) === String(saved.exam))) ? saved.exam : (examData.length > 0 ? examData[0].id : '');
      const sBatch = (saved.batch && batchData.find(b => String(b.id) === String(saved.batch))) ? saved.batch : (batchData.length > 0 ? batchData[0].id : '');
      setSelectedExam(sExam);
      setSelectedBatch(sBatch);
      setLoading(false);
      setInitDone(true);
    }).catch(err => {
      console.error(err);
      setLoading(false);
      setInitDone(true);
    });
  }, []);

  // Update filtered courses when exam changes
  useEffect(() => {
    const exam = exams.find(e => String(e.id) === String(selectedExam));
    if (!exam) return;
    const isSummer = exam.season === 'Summer';
    const filtered = allCourses.filter(c => {
      const hasPractical = parseFloat(c.fa_pr_max) > 0;
      const isEven = parseInt(c.semester) % 2 === 0;
      return hasPractical && (isSummer ? isEven : !isEven);
    });
    setCourses(filtered);
    
    if (filtered.length > 0 && !filtered.find(c => String(c.id) === String(selectedCourse))) {
      const saved = (() => { try { return JSON.parse(localStorage.getItem(K3_STORAGE_KEY)) || {}; } catch(e) { return {}; } })();
      if (saved.course && filtered.find(c => String(c.id) === String(saved.course))) setSelectedCourse(saved.course);
      else setSelectedCourse(filtered[0].id);
    }
  }, [selectedExam, exams, allCourses]);

  // Persist filter selections
  useEffect(() => {
    if (initDone && selectedCourse && selectedExam && selectedBatch) {
      try { localStorage.setItem(K3_STORAGE_KEY, JSON.stringify({ course: selectedCourse, exam: selectedExam, batch: selectedBatch })); } catch(e) {}
    }
  }, [selectedCourse, selectedExam, selectedBatch, initDone]);

  useEffect(() => {
    if (courses.length > 0) {
      const filtered = courses.filter(c => (c.code + ' ' + c.name).toLowerCase().includes(courseSearchQuery.toLowerCase()));
      if (filtered.length > 0 && !filtered.find(c => String(c.id) === String(selectedCourse))) {
        setSelectedCourse(filtered[0].id);
      }
    }
  }, [courseSearchQuery]);

  // Auto-set sessionMax from course master fa_pr_max
  useEffect(() => {
    if (selectedCourse && courses.length > 0) {
      const course = courses.find(c => String(c.id) === String(selectedCourse));
      if (course && parseFloat(course.fa_pr_max) > 0) {
        setSessionMax(parseFloat(course.fa_pr_max));
      }
    }
  }, [selectedCourse, courses]);

  // Fetch lab plans + students + marks whenever filters change
  useEffect(() => {
    if (!initDone || !selectedExam || !selectedCourse || !selectedBatch) return;
    setLoading(true);
    
    Promise.all([
      fetch(`/api/lab-plans?exam_id=${selectedExam}&course_id=${selectedCourse}&batch_id=${selectedBatch}&t=${Date.now()}`).then(res => res.ok ? res.json() : []),
      fetch(`/api/courses/${selectedCourse}/eligible-students?exam_id=${selectedExam}&t=${Date.now()}`).then(res => res.json()),
      fetch(`/api/exams/${selectedExam}/roster?t=${Date.now()}`).then(res => res.json()),
      fetch(`/api/lab-assessments?exam_id=${selectedExam}&course_id=${selectedCourse}&t=${Date.now()}`).then(res => res.ok ? res.json() : [])
    ]).then(([plansData, eligibleData, rosterData, marksData]) => {
      // Lab Plans
      const plans = Array.isArray(plansData) ? plansData.sort((a,b) => a.practical_no - b.practical_no) : [];
      setLabPlans(plans);
      setHasLabPlan(plans.length > 0);
      
      // Students — filter by batch
      const allStudents = Array.isArray(eligibleData) ? eligibleData : [];
      const batchFiltered = allStudents.filter(s => String(s.batch_id) === String(selectedBatch));
      const mappedStudents = batchFiltered.map(student => {
        const registered = Array.isArray(rosterData) ? rosterData.find(r => r.student_id === student.id) : null;
        return { ...student, seat_number: registered ? registered.seat_number : '' };
      });
      setStudents(mappedStudents);
      
      // Build grid marks from existing data
      const grid = {};
      mappedStudents.forEach(s => {
        grid[s.id] = {};
        if (Array.isArray(marksData)) {
          marksData.filter(m => m.student_id === s.id).forEach(m => {
            const total = (parseFloat(m.process_marks) || 0) + (parseFloat(m.product_marks) || 0);
            grid[s.id][m.practical_no] = total > 0 ? String(total) : '';
          });
        }
      });
      setGridMarks(grid);
      setLoading(false);
    }).catch(err => {
      console.error(err);
      setLoading(false);
    });
  }, [selectedExam, selectedCourse, selectedBatch, initDone]);

  const handleMarkChange = (studentId, practicalNo, value) => {
    let numValue = parseFloat(value);
    if (isNaN(numValue)) numValue = '';
    else if (numValue === 401) {
      numValue = 401;
    } else {
      if (numValue > sessionMax) numValue = sessionMax;
      if (numValue < 0) numValue = 0;
    }
    setGridMarks(prev => ({
      ...prev,
      [studentId]: {
        ...prev[studentId],
        [practicalNo]: numValue === '' ? '' : String(numValue)
      }
    }));
  };

  const getStudentTotal = (studentId) => {
    const studentRow = gridMarks[studentId] || {};
    let sum = 0;
    let count = 0;
    labPlans.forEach(p => {
      const v = parseFloat(studentRow[p.practical_no]);
      if (!isNaN(v)) { 
        sum += (v === 401 ? 0 : v); 
        count++; 
      }
    });
    return { sum, count };
  };

  const getFAMarks = (studentId) => {
    const course = courses.find(c => String(c.id) === String(selectedCourse));
    const faMax = course ? parseFloat(course.fa_pr_max) || 0 : 0;
    const { sum, count } = getStudentTotal(studentId);
    if (count === 0) return '-';
    const scaleFactor = faMax / (sessionMax * count);
    return Math.round(sum * scaleFactor);
  };

  // Column-wise copy: copy all students' marks from practical (practicalNo-1) to practicalNo
  const handleCopyFromPrevious = (practicalNo) => {
    const prevNo = practicalNo - 1;
    if (prevNo < 1) return alert('No previous practical to copy from.');
    
    // Check if previous practical has any marks
    const hasAnyPrevMarks = students.some(s => {
      const val = gridMarks[s.id]?.[prevNo];
      return val !== '' && val !== undefined && !isNaN(parseFloat(val));
    });
    if (!hasAnyPrevMarks) return alert(`No marks found in P${prevNo} to copy.`);
    
    if (!confirm(`Copy all students' marks from P${prevNo} → P${practicalNo}?`)) return;
    
    setGridMarks(prev => {
      const next = { ...prev };
      students.forEach(s => {
        const prevVal = prev[s.id]?.[prevNo];
        if (prevVal !== '' && prevVal !== undefined && !isNaN(parseFloat(prevVal))) {
          next[s.id] = { ...next[s.id], [practicalNo]: prevVal };
        }
      });
      return next;
    });
  };

  const handleClearAll = () => {
    if (!confirm("Clear all marks in the grid? (Database is not affected until you save)")) return;
    const cleared = {};
    students.forEach(s => { cleared[s.id] = {}; });
    setGridMarks(cleared);
  };

  const handleDeleteAllData = async () => {
    if (!selectedCourse || !selectedExam) return alert("Select course and exam term first.");
    const course = courses.find(c => String(c.id) === String(selectedCourse));
    if (!confirm(`PERMANENTLY DELETE all K3 marks for ${course?.code || 'this course'}?\n\nThis will remove marks for ALL practicals and ALL students. This cannot be undone!`)) return;
    try {
      const res = await fetch(`/api/lab-assessments?exam_id=${selectedExam}&course_id=${selectedCourse}`, { method: 'DELETE' });
      if (res.ok) { alert("All K3 data deleted successfully."); 
        const cleared = {};
        students.forEach(s => { cleared[s.id] = {}; });
        setGridMarks(cleared);
      }
      else alert("Failed to delete data.");
    } catch(e) { alert("Error deleting data."); }
  };

  const handleSave = async () => {
    if (!selectedCourse || !selectedExam) return alert('Please select a course and an exam term first!');
    if (!hasLabPlan) return alert('Lab Plan is required before saving K3 marks.');
    
    setLoading(true);
    let successes = 0;
    let errors = 0;
    
    for (const plan of labPlans) {
      const pNo = plan.practical_no;
      const marksForPractical = [];
      
      students.forEach(s => {
        const val = gridMarks[s.id]?.[pNo];
        if (val !== '' && val !== undefined && !isNaN(parseFloat(val))) {
          marksForPractical.push({
            student_id: s.id,
            process_marks: parseFloat(val),
            product_marks: 0
          });
        }
      });
      
      if (marksForPractical.length > 0) {
        try {
          const res = await fetch('/api/lab-assessments', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              exam_id: selectedExam,
              course_id: selectedCourse,
              practical_no: pNo,
              session_max_marks: sessionMax,
              marksData: marksForPractical
            })
          });
          if (res.ok) successes++;
          else errors++;
        } catch(e) { errors++; }
      }
    }
    
    setLoading(false);
    alert(`Marks saved!\nPracticals updated: ${successes}\nErrors: ${errors}`);
  };

  const handleExportExcel = async () => {
    if (!selectedCourse || !selectedExam) return alert('Select course and exam term first');
    const course = courses.find(c => String(c.id) === String(selectedCourse));
    const exam = exams.find(e => String(e.id) === String(selectedExam));
    if (!course || !exam) return alert("Failed to find course or exam.");

    const N = labPlans.length || 12;
    const faMax = parseFloat(course.fa_pr_max) || 0;

    let allMarks = [];
    try {
      const resMarks = await fetch(`/api/lab-assessments?exam_id=${selectedExam}&course_id=${selectedCourse}&t=${Date.now()}`);
      allMarks = await resMarks.json();
    } catch(err) { console.error(err); }

    const createRow = () => new Array(4 + N + 3).fill('');

    const row0 = createRow(); row0[row0.length - 1] = "K3";
    const row1 = createRow(); row1[0] = "For AICTE Diploma Engineering Courses"; row1[row1.length - 2] = "wef - 2023-24";
    const row2 = createRow(); row2[5] = "Maharashtra State Board of Technical Education";
    const row3 = createRow(); row3[5] = "FORMATIVE ASSESSMENT OF PRACTICAL (FA-PR)";
    const row4 = createRow();
    const row5 = createRow(); row5[0] = "Institute Name:"; row5[1] = "Government Polytechnic Vikramgad"; row5[2] = "Institute Code: 1547"; row5[row5.length - 3] = `Exam: ${exam.season} ${exam.year}`;
    const acYear = exam.season === 'Winter' ? `${exam.year}-${String(Number(exam.year)+1).slice(-2)}` : `${Number(exam.year)-1}-${String(exam.year).slice(-2)}`;
    const row6 = createRow(); row6[0] = "Academic Year:"; row6[1] = acYear;
    const row7 = createRow(); row7[0] = "Programme:"; row7[1] = "___________________________"; row7[5 > N? 4:5] = "Course:"; row7[6 > N? 5:6] = course.name; row7[9 > N? Math.min(8, N+1): 9] = "Course Code:"; row7[10 > N? Math.min(9, N+2): 10] = course.code;
    const row8 = createRow(); row8[0] = "Semester:"; row8[1] = String(course.semester) + 'K';
    const row9 = createRow();

    const row10 = createRow();
    row10[0] = "Roll\nNo."; row10[1] = "Enrollment\nNo."; row10[2] = "Exam Seat\nNumber"; row10[3] = "Name of the Student";
    row10[4] = `Practical / Tutorial\n(Marks out of ${sessionMax} per Experiment)`;
    row10[4+N] = `Total Marks\n(${sessionMax} x ${N} Expt.)`;
    row10[5+N] = `FA Marks of Practical Converted according to L-A Scheme (Max Marks: ${faMax})\n(Average across ${N} practicals, rounded up to full integer)`;
    row10[6+N] = "Signature of Student";

    const row11 = createRow();
    row11[0] = "1"; row11[1] = "2"; row11[2] = "3"; row11[3] = "4"; row11[4] = "5";
    row11[4+N] = "6"; row11[5+N] = "7"; row11[6+N] = "8";

    const row12 = createRow();
    for (let i = 0; i < N; i++) row12[4+i] = String(i+1);

    const row13 = createRow();
    row13[0] = "Related\nCO";
    for (let i = 0; i < N; i++) {
        let coVals = labPlans[i]?.related_co;
        if(coVals && String(coVals).trim() !== '') {
            let formatted = String(coVals).split(',').map(s => {
                let sTrim = s.trim();
                if(sTrim === '') return '';
                return sTrim.toLowerCase().startsWith('co') ? sTrim.toUpperCase() : 'CO' + sTrim.toUpperCase();
            }).filter(Boolean).join(', ');
            row13[4+i] = formatted;
        }
    }

    const wsData = [row0, row1, row2, row3, row4, row5, row6, row7, row8, row9, row10, row11, row12, row13];

    students.forEach(s => {
      const srow = createRow();
      srow[0] = s.roll_no || '-';
      srow[1] = s.enrollment_no;
      srow[2] = s.seat_number || '-';
      srow[3] = s.full_name || s.name;
      
      let studentSum = 0;
      let assessedCount = 0;
      for (let i = 0; i < N; i++) {
        const pNo = labPlans[i] ? labPlans[i].practical_no : i + 1;
        const prRecord = Array.isArray(allMarks) ? allMarks.find(m => m.student_id === s.id && String(m.practical_no) === String(pNo)) : null;
        if (prRecord) {
          const prMark = (parseFloat(prRecord.process_marks) || 0) + (parseFloat(prRecord.product_marks) || 0);
          srow[4 + i] = prMark;
          studentSum += prMark;
          assessedCount++;
        } else {
          srow[4 + i] = '-';
        }
      }
      if (studentSum > 0 && assessedCount > 0) {
        srow[4 + N] = studentSum;
        const scaleFactor = faMax / (sessionMax * assessedCount);
        srow[5 + N] = Math.round(studentSum * scaleFactor);
      } else {
        srow[4 + N] = '-';
        srow[5 + N] = '-';
      }
      srow[6 + N] = '';
      wsData.push(srow);
    });

    wsData.push([], [], ["Signature of Faculty", "", "", "", "", "", "", "", "", "", "", "", "", "", "Signature of HoD"]);
    wsData.push(["Name", "", "", "", "", "", "", "", "", "", "", "", "", "", "Name"]);
    wsData.push([]);
    wsData.push(["Note: 1. Fractional marks shall be rounded to nearest integer number"]);
    wsData.push(["      2. Faculty shall add more columns as per requirement"]);

    const ws = window.XLSX.utils.aoa_to_sheet(wsData);
    
    for (const key in ws) {
      if (key[0] === '!') continue;
      const cellAddress = window.XLSX.utils.decode_cell(key);
      ws[key].s = {
        alignment: { 
          wrapText: (cellAddress.r >= 10 && cellAddress.r !== wsData.length - 5) || cellAddress.r === 1,
          vertical: 'center', 
          horizontal: (cellAddress.r >= 10 && cellAddress.c === 3) ? 'left' : (cellAddress.r < 10 && cellAddress.c === 0 ? 'left' : 'center')
        },
        font: { name: 'Calibri', sz: cellAddress.r >= 10 ? 10 : 11, bold: cellAddress.r < 13 }
      };
      if (cellAddress.r >= 10 && cellAddress.r < wsData.length - 5) {
        ws[key].s.border = { top: {style:'thin'}, bottom: {style:'thin'}, left: {style:'thin'}, right: {style:'thin'} };
      }
    }
    
    ws['!merges'] = [
      { s: {r:2, c:5}, e: {r:2, c: Math.max(5, 4+N-1)} },
      { s: {r:3, c:5}, e: {r:3, c: Math.max(5, 4+N-1)} },
      { s: {r:10, c:0}, e: {r:11, c:0} },
      { s: {r:10, c:1}, e: {r:11, c:1} },
      { s: {r:10, c:2}, e: {r:11, c:2} },
      { s: {r:10, c:3}, e: {r:11, c:3} },
      { s: {r:10, c:4}, e: {r:10, c: 4+N-1} },
      { s: {r:11, c:4}, e: {r:11, c: 4+N-1} },
      { s: {r:10, c:4+N}, e: {r:12, c:4+N} },
      { s: {r:10, c:5+N}, e: {r:12, c:5+N} },
      { s: {r:10, c:6+N}, e: {r:12, c:6+N} },
      { s: {r:13, c:0}, e: {r:13, c:3} }
    ];

    const widthsConfig = [{wch: 8}, {wch: 15}, {wch: 15}, {wch: 30}];
    for (let i = 0; i < N; i++) widthsConfig.push({wch: 4});
    widthsConfig.push({wch: 12}, {wch: 25}, {wch: 18});
    ws['!cols'] = widthsConfig;

    const wb = window.XLSX.utils.book_new();
    window.XLSX.utils.book_append_sheet(wb, ws, `Format_K3_FA_PR`);
    window.XLSX.writeFile(wb, `K3_FA_PR_Sheet_${course.code}.xlsx`);
  };

  if (loading && students.length === 0 && !initDone) return <div className="p-8 text-center text-lg">Loading Assessment Data...</div>;

  const practicalCount = labPlans.length;
  const conductedSet = new Set(labPlans.filter(p => p.actual_date || p.planned_date).map(p => p.practical_no));

  // Inline styles for sticky columns
  const stickyCol0 = { position: 'sticky', left: 0, zIndex: 12, minWidth: COL_ROLL_W, width: COL_ROLL_W };
  const stickyCol1 = { position: 'sticky', left: COL_ROLL_W, zIndex: 12, minWidth: COL_ENROLL_W, width: COL_ENROLL_W };
  const stickyCol2 = { position: 'sticky', left: COL_ROLL_W + COL_ENROLL_W, zIndex: 12, minWidth: COL_SEAT_W, width: COL_SEAT_W };
  const stickyCol3 = { position: 'sticky', left: COL_ROLL_W + COL_ENROLL_W + COL_SEAT_W, zIndex: 12, minWidth: COL_NAME_W, width: COL_NAME_W, borderRight: '2px solid #cbd5e1' };

  const stickyHeadCol0 = { ...stickyCol0, zIndex: 32 };
  const stickyHeadCol1 = { ...stickyCol1, zIndex: 32 };
  const stickyHeadCol2 = { ...stickyCol2, zIndex: 32 };
  const stickyHeadCol3 = { ...stickyCol3, zIndex: 32 };

  return (
    <div className="max-w-full mx-auto p-2 sm:p-4">
      <div className="bg-white rounded-xl shadow-xl overflow-hidden border border-gray-200">
        <div className="bg-blue-800 p-4 text-white text-center shadow-inner">
          <h2 className="text-2xl font-bold tracking-wider">Format-K3</h2>
          <p className="text-blue-200 text-sm mt-1">Formative Assessment of Practical (FA-PR) — Grid Entry</p>
        </div>
        
        {/* Filters Row */}
        <div className="flex flex-wrap items-end p-3 bg-gray-50 border-b border-gray-200 gap-3">
          <div className="flex flex-col">
            <label className="text-[10px] font-bold text-gray-500 uppercase mb-1">Term</label>
            <select className="border border-red-300 rounded px-2 py-1.5 font-bold bg-red-50 text-red-900 text-sm" value={selectedExam} onChange={e => setSelectedExam(e.target.value)}>
              {exams.length === 0 && <option value="">No Terms</option>}
              {exams.map(e => <option key={e.id} value={e.id}>{e.season} {e.year}</option>)}
            </select>
          </div>
          <div className="flex flex-col">
            <label className="text-[10px] font-bold text-gray-500 uppercase mb-1">Course</label>
            <input 
              type="text" placeholder="Search..." value={courseSearchQuery} onChange={e => setCourseSearchQuery(e.target.value)}
              className="w-36 border border-gray-300 rounded px-2 py-1 text-xs bg-white mb-1 focus:ring-2 outline-none"
            />
            <select className="border border-gray-300 rounded px-2 py-1.5 text-sm bg-white" value={selectedCourse} onChange={e => setSelectedCourse(e.target.value)}>
              {courses.length === 0 && <option value="">No Course</option>}
              {courses.filter(c => (c.code + ' ' + c.name).toLowerCase().includes(courseSearchQuery.toLowerCase())).map(c => <option key={c.id} value={c.id}>{c.code} - {c.name}</option>)}
            </select>
          </div>
          <div className="flex flex-col">
            <label className="text-[10px] font-bold text-gray-500 uppercase mb-1">Batch</label>
            <select className="border border-indigo-300 rounded px-2 py-1.5 text-sm bg-indigo-50 font-bold text-indigo-900" value={selectedBatch} onChange={e => setSelectedBatch(e.target.value)}>
              {batches.length === 0 && <option value="">No Batches</option>}
              {batches.map(b => <option key={b.id} value={b.id}>{b.batch_name}</option>)}
            </select>
          </div>
          <div className="flex flex-col">
            <label className="text-[10px] font-bold text-gray-500 uppercase mb-1">Max / Practical</label>
            <div className="flex items-center gap-1">
              <input type="number" min="1" className="w-16 border border-gray-300 rounded text-center text-sm px-1 py-1.5 font-bold" value={sessionMax} onChange={e => setSessionMax(parseInt(e.target.value) || 25)} />
              {(() => { const c = courses.find(x => String(x.id) === String(selectedCourse)); return c && c.fa_pr_min > 0 ? <span className="text-[10px] text-gray-400 font-bold">Min:{c.fa_pr_min}</span> : null; })()}
            </div>
          </div>
          
          <div className="ml-auto flex gap-2 flex-wrap items-center">
            <button onClick={handleDeleteAllData} className="flex items-center gap-1 px-3 py-1.5 bg-white border border-red-300 text-red-700 hover:bg-red-600 hover:text-white transition shadow rounded-md font-medium text-xs">
              🗑 Delete All
            </button>
            <button onClick={handleClearAll} className="px-3 py-1.5 bg-white border border-red-200 text-red-600 hover:bg-red-50 transition shadow rounded-md font-medium text-xs">
              Clear Grid
            </button>
            <button onClick={handleExportExcel} className="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 transition shadow rounded-md text-white font-medium text-xs">
              📥 Excel
            </button>
            <button onClick={handleSave} className="px-4 py-1.5 bg-emerald-600 hover:bg-emerald-700 transition shadow rounded-md text-white font-bold text-sm">
              💾 Save All
            </button>
          </div>
        </div>

        {/* Lab Plan Gate */}
        {!hasLabPlan && !loading && initDone && (
          <div className="bg-amber-50 border-b border-amber-200 p-6 text-center">
            <div className="flex items-center justify-center gap-3 text-amber-800">
              <svg className="w-8 h-8 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" /></svg>
              <div>
                <p className="font-bold text-lg">Lab Plan Required</p>
                <p className="text-sm mt-1">Please create a Lab Plan (K2-A) for this course, batch, and term before entering K3 marks.</p>
                <p className="text-xs text-amber-600 mt-2">Go to <strong>K2-A Lab Plan</strong> from the sidebar to create one.</p>
              </div>
            </div>
          </div>
        )}

        {/* Grid Table */}
        {hasLabPlan && (
          <div className="relative" style={{ maxHeight: 'calc(100vh - 260px)' }}>
            {loading && (
              <div className="absolute inset-0 bg-white/60 backdrop-blur-[2px] z-40 flex items-center justify-center">
                <div className="bg-white p-4 rounded-xl shadow-xl flex items-center gap-3 border border-blue-100 font-bold text-blue-800">
                  <svg className="animate-spin h-5 w-5 text-blue-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
                  Loading...
                </div>
              </div>
            )}
            <div className="overflow-auto" style={{ maxHeight: 'calc(100vh - 260px)' }}>
              <table className="text-left text-sm text-gray-700 border-collapse" style={{ minWidth: STICKY_TOTAL + practicalCount * 60 + 120 }}>
                <thead className="sticky top-0 z-20">
                  <tr className="bg-gray-100 border-b border-gray-300">
                    <th rowSpan="2" className="px-2 py-2 text-center text-[10px] uppercase font-bold text-gray-500 border border-gray-200 bg-gray-100" style={stickyHeadCol0}>Roll</th>
                    <th rowSpan="2" className="px-2 py-2 text-[10px] uppercase font-bold text-gray-500 border border-gray-200 bg-gray-100" style={stickyHeadCol1}>Enrollment</th>
                    <th rowSpan="2" className="px-2 py-2 text-[10px] uppercase font-bold text-gray-500 border border-gray-200 bg-gray-100 text-center" style={stickyHeadCol2}>Seat</th>
                    <th rowSpan="2" className="px-2 py-2 text-[10px] uppercase font-bold text-gray-500 border border-gray-200 bg-gray-100" style={stickyHeadCol3}>Student Name</th>
                    {labPlans.map(p => {
                      const isConducted = conductedSet.has(p.practical_no);
                      return (
                        <th key={p.practical_no} className={`px-1 py-1 text-center border border-gray-200 min-w-[58px] ${isConducted ? 'bg-blue-50' : 'bg-gray-50'}`}>
                          <div className={`text-[9px] font-bold leading-tight ${isConducted ? 'text-blue-800' : 'text-gray-400'}`}>P{p.practical_no}</div>
                          {p.related_co && (
                             <div className="text-[7px] text-purple-600 font-bold leading-tight mt-0.5">
                                {String(p.related_co).split(',').map(c => c.trim().toLowerCase().startsWith('co') ? c.trim().toUpperCase() : 'CO'+c.trim().toUpperCase()).join(', ')}
                             </div>
                          )}
                          <div className="text-[7px] text-gray-500 leading-tight truncate max-w-[56px] mt-0.5" title={p.title}>{p.title || ''}</div>
                          {p.practical_no > 1 && (
                            <button 
                              onClick={() => handleCopyFromPrevious(p.practical_no)} 
                              className="mt-0.5 text-[7px] px-1 py-0 bg-blue-100 text-blue-600 rounded hover:bg-blue-600 hover:text-white transition font-bold border border-blue-200"
                              title={`Copy all marks from P${p.practical_no - 1} → P${p.practical_no}`}
                            >
                              ← Copy
                            </button>
                          )}
                        </th>
                      );
                    })}
                    <th rowSpan="2" className="px-2 py-2 text-center text-[10px] uppercase font-bold text-emerald-700 border border-gray-200 bg-emerald-50 min-w-[50px]">Total</th>
                    <th rowSpan="2" className="px-2 py-2 text-center text-[10px] uppercase font-bold text-purple-700 border border-gray-200 bg-purple-50 min-w-[50px]">FA-PR</th>
                  </tr>
                  <tr className="bg-gray-50 border-b border-gray-300">
                    {labPlans.map(p => (
                      <th key={p.practical_no} className="px-1 py-0.5 text-center text-[7px] text-gray-400 border border-gray-200 bg-blue-50/30">
                        Max:{sessionMax}
                      </th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {students.map((student) => {
                    const { sum, count } = getStudentTotal(student.id);
                    const fa = getFAMarks(student.id);
                    return (
                      <tr key={student.id} className="hover:bg-blue-50/30 border-b transition-colors group">
                        <td className="px-2 py-1 text-center text-gray-400 text-xs border border-gray-100 bg-white group-hover:bg-blue-50/30" style={stickyCol0}>{student.roll_no}</td>
                        <td className="px-2 py-1 font-mono text-gray-500 text-xs border border-gray-100 bg-white group-hover:bg-blue-50/30" style={stickyCol1}>{student.enrollment_no}</td>
                        <td className="px-2 py-1 font-mono text-center font-bold text-emerald-700 text-xs border border-gray-100 bg-white group-hover:bg-blue-50/30" style={stickyCol2}>{student.seat_number || '-'}</td>
                        <td className="px-2 py-1 font-medium text-gray-800 text-xs border border-gray-100 whitespace-nowrap bg-white group-hover:bg-blue-50/30" style={stickyCol3}>{student.full_name || student.name}</td>
                        {labPlans.map(p => {
                          const isConducted = conductedSet.has(p.practical_no);
                          return (
                            <td key={p.practical_no} className={`px-0.5 py-0.5 text-center border border-gray-100 ${!isConducted ? 'bg-gray-50/50' : ''}`}>
                              <input
                                type="number"
                                min="0"
                                max={sessionMax}
                                value={gridMarks[student.id]?.[p.practical_no] ?? ''}
                                onChange={e => handleMarkChange(student.id, p.practical_no, e.target.value)}
                                className={`w-12 px-1 py-1 border rounded text-center text-xs font-bold outline-none transition ${isConducted ? 'border-gray-200 text-blue-900 focus:ring-2 focus:ring-blue-300 bg-white hover:bg-blue-50/30' : 'border-gray-100 text-gray-400 bg-gray-50'}`}
                                tabIndex={isConducted ? 0 : -1}
                              />
                            </td>
                          );
                        })}
                  <td className="px-2 py-1 text-center font-bold text-emerald-700 text-xs border border-gray-100 bg-emerald-50/20">
                          {count > 0 ? sum : '-'}
                        </td>
                        <td className="px-2 py-1 text-center font-bold text-purple-700 text-xs border border-gray-100 bg-purple-50/20">
                          <span className="bg-purple-100 px-2 py-0.5 rounded-full text-[10px]">{count > 0 ? fa : '-'}</span>
                        </td>
                      </tr>
                    );
                  })}
                  {students.length === 0 && !loading && (
                    <tr><td colSpan={4 + practicalCount + 2} className="text-center py-10 text-gray-400 font-medium">No students found for this course / batch.</td></tr>
                  )}
                </tbody>
              </table>
            </div>
          </div>
        )}

        {/* Footer info */}
        {hasLabPlan && practicalCount > 0 && (
          <div className="p-2 bg-gray-50 border-t border-gray-200 flex items-center justify-between text-xs text-gray-500">
            <span>📊 {students.length} students × {practicalCount} practicals | Max: {sessionMax}{(() => { const c = courses.find(x => String(x.id) === String(selectedCourse)); return c && c.fa_pr_min > 0 ? ` | Min Pass: ${c.fa_pr_min}` : ''; })()} | Batch: {batches.find(b => String(b.id) === String(selectedBatch))?.batch_name || '-'}</span>
            <span className="text-gray-400">← Copy in column header copies all marks from previous practical</span>
          </div>
        )}
      </div>
    </div>
  );
};
window.FormatK3 = FormatK3;
