/* ==================================================================
   Admin → KinetiX Access
   ------------------------------------------------------------------
   Soft-launch gate for learn-kinetiX. Three stacked cards:

     1. Pending requests — student-initiated, [Approve] / [Deny]
     2. Granted students — currently entitled, [Revoke]
     3. Bulk grant      — class-section dropdown + "Grant section"

   All endpoints live under /api/admin/kinetix/* and require admin.
   ================================================================== */

const { Icon: KAIcon } = window.KXUI;

const KinetixAccessScreen = () => {
  const [data, setData]                 = React.useState({ granted: [], pending: [], decided: [] });
  const [classSections, setClassSections] = React.useState([]);
  const [loading, setLoading]           = React.useState(true);
  const [err, setErr]                   = React.useState(null);
  const [bulkCs, setBulkCs]             = React.useState("");
  const [bulkBusy, setBulkBusy]         = React.useState(false);
  const [bulkFlash, setBulkFlash]       = React.useState(null);
  const [decideBusy, setDecideBusy]     = React.useState({});  // {requestId: bool}
  const [revokeBusy, setRevokeBusy]     = React.useState({});  // {studentId: bool}
  const [pwBusy, setPwBusy]             = React.useState({});  // {studentId: bool}
  // After a /generate-password call, hold the plaintext + the student's
  // identity in this slot so the modal can render it. Plaintext is shown
  // exactly once — there is no way to recover it after the admin dismisses
  // the modal. They have to re-issue a fresh one.
  const [issuedPassword, setIssuedPassword] = React.useState(null);
  // Security-leaning: when WhatsApp delivery succeeded the plaintext is
  // hidden behind a confirm so we don't tempt the admin to share it through
  // a second channel. When delivery skipped or failed, the admin needs the
  // plaintext to share manually, so reveal it upfront.
  const [pwRevealed, setPwRevealed] = React.useState(false);
  const dismissIssuedModal = () => { setIssuedPassword(null); setPwRevealed(false); };
                                                   // null | { student_id, name, roll_no, password, generated_at }

  const load = React.useCallback(async () => {
    setLoading(true); setErr(null);
    try {
      setData(await window.KXApi.get("/admin/kinetix/access"));
    } catch (e) {
      setErr(String(e.message || e));
    } finally {
      setLoading(false);
    }
  }, []);

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

  const decide = async (requestId, decision) => {
    if (decision === "denied" && !window.confirm("Deny this request? The student will be notified.")) return;
    setDecideBusy(b => ({ ...b, [requestId]: true }));
    try {
      await window.KXApi.post(`/admin/kinetix/requests/${requestId}/decide`, { decision });
      await load();
    } catch (e) {
      window.alert("Could not decide: " + (e.message || e));
    } finally {
      setDecideBusy(b => { const n = { ...b }; delete n[requestId]; return n; });
    }
  };

  const revoke = async (studentId, name) => {
    if (!window.confirm(`Revoke KinetiX access for ${name}? They'll lose access on next launch.`)) return;
    setRevokeBusy(b => ({ ...b, [studentId]: true }));
    try {
      await window.KXApi.del(`/admin/kinetix/access/${studentId}`);
      await load();
    } catch (e) {
      window.alert("Could not revoke: " + (e.message || e));
    } finally {
      setRevokeBusy(b => { const n = { ...b }; delete n[studentId]; return n; });
    }
  };

  // Issue a fresh one-time password the student can use to log into
  // learn.adarshabani.in directly. Re-issuing while one is still active
  // INVALIDATES the previous unused password — the backend overwrites the
  // hash. Plaintext is shown exactly once in the modal.
  const generatePassword = async (studentId, name, roll_no) => {
    if (!window.confirm(
      `Issue a fresh KinetiX login password for ${name}? ` +
      `Any previously-issued password (used or not) will be invalidated. ` +
      `You'll see the new password ONCE — save it before dismissing.`,
    )) return;
    setPwBusy(b => ({ ...b, [studentId]: true }));
    try {
      const r = await window.KXApi.post(
        `/admin/kinetix/access/${studentId}/generate-password`, {},
      );
      setIssuedPassword({
        student_id: studentId,
        name,
        roll_no,
        password: r.password,
        generated_at: r.generated_at,
        delivery: r.delivery || null,
      });
      // Hide plaintext upfront only when WhatsApp delivery actually succeeded.
      // Anything else (skipped, failed) needs the plaintext visible so the
      // admin can fall back to the old share-by-hand workflow.
      setPwRevealed(!(r.delivery && r.delivery.status === "sent"));
    } catch (e) {
      window.alert("Could not generate password: " + (e.message || e));
    } finally {
      setPwBusy(b => { const n = { ...b }; delete n[studentId]; return n; });
    }
  };

  // Bulk grant fetches the section's roster, then issues a grant per student.
  // Per-row failures are tolerated — final flash shows the count.
  const bulkGrant = async () => {
    if (!bulkCs) { window.alert("Pick a class-section first."); return; }
    const cs = classSections.find(c => c.id === bulkCs);
    if (!cs) return;
    if (!window.confirm(`Grant KinetiX access to every student in ${cs.label}? Already-granted students are left untouched.`)) return;
    setBulkBusy(true); setBulkFlash(null);
    try {
      const roster = await window.KXApi.get(`/admin/students?class_section_id=${encodeURIComponent(bulkCs)}`);
      let ok = 0, skipped = 0, failed = 0;
      for (const s of roster) {
        try {
          const r = await window.KXApi.post(`/admin/kinetix/access/${s.id}/grant`, { note: `Bulk grant: ${cs.label}` });
          if (r?.alreadyGranted) skipped += 1; else ok += 1;
        } catch { failed += 1; }
      }
      setBulkFlash({ ok, skipped, failed, total: roster.length, label: cs.label });
      await load();
    } catch (e) {
      window.alert("Bulk grant failed: " + (e.message || e));
    } finally {
      setBulkBusy(false);
    }
  };

  const sectionLabel = (cs) => `${cs.label}${cs.academic_year ? ` · ${cs.academic_year}` : ""}`;

  return (
    <div style={{ overflow: "auto", padding: "24px 28px", height: "100%" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 4 }}>
        <span style={{ color: "var(--accent)" }}><KAIcon name="sparkle" size={18}/></span>
        <div className="muted" style={{ fontSize: 11, letterSpacing: ".08em", textTransform: "uppercase" }}>Admin · Access</div>
      </div>
      <h1 style={{ margin: "4px 0 6px", color: "var(--ink-0)", fontFamily: "'Instrument Serif', serif", fontWeight: 400, fontSize: 32, letterSpacing: "-0.01em" }}>
        KinetiX Access
      </h1>
      <p className="muted" style={{ margin: 0, fontSize: 13.5, maxWidth: 720 }}>
        Soft-launch gate for the AI tutor. Approve student requests, bulk-grant a class,
        or revoke ad-hoc. Revoking only affects the next launch — an in-flight session stays valid.
      </p>

      {err && (
        <div style={{ marginTop: 16, padding: "10px 14px", background: "rgba(225, 80, 80, 0.08)",
                     border: "1px solid var(--red)", borderRadius: 8, color: "var(--ink-0)", fontSize: 13 }}>
          {err}
        </div>
      )}

      {/* ---------- Pending requests ---------- */}
      <div className="card" style={{ marginTop: 18 }}>
        <div className="card-head" style={{ gap: 10 }}>
          <span className="card-title">Pending requests</span>
          <span className="muted" style={{ fontSize: 11 }}>{data.pending.length} waiting</span>
          <button className="btn ghost sm" onClick={load} disabled={loading} style={{ marginLeft: "auto" }}>
            Refresh
          </button>
        </div>
        <div className="card-body" style={{ padding: 0 }}>
          {loading && <div style={{ padding: 20, color: "var(--ink-3)", fontSize: 12 }}>Loading…</div>}
          {!loading && data.pending.length === 0 && (
            <div style={{ padding: 28, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
              No pending requests.
            </div>
          )}
          {data.pending.map(r => (
            <div key={r.id} className="cx-datarow c4"
              style={{
                padding: "12px 16px", borderBottom: "1px solid var(--line-soft)",
                gap: 12, alignItems: "center",
              }}>
              <span className="mono muted" style={{ fontSize: 11 }}>{r.roll_no}</span>
              <div>
                <div style={{ color: "var(--ink-0)", fontSize: 13, fontWeight: 500 }}>{r.name}</div>
                <div className="muted" style={{ fontSize: 11, marginTop: 2, fontStyle: r.reason ? "italic" : "normal" }}>
                  {r.reason ? `"${r.reason}"` : <span style={{ opacity: 0.6 }}>(no reason given)</span>}
                </div>
              </div>
              <div style={{ fontSize: 11, color: "var(--ink-2)" }}>{r.class_label || "—"}</div>
              <div style={{ display: "flex", gap: 6 }}>
                <button className="btn sm primary" disabled={!!decideBusy[r.id]} onClick={() => decide(r.id, "approved")}>
                  <KAIcon name="check" size={11}/> Approve
                </button>
                <button className="btn sm" disabled={!!decideBusy[r.id]} onClick={() => decide(r.id, "denied")}>
                  <KAIcon name="x" size={11}/> Deny
                </button>
              </div>
            </div>
          ))}
        </div>
      </div>

      {/* ---------- Granted students ---------- */}
      <div className="card" style={{ marginTop: 18 }}>
        <div className="card-head" style={{ gap: 10 }}>
          <span className="card-title">Granted</span>
          <span className="muted" style={{ fontSize: 11 }}>
            {data.granted.length} student{data.granted.length === 1 ? "" : "s"}
          </span>
        </div>
        <div className="card-body" style={{ padding: 0 }}>
          {!loading && data.granted.length === 0 && (
            <div style={{ padding: 28, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
              Nobody has access yet. Grant a section below or approve a pending request.
            </div>
          )}
          {data.granted.map(g => (
            <div key={g.student_id} className="cx-datarow c5"
              style={{
                padding: "10px 16px", borderBottom: "1px solid var(--line-soft)",
                gap: 12, alignItems: "center",
              }}>
              <span className="mono muted" style={{ fontSize: 11 }}>{g.roll_no}</span>
              <div style={{ color: "var(--ink-0)", fontSize: 13, fontWeight: 500 }}>{g.name}</div>
              <div style={{ fontSize: 11, color: "var(--ink-2)" }}>{g.class_label || "—"}</div>
              <div className="muted" style={{ fontSize: 11 }}>
                {g.granted_by_name ? `by ${g.granted_by_name}` : "by system"}
                {g.granted_at && <> · {new Date(g.granted_at).toLocaleDateString()}</>}
                {g.notes && <> · <span style={{ fontStyle: "italic" }}>"{g.notes}"</span></>}
              </div>
              <div style={{ display: "flex", gap: 6 }}>
                <button className="btn sm ghost"
                  disabled={!!pwBusy[g.student_id]}
                  onClick={() => generatePassword(g.student_id, g.name, g.roll_no)}
                  title="Issue a one-time password for direct login at learn.adarshabani.in"
                >
                  <KAIcon name="key" size={11}/> {pwBusy[g.student_id] ? "Issuing…" : "Issue password"}
                </button>
                <button className="btn sm ghost"
                  disabled={!!revokeBusy[g.student_id]}
                  onClick={() => revoke(g.student_id, g.name)}
                >
                  <KAIcon name="trash" size={11}/> Revoke
                </button>
              </div>
            </div>
          ))}
        </div>
      </div>

      {/* ---------- Bulk grant by section ---------- */}
      <div className="card" style={{ marginTop: 18 }}>
        <div className="card-head"><span className="card-title">Bulk grant by class</span></div>
        <div className="card-body" style={{ padding: 16 }}>
          <p className="muted" style={{ margin: "0 0 10px", fontSize: 12.5 }}>
            Grant access to every student in a section. Already-granted students are skipped — no duplicates.
          </p>
          <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
            {/* min-width had to become a max: a 220px floor plus the button
                overflowed the card on a phone. */}
            <select className="input" value={bulkCs} onChange={e => setBulkCs(e.target.value)}
              style={{ padding: "5px 8px", fontSize: 12.5, width: "min(220px, 100%)", flex: "1 1 180px" }}>
              <option value="">Pick a class…</option>
              {classSections.map(cs => (
                <option key={cs.id} value={cs.id}>{sectionLabel(cs)}</option>
              ))}
            </select>
            <button className="btn sm primary" disabled={bulkBusy || !bulkCs} onClick={bulkGrant}>
              <KAIcon name="sparkle" size={11}/> {bulkBusy ? "Granting…" : "Grant section"}
            </button>
          </div>
          {bulkFlash && (
            <div style={{ marginTop: 12, padding: "8px 12px", borderRadius: 6,
                          background: "rgba(75, 201, 123, 0.08)", border: "1px solid var(--green)",
                          color: "var(--ink-0)", fontSize: 12.5 }}>
              {bulkFlash.label}: {bulkFlash.ok} newly granted, {bulkFlash.skipped} already had access
              {bulkFlash.failed > 0 && <>, {bulkFlash.failed} failed</>}
              {" "}(out of {bulkFlash.total}).
            </div>
          )}
        </div>
      </div>

      {/* ---------- Recent decisions (history) ---------- */}
      {data.decided.length > 0 && (
        <div className="card" style={{ marginTop: 18 }}>
          <div className="card-head"><span className="card-title">Recent decisions</span></div>
          <div className="card-body" style={{ padding: 0 }}>
            {data.decided.slice(0, 20).map(d => (
              <div key={d.id} className="cx-datarow c5b"
                style={{
                  padding: "8px 16px", borderBottom: "1px solid var(--line-soft)",
                  gap: 12, alignItems: "center",
                  fontSize: 12,
                }}>
                <span className="mono muted" style={{ fontSize: 11 }}>{d.roll_no}</span>
                <div style={{ color: "var(--ink-1)" }}>{d.name}</div>
                <div style={{
                  fontSize: 10, fontFamily: "monospace", textTransform: "uppercase", letterSpacing: ".06em",
                  color: d.status === "approved" ? "var(--green)" : "var(--red)",
                  border: `1px solid ${d.status === "approved" ? "var(--green)" : "var(--red)"}`,
                  borderRadius: 4, padding: "1px 6px", textAlign: "center",
                }}>{d.status}</div>
                <div className="muted" style={{ fontSize: 11, fontStyle: d.decision_note ? "italic" : "normal" }}>
                  {d.decision_note ? `"${d.decision_note}"` : d.reason ? `req: "${d.reason}"` : "—"}
                </div>
                <div className="muted" style={{ fontSize: 11, textAlign: "right" }}>
                  {d.decided_at ? new Date(d.decided_at).toLocaleDateString() : ""}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* One-time-password issuance modal. The plaintext sits in component
          state from the /generate-password response; once the admin dismisses
          the modal the value is gone, irrecoverable. They must save it
          first — copy button + clear hint. */}
      {issuedPassword && (
        <div
          onClick={dismissIssuedModal}
          style={{
            position: "fixed", inset: 0, background: "rgba(0,0,0,0.72)",
            display: "grid", placeItems: "center", padding: 20, zIndex: 9999,
          }}>
          <div onClick={(e) => e.stopPropagation()}
            style={{
              background: "var(--panel)", border: "1px solid var(--line)",
              borderRadius: 14, maxWidth: 480, width: "100%", padding: 24,
              boxShadow: "0 20px 60px rgba(0,0,0,0.5)",
            }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
              <div style={{
                width: 32, height: 32, borderRadius: 8, background: "var(--green-soft)",
                display: "grid", placeItems: "center",
              }}>
                <KAIcon name="key" size={16}/>
              </div>
              <div>
                <div style={{ fontWeight: 600, color: "var(--ink-0)", fontSize: 15 }}>
                  Password issued
                </div>
                <div className="muted" style={{ fontSize: 11.5 }}>
                  For {issuedPassword.name} ({issuedPassword.roll_no})
                </div>
              </div>
            </div>

            {/* Delivery banner — green pill on success, amber on skipped/failed.
                The exact copy depends on which branch the backend took so the
                admin always knows whether they still need to share manually. */}
            {(() => {
              const d = issuedPassword.delivery;
              if (!d || d.status === "skipped_no_config") {
                return (
                  <div style={{
                    marginTop: 8, padding: "8px 12px", borderRadius: 8, fontSize: 12,
                    background: "rgba(225, 175, 80, 0.10)", border: "1px solid rgba(225,175,80,0.45)",
                    color: "var(--ink-1)",
                  }}>
                    WhatsApp delivery isn't configured on this server. Share the password manually below.
                  </div>
                );
              }
              if (d.status === "skipped_no_phone") {
                return (
                  <div style={{
                    marginTop: 8, padding: "8px 12px", borderRadius: 8, fontSize: 12,
                    background: "rgba(225, 175, 80, 0.10)", border: "1px solid rgba(225,175,80,0.45)",
                    color: "var(--ink-1)",
                  }}>
                    No usable phone on file for this student — share the password manually below.
                  </div>
                );
              }
              if (d.status === "failed") {
                return (
                  <div style={{
                    marginTop: 8, padding: "8px 12px", borderRadius: 8, fontSize: 12,
                    background: "rgba(225, 80, 80, 0.10)", border: "1px solid rgba(225,80,80,0.45)",
                    color: "var(--ink-1)",
                  }}>
                    WhatsApp send to {d.phone_mask || "the student"} failed: <span className="mono">{d.error || "unknown error"}</span>. Share manually below.
                  </div>
                );
              }
              // sent
              return (
                <div style={{
                  marginTop: 8, padding: "8px 12px", borderRadius: 8, fontSize: 12,
                  background: "rgba(80, 200, 120, 0.10)", border: "1px solid rgba(80,200,120,0.45)",
                  color: "var(--ink-1)",
                }}>
                  <KAIcon name="check" size={11}/> Sent via WhatsApp to <span className="mono">{d.phone_mask}</span>. The student has it.
                </div>
              );
            })()}

            {/* Plaintext block. Visible by default whenever delivery didn't
                succeed; on success it sits behind a confirm so the admin
                doesn't casually copy a password the student already received. */}
            {pwRevealed ? (
              <div style={{
                marginTop: 12, marginBottom: 10,
                border: "1px dashed var(--line-soft)", borderRadius: 8,
                padding: "12px 14px", background: "var(--bg)",
                display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8,
              }}>
                <span style={{
                  fontFamily: "monospace", fontSize: 18, color: "var(--ink-0)",
                  letterSpacing: "0.06em", userSelect: "all",
                }}>{issuedPassword.password}</span>
                <button className="btn sm"
                  onClick={async () => {
                    try {
                      await navigator.clipboard.writeText(issuedPassword.password);
                      window.alert("Password copied to clipboard.");
                    } catch {
                      window.alert("Couldn't copy automatically. Select the text and copy manually.");
                    }
                  }}>
                  <KAIcon name="copy" size={11}/> Copy
                </button>
              </div>
            ) : (
              <div style={{ marginTop: 12, marginBottom: 4 }}>
                <button className="btn ghost sm"
                  onClick={() => {
                    if (window.confirm(
                      "Reveal the plaintext password? The student already received it via WhatsApp — only reveal if you have a specific reason to share it through another channel.",
                    )) setPwRevealed(true);
                  }}>
                  <KAIcon name="eye" size={11}/> Show password anyway
                </button>
              </div>
            )}

            <p className="muted" style={{ fontSize: 12.5, lineHeight: 1.55, margin: "10px 0 0" }}>
              {pwRevealed ? (
                <>
                  <strong style={{ color: "var(--ink-1)" }}>Save before closing</strong> — it is shown only once.
                </>
              ) : (
                <strong style={{ color: "var(--ink-1)" }}>You can dismiss this safely.</strong>
              )}
              {" "}The student logs in at <span style={{ fontFamily: "monospace" }}>learn.adarshabani.in/login</span> using
              their roll <span style={{ fontFamily: "monospace" }}>{issuedPassword.roll_no}</span> + this password.
              It stays valid until you issue a new one — the next "Issue password" overwrites it.
            </p>

            <div style={{ marginTop: 18, display: "flex", justifyContent: "flex-end" }}>
              <button className="btn" onClick={dismissIssuedModal}>
                {pwRevealed ? "I have saved it" : "Done"}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

window.KinetixAccessScreen = KinetixAccessScreen;
