/* ==================================================================
   Admin → Students
   ------------------------------------------------------------------
   CRUD over the `students` table. Mirrors the Teachers screen — the
   class+section filter on the left, a roster grid, and a right-hand
   detail panel for edit / move section / issue or reset portal login.
   ================================================================== */

const { Icon: StIcon } = window.KXUI;

const StudentsScreen = () => {
  const [students, setStudents] = React.useState([]);
  const [classSections, setClassSections] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState(null);
  const [filterCs, setFilterCs] = React.useState("");
  // Residential-type filter ("", "boarder", "day_scholar"). Applied client-
  // side over the already-fetched roster so it's instant and doesn't add a
  // round-trip per change.
  const [filterType, setFilterType] = React.useState("");
  const [search, setSearch] = React.useState("");
  const [selectedId, setSelectedId] = React.useState(null);
  const [credentialFlash, setCredentialFlash] = React.useState(null);

  const loadStudents = React.useCallback(async () => {
    setLoading(true); setErr(null);
    try {
      const qs = filterCs ? `?class_section_id=${encodeURIComponent(filterCs)}` : "";
      setStudents(await window.KXApi.get(`/admin/students${qs}`));
    } catch (e) { setErr(String(e.message || e)); }
    finally { setLoading(false); }
  }, [filterCs]);

  React.useEffect(() => { loadStudents(); }, [loadStudents]);
  React.useEffect(() => {
    window.KXApi.get("/admin/class-sections").then(setClassSections).catch(() => {});
  }, []);

  const filtered = students.filter(s => {
    if (filterType && s.student_type !== filterType) return false;
    if (!search) return true;
    const q = search.toLowerCase();
    return (s.name || "").toLowerCase().includes(q)
        || (s.roll_no || "").toLowerCase().includes(q)
        || (s.email || "").toLowerCase().includes(q)
        || (s.username || "").toLowerCase().includes(q)
        // Searchable by the finance-system id: the office reads it off a
        // receipt or a Ledger screen and needs to find the child here.
        || (s.ledger_student_uid || "").toLowerCase().includes(q);
  });

  const selected = selectedId ? students.find(s => s.id === selectedId) : null;

  // No height:100% / overflow:hidden on the split any more. The app shell is
  // min-height:100vh, so that height never resolved and the pane simply grew
  // with the page — and `overflow: hidden` on an ancestor silently disables
  // position:sticky on the detail pane inside it.
  return (
    <div className={`cx-roster-split${selected ? " has-detail" : ""}`}>
      <div style={{ padding: "24px 28px", minWidth: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 4 }}>
          <span style={{ color: "var(--accent)" }}><StIcon name="student" size={18}/></span>
          <div className="muted" style={{ fontSize: 11, letterSpacing: ".08em", textTransform: "uppercase" }}>Admin · Roster</div>
        </div>
        <h1 style={{ margin: "4px 0 6px", color: "var(--ink-0)", fontFamily: "'Instrument Serif', serif", fontWeight: 400, fontSize: 32, letterSpacing: "-0.01em" }}>
          Students
        </h1>
        <p className="muted" style={{ margin: 0, fontSize: 13.5, maxWidth: 640 }}>
          The roster mirrors the Ledger — students are admitted, corrected and discontinued there, and appear here on the next sync. Portal logins and everything in a student's record beyond these fields are managed here.
        </p>

        {credentialFlash && (
          <div style={{
            marginTop: 16, padding: "12px 16px", background: "rgba(75, 201, 123, 0.08)",
            border: "1px solid var(--green)", borderRadius: 8, color: "var(--ink-0)", fontSize: 13,
          }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12 }}>
              <div>
                <div style={{ fontWeight: 600, marginBottom: 4 }}>Portal credentials — copy now, won't be shown again</div>
                <div className="mono" style={{ fontSize: 13 }}>
                  Username: <b>{credentialFlash.username}</b>
                  &nbsp;&nbsp;·&nbsp;&nbsp;
                  Password: <b>{credentialFlash.temp_password}</b>
                </div>
              </div>
              <button className="btn ghost sm" onClick={() => setCredentialFlash(null)}><StIcon name="x" size={12}/></button>
            </div>
          </div>
        )}

        <div className="card" style={{ marginTop: 18 }}>
          <div className="card-head" style={{ gap: 10 }}>
            <span className="card-title">{students.length} {students.length === 1 ? "student" : "students"}</span>
            <select className="input" value={filterCs} onChange={e => setFilterCs(e.target.value)}
              style={{ marginLeft: 12, padding: "4px 8px", fontSize: 12 }}>
              <option value="">All classes</option>
              {classSections.map(cs => (
                <option key={cs.id} value={cs.id}>{cs.label} · {cs.academic_year}</option>
              ))}
            </select>
            <select className="input" value={filterType} onChange={e => setFilterType(e.target.value)}
              style={{ padding: "4px 8px", fontSize: 12 }}
              title="Filter by residential type">
              <option value="">All types</option>
              <option value="boarder">Boarders</option>
              <option value="day_scholar">Day Scholars</option>
            </select>
            <div style={{ position: "relative", flex: 1 }}>
              <span style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "var(--ink-3)" }}>
                <StIcon name="search" size={12}/>
              </span>
              <input className="input" placeholder="Search name, roll, email…" value={search} onChange={e => setSearch(e.target.value)}
                style={{ paddingLeft: 28, padding: "5px 10px 5px 28px", fontSize: 12.5, width: "100%", background: "var(--bg-2)" }}/>
            </div>
            {/* No "Add student" here by design — admissions happen in the
                Ledger so the child gets a fee record and a permanent id. */}
            <a className="btn sm ghost" href="https://ledger.adarshabani.in/students/new" target="_blank" rel="noreferrer"
               title="Students are admitted in the Ledger; they appear here after the next roster sync">
              Admit in Ledger ↗
            </a>
          </div>

          <div className="card-body" style={{ padding: 0 }}>
            {loading && <div style={{ padding: 28, color: "var(--ink-3)", fontSize: 12 }}>Loading…</div>}
            {err && <div style={{ padding: 18, color: "var(--red)", fontSize: 12 }}>{err}</div>}
            {!loading && filtered.length === 0 && (
              <div style={{ padding: 36, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
                {students.length === 0 ? "No students yet. Add one." : "No matches."}
              </div>
            )}
            {filtered.map(s => (
              <div key={s.id}
                onClick={() => setSelectedId(s.id)}
                className="cx-roster-row"
                style={{
                  padding: "10px 14px", borderBottom: "1px solid var(--line-soft)",
                  gap: 12, alignItems: "center", cursor: "pointer",
                  background: selectedId === s.id ? "var(--bg-2)" : "transparent",
                }}>
                <span className="mono muted" style={{ fontSize: 11 }}>{s.roll_no}</span>
                <div>
                  <div style={{ color: "var(--ink-0)", fontSize: 13, fontWeight: 500 }}>
                    {s.name}
                    {s.username && !s.password_used_at && <span style={{ marginLeft: 8, fontSize: 10, color: "var(--ink-3)", padding: "1px 6px", border: "1px solid var(--line)", borderRadius: 4 }}>NEVER USED</span>}
                    {/* Only the MISSING case is loud. A linked student is the norm and
                        needs no decoration; an unlinked one means their parent's fee
                        screen cannot resolve, which is invisible everywhere else. */}
                    {!s.ledger_student_uid && <span title="No link to the finance system — this student's fees cannot be shown to their parent" style={{ marginLeft: 8, fontSize: 10, color: "var(--amber, #d97706)", padding: "1px 6px", border: "1px solid var(--amber, #d97706)", borderRadius: 4 }}>NO FEE LINK</span>}
                  </div>
                  <div className="muted" style={{ fontSize: 11, marginTop: 2 }}>{s.email || "—"}</div>
                </div>
                <div style={{ fontSize: 11, color: "var(--ink-2)" }}>{s.class_label}</div>
                <StudentTypePill type={s.student_type}/>
                <div className="mono muted" style={{ fontSize: 11 }}>{s.username || <span style={{ color: "var(--ink-3)" }}>no login</span>}</div>
                <div className="muted" style={{ fontSize: 11, textAlign: "right" }}>
                  {s.attempt_count + s.sheet_count > 0 ? `${s.attempt_count + s.sheet_count} acts` : "—"}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {selected && (
        <StudentDetail
          key={selected.id}
          studentSummary={selected}
          classSections={classSections}
          onClose={() => setSelectedId(null)}
          onChanged={async (flashCreds) => { if (flashCreds) setCredentialFlash(flashCreds); await loadStudents(); }}
        />
      )}
    </div>
  );
};

/* ---------- detail panel ---------- */

// A Ledger-owned value, rendered as text. Deliberately not a disabled input:
// a greyed-out box still reads as "editable, just not right now".
const ReadOnly = ({ value, mono }) => (
  <div style={{
    padding: "4px 0", fontSize: 12, color: value ? "var(--ink-0)" : "var(--ink-3)",
    fontFamily: mono ? "var(--mono, ui-monospace, monospace)" : "inherit",
  }}>{value || "—"}</div>
);

const REQUESTABLE = [
  { key: "name", label: "Name" },
  { key: "roll_no", label: "Roll no" },
  { key: "email", label: "Email" },
  { key: "class_section_id", label: "Class / section" },
  { key: "student_type", label: "Boarder / day scholar" },
  { key: "avails_transport", label: "Avails transport" },
];

// Asks the office to change something in the Ledger. Resolving the request does
// not write here — the office edits the Ledger and the roster sync brings it
// back, so these fields only ever change by one route.
const EditRequest = ({ student, onDone }) => {
  const [field, setField] = React.useState("name");
  const [value, setValue] = React.useState("");
  const [reason, setReason] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const [sent, setSent] = React.useState(false);

  const submit = async () => {
    setBusy(true); setErr(null);
    try {
      await window.KXApi.post(`/admin/students/${student.id}/edit-request`, {
        field, requested_value: value.trim(), reason: reason.trim() || null,
      });
      setSent(true);
      setTimeout(onDone, 1600);
    } catch (e) { setErr(String(e.message || e)); }
    finally { setBusy(false); }
  };

  if (sent) return (
    <div style={{ marginTop: 10, padding: "10px 12px", background: "var(--bg-2)", borderRadius: 6, fontSize: 12 }}>
      Sent to the office. It will appear here once they update the Ledger.
    </div>
  );

  return (
    <div style={{ marginTop: 10, padding: "12px", background: "var(--bg-2)", borderRadius: 6, display: "grid", gap: 8 }}>
      <FormRow label="Field">
        <select className="input" value={field} onChange={e => setField(e.target.value)} style={{ width: "100%", padding: "4px 8px" }}>
          {REQUESTABLE.map(f => <option key={f.key} value={f.key}>{f.label}</option>)}
        </select>
      </FormRow>
      <FormRow label="Should be">
        <input className="input" value={value} onChange={e => setValue(e.target.value)}
               placeholder={field === "class_section_id" ? "e.g. VI-B" : "correct value"} style={{ width: "100%" }}/>
      </FormRow>
      <FormRow label="Why">
        <input className="input" value={reason} onChange={e => setReason(e.target.value)}
               placeholder="optional — helps the office check" style={{ width: "100%" }}/>
      </FormRow>
      {err && <div style={{ color: "var(--red, #d05)", fontSize: 11 }}>{err}</div>}
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 6 }}>
        <button className="btn ghost sm" onClick={onDone}>Cancel</button>
        <button className="btn sm primary" disabled={busy || !value.trim()} onClick={submit}>
          {busy ? "Sending…" : "Send to office"}
        </button>
      </div>
    </div>
  );
};

const StudentDetail = ({ studentSummary, classSections, onClose, onChanged }) => {
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const [showRequest, setShowRequest] = React.useState(false);

  React.useEffect(() => { setShowRequest(false); }, [studentSummary.id]);

  const issueLogin = async () => {
    setBusy(true); setErr(null);
    try {
      const out = await window.KXApi.post(`/admin/students/${studentSummary.id}/credentials`, {});
      await onChanged({ username: out.username, temp_password: out.temp_password });
    } catch (e) { setErr(String(e.message || e)); }
    finally { setBusy(false); }
  };

  const removeLogin = async () => {
    if (!window.confirm("Disable portal login for this student?")) return;
    setBusy(true); setErr(null);
    try {
      await window.KXApi.del(`/admin/students/${studentSummary.id}/credentials`);
      await onChanged();
    } catch (e) { setErr(String(e.message || e)); }
    finally { setBusy(false); }
  };

  // While the pane is a bottom sheet the page behind it must not scroll —
  // otherwise a drag on the sheet's own content scrolls the 881-row roster
  // underneath and the sheet appears to drift. Guarded to the phone layout,
  // since on desktop the pane is just a column and the page should scroll.
  useEffect(() => {
    const sheet = window.matchMedia("(max-width: 1000px)");
    if (!sheet.matches) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => {
      document.body.style.overflow = prev;
      window.removeEventListener("keydown", onKey);
    };
  }, [onClose]);

  return (
    <>
    {/* Backdrop — only painted on the phone layout, where the pane is a
        bottom sheet rather than a column. */}
    <div className="cx-roster-scrim" onClick={onClose} aria-hidden="true"/>
    <aside className="cx-roster-detail">
      {/* Grab handle: the affordance that says "this sheet closes downward".
          Hidden on desktop, where the pane is a column with a × button. */}
      <div className="cx-sheet-grip" aria-hidden="true"/>
      <div className="cx-roster-detail-head" style={{ padding: "16px 20px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{ flex: 1 }}>
          <div style={{ color: "var(--ink-0)", fontSize: 14, fontWeight: 500 }}>{studentSummary.name}</div>
          <div className="muted" style={{ fontSize: 11, marginTop: 2 }}>{studentSummary.class_label} · {studentSummary.roll_no}</div>
          {/* The finance-system identity. Read-only here on purpose: the Ledger
              owns it, and a value typed in this panel would be a guess that
              silently points a family's fees at the wrong child. */}
          <div className="mono muted" style={{ fontSize: 10, marginTop: 3 }}
               title={studentSummary.ledger_student_uid
                 ? "Permanent student ID in the finance system — set at admission, unchanged by promotions"
                 : "Not linked to the finance system. Fees cannot be shown to this student's parent."}>
            {studentSummary.ledger_student_uid
              ? <>Ledger: {studentSummary.ledger_student_uid}</>
              : <span style={{ color: "var(--amber, #d97706)" }}>Ledger: not linked</span>}
          </div>
        </div>
        <button className="btn ghost sm" onClick={onClose}><StIcon name="x" size={13}/></button>
      </div>

      {err && (
        <div style={{ margin: "10px 20px", padding: 10, background: "rgba(255,107,107,0.08)", border: "1px solid var(--red)", borderRadius: 6, color: "var(--red)", fontSize: 12 }}>{err}</div>
      )}

      {/* These fields belong to the Ledger. Shown as values, not inputs — an
          editable box that silently reverts overnight is worse than no box,
          because the clerk believes the correction stuck. */}
      <section style={{ padding: "14px 20px", borderBottom: "1px solid var(--line)" }}>
        <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 10, marginBottom: 8 }}>
          <div className="muted" style={{ fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase" }}>Profile</div>
          <span className="muted" style={{ fontSize: 10 }}>from the Ledger · read-only</span>
        </div>
        <FormRow label="Name"><ReadOnly value={studentSummary.name}/></FormRow>
        <FormRow label="Roll no"><ReadOnly value={studentSummary.roll_no} mono/></FormRow>
        <FormRow label="Email"><ReadOnly value={studentSummary.email}/></FormRow>
        <FormRow label="Class"><ReadOnly value={studentSummary.class_label}/></FormRow>
        <FormRow label="Type"><ReadOnly value={studentSummary.student_type === "day_scholar" ? "Day Scholar" : "Boarder"}/></FormRow>
        <FormRow label="Transport"><ReadOnly value={studentSummary.avails_transport ? "Avails transport" : "—"}/></FormRow>
        <p className="muted" style={{ fontSize: 11, marginTop: 10, lineHeight: 1.5 }}>
          Students are admitted and corrected in the Ledger; this roster reflects it.
          Spotted something wrong? <button className="btn ghost sm" style={{ padding: "1px 7px", fontSize: 11 }}
            onClick={() => setShowRequest(v => !v)}>Request a correction</button>
        </p>
        {showRequest && <EditRequest student={studentSummary} onDone={() => setShowRequest(false)}/>}
      </section>

      <section style={{ padding: "14px 20px", borderBottom: "1px solid var(--line)" }}>
        <div className="muted" style={{ fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase", marginBottom: 8 }}>Portal login</div>
        <FormRow label="Username">
          <span className="mono" style={{ fontSize: 12, color: "var(--ink-1)" }}>{studentSummary.username || <span className="muted">no login</span>}</span>
        </FormRow>
        {studentSummary.username && (
          <FormRow label="Last login">
            {studentSummary.password_used_at
              ? <span style={{ fontSize: 12, color: "var(--ink-1)" }}>{new Date(studentSummary.password_used_at).toLocaleString()}</span>
              : <span style={{ fontSize: 12, color: "var(--ink-3)" }}>never used</span>
            }
          </FormRow>
        )}
        <div style={{ display: "flex", gap: 6, marginTop: 10, flexWrap: "wrap" }}>
          <button className="btn sm" disabled={busy} onClick={issueLogin}>
            {studentSummary.username ? "Issue new password" : "Create login"}
          </button>
          {studentSummary.username && (
            <button className="btn sm ghost" disabled={busy} onClick={removeLogin}>Disable login</button>
          )}
        </div>
      </section>

      <section style={{ padding: "14px 20px" }}>
        <div className="muted" style={{ fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase", marginBottom: 8 }}>Activity</div>
        <FormRow label="Sheets"><span style={{ fontSize: 12 }}>{studentSummary.sheet_count}</span></FormRow>
        <FormRow label="Attempts"><span style={{ fontSize: 12 }}>{studentSummary.attempt_count}</span></FormRow>
        {/* No delete. A departure is recorded in the Ledger, where the fees,
            deposit and refund are settled; the record here is kept because it
            holds this child's attempts and results. */}
        <p className="muted" style={{ fontSize: 11, marginTop: 12, lineHeight: 1.5 }}>
          Left the school? Mark them discontinued in the Ledger — their results stay here.
        </p>
      </section>
    </aside>
    </>
  );
};

const FormRow = ({ label, children }) => (
  <div style={{ display: "grid", gridTemplateColumns: "90px 1fr", gap: 10, alignItems: "center", padding: "4px 0", fontSize: 12 }}>
    <span className="muted" style={{ fontSize: 11 }}>{label}</span>
    <div>{children}</div>
  </div>
);

// Residential-type badge for the roster row. Subtle background tint keeps the
// list scannable without drawing attention away from name/class. Defaults to
// boarder if the field is unset — the migration backfilled every existing row
// to boarder so the null-coalesce here is belt-and-braces.
const StudentTypePill = ({ type }) => {
  const isDay = type === "day_scholar";
  const label = isDay ? "DAY" : "BOARDER";
  const tint = isDay
    ? { color: "var(--accent)", border: "1px solid rgba(80,160,255,0.4)", background: "rgba(80,160,255,0.06)" }
    : { color: "var(--ink-2)", border: "1px solid var(--line)", background: "var(--bg-2)" };
  return (
    <span className="mono" style={{
      fontSize: 10, letterSpacing: ".06em", textAlign: "center",
      padding: "2px 6px", borderRadius: 4, ...tint,
    }}>{label}</span>
  );
};

/* ---------- add modal ---------- */

