/* ============================================================
   Admin view — front-office aggregate + certificate issuance
   ============================================================ */

function AdminOverview({ ctx }) {
  const students = window.Store.listStudents();
  const n = students.length;
  // Only count uploaded/upload-source docs against the verified total
  // so the denominator matches the live DOCUMENTS list.
  const uploadDocs = (window.DOCUMENTS || []).filter(d => d.source === 'upload' || !d.source);
  const totalDocs = n * uploadDocs.length;
  const verifiedDocs = students.reduce((sum, s) =>
    sum + Object.values(s.documents || {}).filter(d => d && d.status === 'verified').length, 0);
  const T = window.Store.getSettings().certThreshold;
  const cleared = students.filter(s => s.issuance?.cleared).length;
  const ready = students.filter(s => !s.issuance?.cleared && (window.calcProgress(s)?.overall || 0) >= T).length;

  // Cohort averages — guard against an empty cohort (NaN%).
  const modules = ['registration','documents','medical','tests','attendance','theory','social'];
  const moduleAvg = (mod) => n
    ? Math.round(students.reduce((sum, s) => sum + ((window.calcProgress(s) || {})[mod + 'Pct'] || 0), 0) / n)
    : 0;
  const avgOverall = n
    ? Math.round(students.reduce((s, x) => s + ((window.calcProgress(x) || {}).overall || 0), 0) / n)
    : 0;
  const verifiedPct = totalDocs ? Math.round(verifiedDocs / totalDocs * 100) : 0;

  return (
    <div className="module">
      <ModuleHeader
        crumbs="ACCOUNTABLE MANAGER"
        title="Cohort overview"
        sub="Live view of all students. Use this to plan certificate issuance and follow-ups."
      />

      <div className="grid g-4">
        <div className="kpi accent">
          <div className="lbl">Active students</div>
          <div className="val">{n}</div>
          <div className="delta">{students[0]?.profile?.batch ? `Batch ${students[0].profile.batch}` : '—'}</div>
        </div>
        <div className="kpi">
          <div className="lbl">Ready for certificate</div>
          <div className="val">{ready}</div>
          <div className="delta">{cleared} already issued</div>
        </div>
        <div className="kpi">
          <div className="lbl">Documents verified</div>
          <div className="val">{verifiedDocs}<span className="unit">/{totalDocs}</span></div>
          <div className="delta">{verifiedPct}% of all required documents</div>
        </div>
        <div className="kpi">
          <div className="lbl">Avg overall progress</div>
          <div className="val">{avgOverall}<span className="unit">%</span></div>
          <div className="delta">Across the cohort</div>
        </div>
      </div>

      <div className="grid g-side">
        <div className="card">
          <div className="card-hd">
            <div><h3>Module completion · cohort average</h3><div className="sub">Weighted bars show how each module is tracking.</div></div>
          </div>
          <div className="card-body stack-sm" style={{gap:14}}>
            {modules.map(m => {
              const v = moduleAvg(m);
              return (
                <div key={m}>
                  <div className="row" style={{justifyContent:'space-between',marginBottom:4}}>
                    <span className="small" style={{textTransform:'capitalize'}}>{m}</span>
                    <span className="small mono">{v}%</span>
                  </div>
                  <div className="bar"><i style={{
                    width:`${v}%`,
                    background: v >= 80 ? 'var(--good)' : v >= 50 ? 'var(--accent)' : 'var(--warn)'}}/></div>
                </div>
              );
            })}
          </div>
        </div>

        <div className="card">
          <div className="card-hd"><h3>Issuance pipeline</h3></div>
          <div className="card-body">
            <PipelineRow label="In progress" count={students.length - ready - cleared} kind="ghost"/>
            <PipelineRow label="Ready to issue" count={ready} kind="warn"/>
            <PipelineRow label="Issued" count={cleared} kind="good"/>
            <div className="divider"/>
            <button className="btn btn-primary btn-sm" onClick={()=>ctx.setRoute('issuance')}>Open issuance →</button>
          </div>
        </div>
      </div>

      <div className="card" style={{padding:0}}>
        <div className="card-hd">
          <div><h3>All students</h3></div>
          <button className="btn btn-sm" onClick={()=>ctx.setRoute('roster')}>Open roster →</button>
        </div>
        <table className="tbl tbl-wide">
          <thead><tr>
            <th>Student</th><th>Reg.</th><th>Docs</th><th>Med.</th><th>Tests</th><th>Att.</th><th>Theory</th><th>Social</th><th className="right">Overall</th><th>Cert.</th>
          </tr></thead>
          <tbody>
            {students.map(s => {
              const p = window.calcProgress(s);
              return (
                <tr key={s.id}>
                  <td>
                    <div className="fw-6 small">{s.profile.fullName}</div>
                    <div className="tiny muted mono">{s.profile.rollNo}</div>
                  </td>
                  <Cell pct={p.registrationPct}/>
                  <Cell pct={p.documentsPct}/>
                  <Cell pct={p.medicalPct}/>
                  <Cell pct={p.testsPct}/>
                  <Cell pct={p.attendancePct}/>
                  <Cell pct={p.theoryPct}/>
                  <Cell pct={p.socialPct}/>
                  <td className="right mono fw-6">{p.overall}%</td>
                  <td>{s.issuance?.cleared ? <StatusPill status="verified"/> : (p.overall >= T ? <StatusPill status="submitted"/> : <span className="tiny muted">—</span>)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function Cell({ pct }) {
  return (
    <td>
      <div className="row" style={{gap:6}}>
        <div className="bar" style={{width:54}}><i style={{width:`${pct}%`,
          background: pct === 100 ? 'var(--good)' : pct > 0 ? 'var(--accent)' : 'var(--line-strong)'}}/></div>
        <span className="tiny mono muted" style={{width:24,textAlign:'right'}}>{pct}</span>
      </div>
    </td>
  );
}

function PipelineRow({ label, count, kind }) {
  const c = kind === 'good' ? 'var(--good)' : kind === 'warn' ? 'var(--warn)' : 'var(--muted)';
  return (
    <div className="row" style={{justifyContent:'space-between',padding:'8px 0',borderBottom:'1px solid var(--line)'}}>
      <div className="row" style={{gap:8}}>
        <span style={{width:8,height:8,borderRadius:50,background:c}}/>
        <span className="small">{label}</span>
      </div>
      <span className="small mono fw-6">{count}</span>
    </div>
  );
}

/* ============================================================
   ADMIN ROSTER — same as officer's, simpler view
   ============================================================ */
function AdminRoster({ ctx }) {
  return <OfficerRoster ctx={ctx}/>;
}

/* ============================================================
   ADMIN ISSUANCE — release final certificates
   ============================================================ */
function AdminIssuance({ ctx }) {
  const students = window.Store.listStudents();
  const T = window.Store.getSettings().certThreshold;
  const eligible = students.filter(s => window.calcProgress(s).overall >= T && !s.issuance?.cleared);
  const issued   = students.filter(s => s.issuance?.cleared);
  const pending  = students.filter(s => window.calcProgress(s).overall < T && !s.issuance?.cleared);

  const issue = (sid) => {
    if (!confirm('Issue the final RPTO certificate? This will be visible to the student immediately.')) return;
    let ref = '';
    window.Store.patchStudent(sid, (r) => {
      r.issuance.cleared = true;
      r.issuance.clearedAt = new Date().toISOString();
      r.issuance.clearedBy = ctx.session.userName;
      ref = `IDA/RPC/${new Date().getFullYear()}/${String(Math.floor(Math.random()*9000+1000))}`;
      r.issuance.refNumber = ref;
      return r;
    });
    const st = window.Store.getStudent(sid);
    window.Store.logAudit('Certificate issued', st?.profile?.fullName || sid, ref);
    ctx.setToast('Certificate issued');
  };
  const revoke = (sid) => {
    if (!confirm('Revoke this certificate?')) return;
    window.Store.patchStudent(sid, (r) => {
      r.issuance.cleared = false;
      r.issuance.refNumber = '';
      return r;
    });
    const st = window.Store.getStudent(sid);
    window.Store.logAudit('Certificate revoked', st?.profile?.fullName || sid, '');
  };

  return (
    <div className="module">
      <ModuleHeader
        crumbs="ACCOUNTABLE MANAGER · ISSUANCE"
        title="Certificate issuance"
        sub={`Release the final RPTO certificate to students who have crossed the ${T}% verification threshold.`}
        right={<div className="row" style={{gap:8}}>
          <div className="pill pill-warn"><i className="dotty"/>{eligible.length} ready</div>
          <div className="pill pill-good"><i className="dotty"/>{issued.length} issued</div>
        </div>}
      />

      {eligible.length > 0 && (
        <div className="card" style={{padding:0}}>
          <div className="card-hd"><div><h3>Eligible for issuance</h3><div className="sub">Crossed {T}% — review and release.</div></div></div>
          <table className="tbl">
            <thead><tr><th>Student</th><th>Overall</th><th>Pending items</th><th>Action</th></tr></thead>
            <tbody>{eligible.map(s => {
              const p = window.calcProgress(s);
              return (
                <tr key={s.id}>
                  <td>
                    <div className="row" style={{gap:8}}>
                      <div className="brand-mark" style={{width:30,height:30}}>{UI.initials(s.profile.fullName)}</div>
                      <div>
                        <div className="fw-6 small">{s.profile.fullName}</div>
                        <div className="tiny muted mono">{s.profile.rollNo}</div>
                      </div>
                    </div>
                  </td>
                  <td><div className="row" style={{gap:8}}>
                    <div className="bar" style={{width:80}}><i style={{width:`${p.overall}%`,background:'var(--good)'}}/></div>
                    <span className="mono fw-6 small">{p.overall}%</span>
                  </div></td>
                  <td><PendingFlags rec={s}/></td>
                  <td><button className="btn btn-primary btn-sm" onClick={()=>issue(s.id)}>Issue certificate</button></td>
                </tr>
              );
            })}</tbody>
          </table>
        </div>
      )}

      {issued.length > 0 && (
        <div className="card" style={{padding:0}}>
          <div className="card-hd"><div><h3>Issued</h3><div className="sub">Already released.</div></div></div>
          <table className="tbl">
            <thead><tr><th>Student</th><th>Ref. No.</th><th>Issued on</th><th>By</th><th></th></tr></thead>
            <tbody>{issued.map(s => (
              <tr key={s.id}>
                <td><div className="fw-6 small">{s.profile.fullName}</div></td>
                <td className="mono small">{s.issuance.refNumber}</td>
                <td className="small muted">{UI.fmtDateTime(s.issuance.clearedAt)}</td>
                <td className="small muted">{s.issuance.clearedBy}</td>
                <td className="right"><button className="btn btn-xs" onClick={()=>revoke(s.id)}>Revoke</button></td>
              </tr>
            ))}</tbody>
          </table>
        </div>
      )}

      {pending.length > 0 && (
        <div className="card" style={{padding:0}}>
          <div className="card-hd"><div><h3>Still in progress</h3><div className="sub">Need to reach {T}% before issuance opens.</div></div></div>
          <table className="tbl">
            <thead><tr><th>Student</th><th>Overall</th><th>Next action</th></tr></thead>
            <tbody>{pending.map(s => {
              const p = window.calcProgress(s);
              return (
                <tr key={s.id}>
                  <td><div className="fw-6 small">{s.profile.fullName}</div></td>
                  <td><div className="row" style={{gap:8}}>
                    <div className="bar" style={{width:80}}><i style={{width:`${p.overall}%`,background:'var(--accent)'}}/></div>
                    <span className="mono fw-6 small">{p.overall}%</span>
                  </div></td>
                  <td className="small muted">{p.next.label}</td>
                </tr>
              );
            })}</tbody>
          </table>
        </div>
      )}
    </div>
  );
}

/* ============================================================
   ADMIN SETTINGS — a couple of toggles for demo
   ============================================================ */
function AdminSettings({ ctx }) {
  const s = window.Store.getSettings();
  const [cert, setCert] = useState(String(s.certThreshold));
  const [theory, setTheory] = useState(String(s.theoryPassPct));
  const [savingP, setSavingP] = useState(false);
  const [resetSent, setResetSent] = useState(false);

  const saveProgram = async () => {
    const c = Math.max(1, Math.min(100, parseInt(cert, 10) || 0));
    const t = Math.max(1, Math.min(100, parseInt(theory, 10) || 0));
    setSavingP(true);
    try {
      await window.Store.saveSettings({ certThreshold: c, theoryPassPct: t });
      setCert(String(c)); setTheory(String(t));
      ctx.setToast('Program settings saved');
    } catch (e) {
      ctx.setToast(e.message || 'Could not save settings');
    } finally { setSavingP(false); }
  };

  const exportCsv = () => {
    const rows = window.Store.listStudents();
    const cols = ['fullName','email','phone','batch','course','startDate','rollNo'];
    const head = [...cols, 'overallPct', 'certCleared', 'updatedAt'];
    const esc = (v) => `"${String(v == null ? '' : v).replace(/"/g, '""')}"`;
    const lines = [head.join(',')];
    rows.forEach(r => {
      const p = window.calcProgress(r) || {};
      lines.push([
        ...cols.map(k => esc(r.profile?.[k])),
        p.overall ?? '', r.issuance?.cleared ? 'yes' : 'no', esc(r.updatedAt),
      ].join(','));
    });
    const blob = new Blob([lines.join('\n')], { type: 'text/csv' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = `ida-cohort-${new Date().toISOString().slice(0,10)}.csv`;
    a.click(); URL.revokeObjectURL(a.href);
    ctx.setToast(`Exported ${rows.length} student${rows.length===1?'':'s'}`);
  };

  const emailReset = async () => {
    try {
      await window.Auth.sendReset(ctx.session.email);
      setResetSent(true);
      ctx.setToast('Password reset email sent');
    } catch (e) { ctx.setToast(e.message || 'Could not send reset email'); }
  };

  return (
    <div className="module">
      <ModuleHeader
        crumbs="ACCOUNTABLE MANAGER · SETTINGS"
        title="Settings"
        sub="Program rules, accounts, data and your security."
      />
      <div className="grid g-2">
        <div className="card">
          <div className="card-hd">
            <div><h3>Program rules</h3>
              <div className="sub">Applied live across the whole cohort.</div></div>
          </div>
          <div className="card-body stack-sm">
            <div className="field">
              <label>Certificate eligibility threshold (%)</label>
              <input className="input" type="number" min="1" max="100"
                value={cert} onChange={e=>setCert(e.target.value)}/>
              <div className="input-help">Overall progress a student must reach before the certificate can be issued.</div>
            </div>
            <div className="field">
              <label>Theory test pass mark (%)</label>
              <input className="input" type="number" min="1" max="100"
                value={theory} onChange={e=>setTheory(e.target.value)}/>
              <div className="input-help">Minimum overall score to pass the theory test.</div>
            </div>
            <div className="row">
              <button className="btn btn-primary btn-sm" disabled={savingP} onClick={saveProgram}>
                {savingP ? 'Saving…' : 'Save program rules'}
              </button>
            </div>
          </div>
        </div>

        <div className="card">
          <div className="card-hd"><h3>Accounts</h3></div>
          <div className="card-body">
            <p className="small muted" style={{margin:'0 0 10px'}}>Create instructor / manager logins and manage every account's role.</p>
            <div className="row" style={{gap:8,flexWrap:'wrap'}}>
              <button className="btn btn-sm" onClick={()=>ctx.setRoute('users')}>User management →</button>
              <button className="btn btn-sm" onClick={()=>ctx.setRoute('instructors')}>Instructor activity →</button>
            </div>
          </div>
        </div>

        <div className="card">
          <div className="card-hd">
            <div><h3>Data export</h3><div className="sub">Snapshot of the cohort for records / reporting.</div></div>
          </div>
          <div className="card-body">
            <p className="small muted" style={{margin:'0 0 10px'}}>
              Download every student — profile, course, overall progress and certificate status — as CSV.
            </p>
            <button className="btn btn-sm" onClick={exportCsv}>
              {window.NavIcons.download} Export cohort (CSV)
            </button>
          </div>
        </div>

        <div className="card">
          <div className="card-hd"><h3>Tooling</h3></div>
          <div className="card-body stack-sm">
            <a className="btn btn-sm" href="assets/Registration-Form.docx" download>{window.NavIcons.download} Registration template (.docx)</a>
            <a className="btn btn-sm" href="assets/Medical-Fitness-Certificate-RPA.pdf" target="_blank">{window.NavIcons.download} Medical template (PDF)</a>
            <a className="btn btn-sm" href="assets/DGCA-Progress-Skill-Test.pdf" target="_blank">{window.NavIcons.download} DGCA Progress/Skill template (PDF)</a>
          </div>
        </div>

        <div className="card">
          <div className="card-hd"><h3>Your account & security</h3></div>
          <div className="card-body">
            <div className="small" style={{marginBottom:4}}><b>{ctx.session.userName}</b></div>
            <div className="tiny muted mono" style={{marginBottom:2}}>{ctx.session.email}</div>
            <div className="tiny muted" style={{marginBottom:12}}>Role · Accountable Manager</div>
            <div className="row" style={{gap:8,flexWrap:'wrap'}}>
              <button className="btn btn-sm" disabled={resetSent} onClick={emailReset}>
                {resetSent ? 'Reset email sent' : 'Email me a password reset'}
              </button>
              <button className="btn btn-sm btn-bad" onClick={()=>window.Auth.signOut()}>Sign out</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   MANAGER — Instructors (activity rollup, no schema change:
   derived from verifiedBy / examinerName / markedBy on students)
   ============================================================ */
function ManagerInstructors({ ctx }) {
  const users = window.Store.listUsers();
  const students = window.Store.listStudents();
  const staff = users.filter(u => u.role === 'instructor' || u.role === 'manager');

  const stats = {};
  const bump = (who, key, ts) => {
    if (!who) return;
    const s = stats[who] || (stats[who] = { docs:0, medical:0, tests:0, attendance:0, last:'' });
    s[key]++;
    if (ts && ts > s.last) s.last = ts;
  };
  students.forEach(st => {
    Object.values(st.documents || {}).forEach(d => {
      if (d.status === 'verified' && d.verifiedBy) bump(d.verifiedBy, 'docs', d.verifiedAt);
    });
    if (st.medical?.status === 'verified' && st.medical.verifiedBy)
      bump(st.medical.verifiedBy, 'medical', st.medical.verifiedAt);
    ['simulator','rpa','skill'].forEach(k => {
      const t = st.tests?.[k];
      if (t?.finalised && t.examinerName) bump(t.examinerName, 'tests', t.finalisedAt);
    });
    Object.values(st.attendance || {}).forEach(a => {
      if ((a.status === 'present' || a.status === 'absent') && a.markedBy)
        bump(a.markedBy, 'attendance', a.markedAt);
    });
  });

  return (
    <div className="module">
      <ModuleHeader
        crumbs="ACCOUNTABLE MANAGER · INSTRUCTORS"
        title="Instructors"
        sub="Every instructor and manager, with what they've actioned across the cohort."
        right={<div className="pill pill-info"><i className="dotty"/>{staff.length} staff</div>}
      />
      <div className="card" style={{padding:0}}>
        <table className="tbl tbl-wide">
          <thead><tr>
            <th>Name</th><th>Role</th><th>Docs verified</th><th>Medical</th>
            <th>Tests filed</th><th>Attendance</th><th>Last activity</th><th>Status</th>
          </tr></thead>
          <tbody>
            {staff.map(u => {
              const s = stats[u.name] || { docs:0, medical:0, tests:0, attendance:0, last:'' };
              const active = !!s.last;
              return (
                <tr key={u.uid}>
                  <td>
                    <div className="fw-6 small">{u.name || '—'}</div>
                    <div className="tiny muted mono">{u.email}</div>
                  </td>
                  <td><span className="pill pill-ghost">{u.role === 'manager' ? 'Accountable Mgr' : (u.officerRole || 'Instructor')}</span></td>
                  <td className="mono small">{s.docs}</td>
                  <td className="mono small">{s.medical}</td>
                  <td className="mono small">{s.tests}</td>
                  <td className="mono small">{s.attendance}</td>
                  <td className="small muted">{s.last ? UI.ago(s.last) : '—'}</td>
                  <td>{active ? <StatusPill status="verified"/> : <span className="tiny muted">No activity</span>}</td>
                </tr>
              );
            })}
            {staff.length === 0 && (
              <tr><td colSpan={8} className="small muted" style={{padding:'18px',textAlign:'center'}}>
                No instructor or manager accounts yet — create one under User management.
              </td></tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
}

/* ============================================================
   MANAGER — User management (provision staff, manage roles)
   ============================================================ */
function ManagerUsers({ ctx }) {
  const me = ctx.session;
  const users = window.Store.listUsers();
  const [form, setForm] = useState({ name:'', email:'', password:'', role:'instructor', officerRole:'' });
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState(null);     // {kind:'good'|'bad', text}

  const set = (k,v) => setForm(f => ({ ...f, [k]:v }));

  const submit = async (e) => {
    e.preventDefault();
    if (busy) return;
    setMsg(null);
    if (!form.name.trim() || !form.email.trim() || form.password.length < 6) {
      setMsg({ kind:'bad', text:'Name, email and a password of 6+ characters are required.' });
      return;
    }
    setBusy(true);
    try {
      const r = await window.Auth.provisionStaff({
        email: form.email, password: form.password, name: form.name,
        role: form.role, officerRole: form.officerRole,
      });
      window.Store.logAudit('Account created', r.email, form.role);
      setMsg({ kind:'good', text:`Created ${form.role} account for ${r.email}. Share the temporary password and ask them to reset it.` });
      setForm({ name:'', email:'', password:'', role:'instructor', officerRole:'' });
    } catch (err) {
      // No more repair mode — if the email already exists, the manager
      // must remove the stale account (or use the Firebase console).
      const tail = err.code === 'auth/email-already-in-use'
        ? ' If the previous account is no longer needed, remove it from the table below first, then try again.'
        : '';
      setMsg({ kind:'bad', text: (err.message || 'Could not create account.') + tail });
    } finally {
      setBusy(false);
    }
  };

  const changeRole = (u, role) => {
    if (u.uid === me.uid) { alert('You cannot change your own role.'); return; }
    window.Store.setUser(u.uid, {
      role,
      studentId: role === 'student' ? (u.studentId || u.uid) : null,
    });
    window.Store.logAudit('Role changed', u.email || u.name, `${u.role} → ${role}`);
  };
  const toggleStatus = (u) => {
    if (u.uid === me.uid) { alert('You cannot deactivate your own account.'); return; }
    const next = u.status === 'inactive' ? 'active' : 'inactive';
    const verb = next === 'inactive' ? 'Deactivate' : 'Reactivate';
    const tail = next === 'inactive'
      ? "They'll be signed out immediately and cannot sign in until reactivated."
      : "They'll be able to sign in again.";
    if (!confirm(`${verb} ${u.name || u.email}? ${tail}`)) return;
    window.Store.setUser(u.uid, { status: next });
    window.Store.logAudit(next === 'inactive' ? 'Account deactivated' : 'Account reactivated',
      u.email || u.name, u.role);
  };
  const removeUser = async (u) => {
    if (u.uid === me.uid) { alert('You cannot remove your own account.'); return; }
    const sid = u.studentId || (u.role === 'student' ? u.uid : null);
    const lines = [
      `⚠ Hard-remove ${u.name || u.email}'s account?`,
      '',
      'This will permanently delete:',
      `  • users/${u.uid} (profile + role)`,
    ];
    if (sid) {
      lines.push(`  • students/${sid} (registration / medical / progress / theory / instructor uploads)`);
      lines.push(`  • every file under students/${sid}/ in Cloud Storage`);
    }
    lines.push('');
    lines.push('The Firebase Auth login itself can ONLY be removed from the');
    lines.push('Firebase console (Spark plan limitation). They will, however,');
    lines.push('be signed out immediately and unable to sign back in.');
    lines.push('');
    lines.push('This cannot be undone.');
    if (!confirm(lines.join('\n'))) return;
    const typed = prompt(`Type the email "${u.email}" to confirm:`);
    if (typed == null) return;
    if (typed.trim().toLowerCase() !== String(u.email || '').trim().toLowerCase()) {
      alert('Email did not match — cancelled.');
      return;
    }
    try {
      await window.Store.hardDeleteUser(u);
      window.Store.logAudit('Account removed', u.email || u.name,
        `${u.role}${sid ? ` · purged students/${sid} + Storage` : ''}`);
      ctx.setToast(`${u.email} removed. Their auth login still exists — clear it from the Firebase console if needed.`);
    } catch (err) {
      ctx.setToast(err.message || 'Could not fully remove account');
    }
  };

  // Group by role so each pillar (Students / Instructors / Managers)
  // has its own table. Sorted by name within each group.
  const byRole = { student: [], instructor: [], manager: [], other: [] };
  users.forEach(u => {
    if (byRole[u.role]) byRole[u.role].push(u);
    else byRole.other.push(u);
  });
  Object.values(byRole).forEach(arr =>
    arr.sort((a, b) => (a.name || a.email || '').localeCompare(b.name || b.email || '')));

  const groups = [
    { key: 'manager',     label: 'Accountable Managers', sub: 'Full administrative access', rows: byRole.manager },
    { key: 'instructor',  label: 'Instructors',          sub: 'Verify documents, mark attendance, fill test reports', rows: byRole.instructor },
    { key: 'student',     label: 'Students',             sub: 'Self-registered students', rows: byRole.student },
  ];
  if (byRole.other.length) {
    groups.push({ key:'other', label:'Other', sub:'Accounts with an unrecognised role', rows: byRole.other });
  }

  return (
    <div className="module">
      <ModuleHeader
        crumbs="ACCOUNTABLE MANAGER · USER MANAGEMENT"
        title="User management"
        sub="Create instructor / manager logins and manage every account's role. Students self-register."
        right={<div className="pill pill-info"><i className="dotty"/>{users.length} account{users.length===1?'':'s'}</div>}
      />

      <div className="grid g-side">
        <div className="card">
          <div className="card-hd">
            <div>
              <h3>Create a staff account</h3>
              <div className="sub">Creates the login without signing you out. Share the temporary password securely.</div>
            </div>
          </div>
          <form className="card-body stack-sm" onSubmit={submit}>
            {msg && (
              <div className={cls('banner', msg.kind === 'good' ? 'good' : 'bad')}>
                <div className="body">{msg.text}</div>
              </div>
            )}
            <div className="field">
              <label>Full name</label>
              <input className="input" value={form.name} onChange={e=>set('name',e.target.value)} placeholder="A. Krishnan"/>
            </div>
            <div className="field">
              <label>Email</label>
              <input className="input" type="email" value={form.email} onChange={e=>set('email',e.target.value)} placeholder="name@ida.test"/>
            </div>
            <div className="field">
              <label>Temporary password</label>
              <input className="input" type="text" value={form.password} onChange={e=>set('password',e.target.value)} placeholder="At least 6 characters"/>
            </div>
            <div className="grid g-2">
              <div className="field">
                <label>Role</label>
                <select className="select" value={form.role} onChange={e=>set('role',e.target.value)}>
                  <option value="instructor">Instructor</option>
                  <option value="manager">Accountable Manager</option>
                </select>
              </div>
              <div className="field">
                <label>Title (optional)</label>
                <input className="input" value={form.officerRole} onChange={e=>set('officerRole',e.target.value)} placeholder="Chief Instructor"/>
              </div>
            </div>
            <div className="row" style={{justifyContent:'flex-end',alignItems:'center'}}>
              <button className="btn btn-primary btn-sm" type="submit" disabled={busy}>
                {busy ? 'Working…' : 'Create account'}
              </button>
            </div>
          </form>
        </div>

        <div className="card">
          <div className="card-hd"><h3>How removals work</h3></div>
          <div className="card-body small muted" style={{lineHeight:1.6}}>
            <p style={{margin:'0 0 8px'}}>Removing an account is a <b>hard delete</b> — the users doc, the students doc (if any), and every Cloud Storage file under that student's folder are purged together. There is no repair / undo.</p>
            <p style={{margin:'0 0 8px'}}>The Firebase Auth login itself can only be removed from the Firebase console (Spark plan limitation). The deleted account is signed out instantly and cannot re-enter the dashboard because its users doc is gone.</p>
            <p style={{margin:0}}>You'll be asked to type the email to confirm before anything is deleted.</p>
          </div>
        </div>
      </div>

      {groups.map(g => (
        <RoleSection key={g.key} group={g} me={me}
          onChangeRole={changeRole} onToggleStatus={toggleStatus} onRemove={removeUser}/>
      ))}
    </div>
  );
}

function RoleSection({ group, me, onChangeRole, onToggleStatus, onRemove }) {
  return (
    <div className="card" style={{padding:0,marginTop:14}}>
      <div className="card-hd">
        <div>
          <h3>{group.label}</h3>
          <div className="sub">{group.sub}</div>
        </div>
        <span className="pill pill-ghost"><i className="dotty"/>{group.rows.length}</span>
      </div>
      <table className="tbl tbl-wide">
        <thead><tr>
          <th>Name</th><th>Email</th><th>Role</th><th>Status</th>
          <th>Student ID</th><th>Created</th><th></th>
        </tr></thead>
        <tbody>
          {group.rows.map(u => {
            const inactive = u.status === 'inactive';
            return (
              <tr key={u.uid} style={inactive ? { opacity: 0.6 } : undefined}>
                <td className="fw-6 small">
                  {u.name || '—'}{u.uid === me.uid && <span className="tiny muted"> (you)</span>}
                </td>
                <td className="small muted mono">{u.email}</td>
                <td>
                  <select className="select" style={{padding:'4px 8px',fontSize:12}}
                          value={u.role} disabled={u.uid === me.uid}
                          onChange={e=>onChangeRole(u, e.target.value)}>
                    <option value="student">Student</option>
                    <option value="instructor">Instructor</option>
                    <option value="manager">Accountable Manager</option>
                  </select>
                </td>
                <td>
                  <span className={cls('pill', inactive ? 'pill-bad' : 'pill-good')}>
                    <i className="dotty"/>{inactive ? 'Inactive' : 'Active'}
                  </span>
                </td>
                <td className="tiny muted mono">{u.studentId || '—'}</td>
                <td className="tiny muted">{u.createdAt ? UI.fmtDate(u.createdAt) : '—'}</td>
                <td className="right">
                  {u.uid !== me.uid && (
                    <div className="row" style={{gap:5,justifyContent:'flex-end'}}>
                      <button className={cls('btn btn-xs', inactive ? 'btn-good' : 'btn-bad')}
                        onClick={()=>onToggleStatus(u)}
                        title={inactive ? 'Reactivate this account' : "Deactivate — they're signed out immediately"}>
                        {inactive ? 'Reactivate' : 'Deactivate'}
                      </button>
                      <button className="btn btn-xs btn-bad" onClick={()=>onRemove(u)}
                        title="Hard delete: users + students doc + all Storage files">
                        Remove…
                      </button>
                    </div>
                  )}
                </td>
              </tr>
            );
          })}
          {group.rows.length === 0 && (
            <tr><td colSpan={7} className="small muted" style={{padding:'18px',textAlign:'center'}}>
              No {group.label.toLowerCase()} on record.
            </td></tr>
          )}
        </tbody>
      </table>
    </div>
  );
}

/* ============================================================
   MANAGER — Audit / activity log (read-only, immutable trail)
   ============================================================ */
// Group an action string into a coarse category so the filter
// dropdown stays useful even as new audit verbs are added.
function auditCategory(action) {
  const a = (action || '').toLowerCase();
  if (a.includes('reject'))      return 'rejected';
  if (a.includes('verified'))    return 'verified';
  if (a.includes('issued') || a.includes('revoked')) return 'issuance';
  if (a.includes('account') || a.includes('role') || a.includes('signature') || a.includes('deactiv') || a.includes('reactiv')) return 'accounts';
  if (a.includes('batch'))       return 'batches';
  if (a.includes('theory'))      return 'theory';
  if (a.includes('attendance'))  return 'attendance';
  if (a.includes('notice'))      return 'notices';
  if (a.includes('packet') || a.includes('export')) return 'exports';
  if (a.includes('submitted') || a.includes('finalised') || a.includes('uploaded')) return 'submissions';
  return 'other';
}

const AUDIT_CATEGORIES = [
  { id: 'verified',     label: 'Verifications' },
  { id: 'rejected',     label: 'Rejections' },
  { id: 'submissions',  label: 'Submissions' },
  { id: 'theory',       label: 'Theory / Learn' },
  { id: 'attendance',   label: 'Attendance' },
  { id: 'issuance',     label: 'Certificate issuance' },
  { id: 'accounts',     label: 'Accounts & signatures' },
  { id: 'batches',      label: 'Batches' },
  { id: 'notices',      label: 'Notices' },
  { id: 'exports',      label: 'Exports / packets' },
  { id: 'other',        label: 'Other' },
];

const AUDIT_WINDOWS = [
  { id: 'all',  label: 'All time', hours: null },
  { id: '24h',  label: 'Last 24 hours', hours: 24 },
  { id: '7d',   label: 'Last 7 days',  hours: 24 * 7 },
  { id: '30d',  label: 'Last 30 days', hours: 24 * 30 },
];

function ManagerAudit({ ctx }) {
  const all = Object.values((ctx.store && ctx.store.audit) || {})
    .sort((a, b) => (b.ts || '').localeCompare(a.ts || ''));
  const [q, setQ] = useState('');
  const [actorUid, setActorUid] = useState('all');
  const [category, setCategory] = useState('all');
  const [window_, setWindow] = useState('all');

  // Build the actor dropdown from the audit data, but only surface
  // actors whose account STILL EXISTS — a removed user's historical
  // entries remain in the (immutable) log, yet cluttering the filter
  // with deleted accounts isn't useful. Entries keyed only by name
  // (legacy, no actorUid) can't be verified, so they're dropped too.
  const liveUsers = (ctx.store && ctx.store.users) || {};
  const actors = (() => {
    const seen = new Map();
    all.forEach(r => {
      const uid = r.actorUid;
      if (!uid || !liveUsers[uid]) return;            // skip removed / unverifiable actors
      if (!seen.has(uid)) seen.set(uid, { uid, name: r.actorName || '—', role: r.actorRole || '' });
    });
    return Array.from(seen.values()).sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  })();
  // If the selected actor was removed, fall back to "all" so the
  // table doesn't silently show nothing.
  useEffect(() => {
    if (actorUid !== 'all' && !liveUsers[actorUid]) setActorUid('all');
  }, [actorUid, liveUsers]);

  const ql = q.trim().toLowerCase();
  const wHours = (AUDIT_WINDOWS.find(w => w.id === window_) || {}).hours;
  const cutoff = wHours ? Date.now() - wHours * 3600 * 1000 : null;

  const rows = all.filter(r => {
    if (actorUid !== 'all' && (r.actorUid || r.actorName) !== actorUid) return false;
    if (category !== 'all' && auditCategory(r.action) !== category) return false;
    if (cutoff && new Date(r.ts).getTime() < cutoff) return false;
    if (ql) {
      const hay = [r.action, r.target, r.detail, r.actorName, r.actorRole]
        .map(v => String(v || '').toLowerCase()).join(' ');
      if (!hay.includes(ql)) return false;
    }
    return true;
  });

  const tone = (action) => {
    const a = (action || '').toLowerCase();
    if (a.includes('reject') || a.includes('revoked') || a.includes('removed') || a.includes('failed') || a.includes('deactiv')) return 'pill-bad';
    if (a.includes('verified') || a.includes('passed') || a.includes('issued') || a.includes('created') || a.includes('reactiv')) return 'pill-good';
    if (a.includes('submitted') || a.includes('finalised') || a.includes('posted')) return 'pill-warn';
    return 'pill-ghost';
  };

  const reset = () => { setQ(''); setActorUid('all'); setCategory('all'); setWindow('all'); };
  const activeFilters = (q.trim() ? 1 : 0) + (actorUid !== 'all' ? 1 : 0)
    + (category !== 'all' ? 1 : 0) + (window_ !== 'all' ? 1 : 0);

  return (
    <div className="module">
      <ModuleHeader
        crumbs="ACCOUNTABLE MANAGER · ACTIVITY LOG"
        title="Activity log"
        sub="Immutable trail of key actions across the academy (most recent 200)."
        right={<div className="pill pill-info"><i className="dotty"/>{all.length} entries</div>}
      />
      <div className="card" style={{padding:0}}>
        <div className="card-hd">
          <div><h3>Audit trail</h3><div className="sub">
            Showing {rows.length} of {all.length} entries
            {activeFilters > 0 && <>
              {' · '}
              <button className="btn-link tiny" onClick={reset}>Clear filters</button>
            </>}
          </div></div>
        </div>
        <div className="card-body" style={{display:'flex',gap:8,flexWrap:'wrap',padding:'10px 14px',borderBottom:'1px solid var(--line)'}}>
          <input className="input" style={{flex:'1 1 200px',minWidth:160}}
            placeholder="Search target / detail / actor…"
            value={q} onChange={e=>setQ(e.target.value)}/>
          <select className="select" style={{flex:'0 1 200px',minWidth:160}}
            value={actorUid} onChange={e=>setActorUid(e.target.value)}>
            <option value="all">All actors ({actors.length})</option>
            {actors.map(a => (
              <option key={a.uid} value={a.uid}>{a.name}{a.role ? ` · ${a.role}` : ''}</option>
            ))}
          </select>
          <select className="select" style={{flex:'0 1 200px',minWidth:160}}
            value={category} onChange={e=>setCategory(e.target.value)}>
            <option value="all">All actions</option>
            {AUDIT_CATEGORIES.map(c => (
              <option key={c.id} value={c.id}>{c.label}</option>
            ))}
          </select>
          <select className="select" style={{flex:'0 1 160px',minWidth:140}}
            value={window_} onChange={e=>setWindow(e.target.value)}>
            {AUDIT_WINDOWS.map(w => (
              <option key={w.id} value={w.id}>{w.label}</option>
            ))}
          </select>
        </div>
        <table className="tbl">
          <thead><tr>
            <th>When</th><th>Actor</th><th>Action</th><th>Target</th><th>Detail</th>
          </tr></thead>
          <tbody>
            {rows.map(r => (
              <tr key={r.id}>
                <td className="tiny muted" style={{whiteSpace:'nowrap'}}>
                  <div>{UI.ago(r.ts)}</div>
                  <div className="mono">{UI.fmtDateTime(r.ts)}</div>
                </td>
                <td>
                  <div className="small fw-6">{r.actorName}</div>
                  <div className="tiny muted">{r.actorRole}</div>
                </td>
                <td><span className={cls('pill', tone(r.action))}>{r.action}</span></td>
                <td className="small">{r.target || '—'}</td>
                <td className="tiny muted">{r.detail || ''}</td>
              </tr>
            ))}
            {rows.length === 0 && (
              <tr><td colSpan={5} className="small muted" style={{padding:'18px',textAlign:'center'}}>
                {all.length === 0
                  ? 'No activity recorded yet.'
                  : activeFilters > 0
                    ? <>No entries match those filters. <button className="btn-link" onClick={reset}>Clear them</button>.</>
                    : 'No entries match that filter.'}
              </td></tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
}

/* ============================================================
   BatchesView — staff-only roster grouped by batch
   - Instructors and managers create / delete batches.
   - Each batch lists its students (or "no students yet").
   - Unassigned students appear in a separate "Unassigned" group.
   ============================================================ */
function BatchesView({ ctx }) {
  const batches = ctx.store && ctx.store.batches ? Object.values(ctx.store.batches) : [];
  // Live-filtered — see Store.listStudents().
  const students = window.Store.listStudents();
  const [name, setName] = useState('');
  const [course, setCourse] = useState('');
  const [busy, setBusy] = useState(false);
  const [dragOver, setDragOver] = useState(null);   // batch name being hovered (or '' for unassigned)

  // Index students by batch name.
  const byBatch = {};
  students.forEach(s => {
    const k = (s.profile && s.profile.batch) || '';
    (byBatch[k] = byBatch[k] || []).push(s);
  });

  const createBatch = async (e) => {
    e.preventDefault();
    const nm = name.trim();
    if (!nm) return;
    if (batches.some(b => b.name === nm)) {
      ctx.setToast(`Batch "${nm}" already exists`);
      return;
    }
    setBusy(true);
    try {
      await window.Store.addBatch({ name: nm, course });
      window.Store.logAudit('Batch created', nm, course || '');
      setName(''); setCourse('');
      ctx.setToast(`Batch "${nm}" created`);
    } catch (err) {
      ctx.setToast(err.message || 'Could not create batch');
    } finally {
      setBusy(false);
    }
  };

  const deleteBatch = async (b) => {
    const rosterSize = (byBatch[b.name] || []).length;
    const msg = rosterSize > 0
      ? `Delete batch "${b.name}"? ${rosterSize} student${rosterSize===1?'':'s'} will become Unassigned.`
      : `Delete batch "${b.name}"?`;
    if (!confirm(msg)) return;
    try {
      await window.Store.deleteBatch(b.id);
      window.Store.logAudit('Batch deleted', b.name, '');
      ctx.setToast(`Batch "${b.name}" deleted`);
    } catch (err) {
      ctx.setToast(err.message || 'Could not delete batch');
    }
  };

  // Reassign a student on drop. `targetBatch === ''` ⇒ Unassigned.
  const assignTo = (sid, targetBatch) => {
    const student = students.find(s => s.id === sid);
    const prev = (student && student.profile && student.profile.batch) || '';
    if (prev === targetBatch) return;
    window.Store.patchStudent(sid, (r) => { r.profile.batch = targetBatch; return r; });
    window.Store.logAudit('Batch reassigned',
      student?.profile?.fullName || sid,
      `${prev || 'Unassigned'} → ${targetBatch || 'Unassigned'}`);
    ctx.setToast(targetBatch
      ? `${student?.profile?.fullName || 'Student'} moved to ${targetBatch}`
      : `${student?.profile?.fullName || 'Student'} marked unassigned`);
  };

  // Wire up drop-zone handlers for a given batch name (use '' for Unassigned).
  const dropProps = (batchName) => ({
    onDragOver: (e) => { e.preventDefault(); setDragOver(batchName); },
    onDragLeave: () => setDragOver(d => d === batchName ? null : d),
    onDrop: (e) => {
      e.preventDefault();
      setDragOver(null);
      const sid = e.dataTransfer.getData('text/plain');
      if (sid) assignTo(sid, batchName);
    },
    style: dragOver === batchName
      ? { outline: '2px dashed var(--accent)', outlineOffset: -2, background: 'var(--accent-soft)' }
      : undefined,
  });

  const sorted = batches.slice().sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  const unassigned = byBatch[''] || [];

  // Reusable draggable student row. cols=true renders all 5 columns
  // (used inside batch cards); cols=false drops the Progress column
  // (used inside the Unassigned card).
  const StudentRow = ({ s, withProgress }) => {
    const p = window.calcProgress(s);
    return (
      <tr key={s.id}
          draggable
          onDragStart={(e) => {
            e.dataTransfer.setData('text/plain', s.id);
            e.dataTransfer.effectAllowed = 'move';
          }}
          style={{ cursor: 'grab' }}>
        <td>
          <span style={{display:'inline-block',width:14,color:'var(--muted)',marginRight:6,userSelect:'none'}}>⋮⋮</span>
          {s.profile.fullName || '—'}
        </td>
        <td className="mono small">{s.profile.rollNo || '—'}</td>
        <td className="small">{s.profile.course || '—'}</td>
        <td className="mono small">{s.profile.email || '—'}</td>
        {withProgress && (
          <td style={{minWidth:140}}>
            <div className="bar"><i style={{width:`${p?.overall||0}%`,
              background: (p?.overall||0)>=85 ? 'var(--good)' : (p?.overall||0)>=50 ? 'var(--accent)' : 'var(--warn)'}}/></div>
            <div className="tiny muted" style={{marginTop:2}}>{p?.overall||0}% overall</div>
          </td>
        )}
      </tr>
    );
  };

  return (
    <div className="module">
      <ModuleHeader
        crumbs="BATCHES"
        title="Batches"
        sub="Drag students between batches and the Unassigned bucket. New sign-ups land in Unassigned until you drop them into a batch."
        right={<div className="pill pill-ghost"><i className="dotty"/>{batches.length} batch{batches.length===1?'':'es'}</div>}
      />

      <div className="grid g-side">
        <div className="stack">
          {sorted.map(b => {
            const roster = byBatch[b.name] || [];
            return (
              <div className="card" key={b.id} {...dropProps(b.name)}>
                <div className="card-hd">
                  <div>
                    <h3>{b.name}</h3>
                    <div className="sub">
                      {b.course ? `${b.course} · ` : ''}{roster.length} student{roster.length===1?'':'s'}
                      {b.createdBy ? ` · created by ${b.createdBy}` : ''}
                    </div>
                  </div>
                  <button className="btn btn-sm btn-bad" onClick={()=>deleteBatch(b)}>Delete batch</button>
                </div>
                {roster.length === 0
                  ? <div className="card-body small muted" style={{padding:'14px 18px'}}>
                      Drop a student here to assign them to this batch.
                    </div>
                  : <table className="tbl">
                      <thead><tr><th>Name</th><th>Roll</th><th>Course</th><th>Email</th><th>Progress</th></tr></thead>
                      <tbody>
                        {roster.map(s => <StudentRow key={s.id} s={s} withProgress/>)}
                      </tbody>
                    </table>}
              </div>
            );
          })}

          {/* Always render Unassigned so it's a valid drop target — even
              with no students in it the instructor can drag someone OUT
              of a batch into here. */}
          <div className="card" {...dropProps('')}>
            <div className="card-hd">
              <div>
                <h3>Unassigned</h3>
                <div className="sub">
                  {unassigned.length} student{unassigned.length===1?'':'s'} not yet in a batch
                  {students.length === 0 ? ' · no students on record yet' : ''}
                </div>
              </div>
            </div>
            {unassigned.length === 0
              ? <div className="card-body small muted" style={{padding:'14px 18px'}}>
                  Drop a student here to remove them from a batch.
                </div>
              : <table className="tbl">
                  <thead><tr><th>Name</th><th>Roll</th><th>Course</th><th>Email</th></tr></thead>
                  <tbody>
                    {unassigned.map(s => <StudentRow key={s.id} s={s} withProgress={false}/>)}
                  </tbody>
                </table>}
          </div>

          {batches.length === 0 && students.length === 0 && (
            <div className="card"><div className="card-body small muted" style={{padding:'28px',textAlign:'center'}}>
              No batches yet, and no students on record. Once students sign up, create a batch and drop them in.
            </div></div>
          )}
        </div>

        <div className="stack">
          <form className="card" onSubmit={createBatch}>
            <div className="card-hd"><div><h3>New batch</h3><div className="sub">Give it a short name and (optionally) the course it runs.</div></div></div>
            <div className="card-body stack-sm" style={{gap:10}}>
              <div className="field">
                <label>Batch name<span className="req">*</span></label>
                <input className="input" type="text" value={name}
                  onChange={e=>setName(e.target.value)}
                  placeholder="e.g. SC-2026-05"/>
              </div>
              <div className="field">
                <label>Course (optional)</label>
                <select className="select" value={course} onChange={e=>setCourse(e.target.value)}>
                  <option value="">— select —</option>
                  {(window.COURSES || []).map(c => <option key={c} value={c}>{c}</option>)}
                </select>
              </div>
              <button className="btn btn-primary" type="submit" disabled={busy || !name.trim()}>
                {busy ? 'Creating…' : 'Create batch'}
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
  );
}

window.AdminOverview = AdminOverview;
window.AdminRoster = AdminRoster;
window.AdminIssuance = AdminIssuance;
window.AdminSettings = AdminSettings;
window.ManagerInstructors = ManagerInstructors;
window.ManagerUsers = ManagerUsers;
window.ManagerAudit = ManagerAudit;
window.BatchesView = BatchesView;
