/* ==================================================================
   Admissions → Intent to Apply
   ------------------------------------------------------------------
   The register behind the "Apply now" button on adarshabani.in.

   A family fills the public form to say they INTEND to apply once the
   admission process opens — it is not the admission application. This
   screen is where the office works each of those families through the
   funnel the public page promises them:

     intent → office informs → payment link → payment → admission test
     → face-to-face with the Secretary of Adarshabani Mission → admitted

   Reading is open to any signed-in staff member (the same register is
   in the teacher app). Advancing a stage or writing an office note is
   admin / HoD / coordinator only, enforced server-side; `can_edit` on
   the list response is what hides the controls for everyone else.

   Endpoints: /api/admission-intents*
   ================================================================== */

const { Icon: AdmIcon } = window.KXUI;

// Mirrors ADMISSION_STAGES in backend/src/routes/admission_intents.ts. The
// server is still the authority — /admission-intents/stages is fetched on
// mount and replaces this — but having it inline means the first paint has
// labels instead of raw keys.
const ADM_FALLBACK_STAGES = [
  { key: "intent_received",        label: "Intent received" },
  { key: "office_contacted",       label: "Office informed family" },
  { key: "payment_link_sent",      label: "Payment link sent" },
  { key: "payment_received",       label: "Payment received" },
  { key: "test_called",            label: "Called for test" },
  { key: "test_taken",             label: "Test taken" },
  { key: "selected_for_interview", label: "Selected · Secretary" },
  { key: "interviewed",            label: "Met the Secretary" },
  { key: "admitted",               label: "Admitted" },
];
const ADM_FALLBACK_TERMINAL = [
  { key: "not_selected", label: "Not selected" },
  { key: "withdrawn",    label: "Withdrawn" },
];

// Cool → warm as the family gets closer to a seat; the two terminal stages are
// deliberately grey/red so they never read as progress.
//
// `fg` is a TEXT weight (the darkened variants), not the saturated fill — these
// are small pill labels on white and the bright dark-theme hues they replaced
// were unreadable here. `bd` is carried explicitly because the border used to
// be built by string-concatenating an alpha suffix onto `fg`, which cannot work
// once `fg` is a var().
const ADM_STAGE_TINT = {
  intent_received:        { fg: "var(--ink-3)",  bg: "rgba(138,138,147,.12)", bd: "rgba(138,138,147,.30)" },
  office_contacted:       { fg: "var(--blue)",   bg: "rgba(59,123,246,.12)",  bd: "rgba(59,123,246,.28)" },
  payment_link_sent:      { fg: "var(--blue)",   bg: "rgba(59,123,246,.12)",  bd: "rgba(59,123,246,.28)" },
  payment_received:       { fg: "var(--violet)", bg: "rgba(107,91,210,.12)",  bd: "rgba(107,91,210,.28)" },
  test_called:            { fg: "var(--amber)",  bg: "rgba(255,159,10,.14)",  bd: "rgba(255,159,10,.32)" },
  test_taken:             { fg: "var(--amber)",  bg: "rgba(255,159,10,.14)",  bd: "rgba(255,159,10,.32)" },
  selected_for_interview: { fg: "var(--amber)",  bg: "rgba(255,159,10,.20)",  bd: "rgba(255,159,10,.38)" },
  interviewed:            { fg: "var(--green)",  bg: "rgba(47,190,95,.12)",   bd: "rgba(47,190,95,.30)" },
  admitted:               { fg: "var(--green)",  bg: "rgba(47,190,95,.20)",   bd: "rgba(47,190,95,.40)" },
  not_selected:           { fg: "var(--red)",    bg: "rgba(229,72,77,.12)",   bd: "rgba(229,72,77,.30)" },
  withdrawn:              { fg: "var(--ink-3)",  bg: "rgba(138,138,147,.12)", bd: "rgba(138,138,147,.30)" },
};

const admAge = (months) => {
  if (months == null) return "—";
  const y = Math.floor(months / 12), m = months % 12;
  return m ? `${y}y ${m}m` : `${y}y`;
};

const admDate = (iso) => iso
  ? new Date(iso).toLocaleDateString(undefined, { day: "numeric", month: "short", year: "numeric" })
  : "—";

const AdmStagePill = ({ stage, label }) => {
  const t = ADM_STAGE_TINT[stage] || ADM_STAGE_TINT.intent_received;
  return (
    <span style={{
      display: "inline-block", padding: "3px 10px", borderRadius: 999,
      fontSize: 11, whiteSpace: "nowrap",
      color: t.fg, background: t.bg, border: `1px solid ${t.bd}`,
    }}>{label || stage}</span>
  );
};

const AdmissionsScreen = () => {
  const [meta, setMeta]       = React.useState({ stages: ADM_FALLBACK_STAGES, terminal: ADM_FALLBACK_TERMINAL, classes: [] });
  const [data, setData]       = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr]         = React.useState(null);
  const [stage, setStage]     = React.useState("");     // "" = all
  const [klass, setKlass]     = React.useState("");
  const [year, setYear]       = React.useState(null);   // null = server's default intake
  const [q, setQ]             = React.useState("");
  const [openId, setOpenId]   = React.useState(null);
  const [busy, setBusy]       = React.useState({});
  const [noteDraft, setNoteDraft] = React.useState("");

  React.useEffect(() => {
    window.KXApi.get("/admission-intents/stages")
      .then(setMeta)
      .catch(() => { /* keep the inline fallback */ });
  }, []);

  const load = React.useCallback(async () => {
    setLoading(true); setErr(null);
    try {
      const qs = new URLSearchParams();
      if (stage) qs.set("stage", stage);
      if (klass) qs.set("class", klass);
      if (year)  qs.set("intake_year", year);
      if (q.trim()) qs.set("q", q.trim());
      setData(await window.KXApi.get(`/admission-intents?${qs}`));
    } catch (e) {
      setErr(String(e.message || e));
    } finally {
      setLoading(false);
    }
  }, [stage, klass, year, q]);

  // Debounced so typing in the search box doesn't fire a request per keystroke.
  React.useEffect(() => {
    const t = setTimeout(load, q ? 300 : 0);
    return () => clearTimeout(t);
  }, [load, q]);

  const allStages = [...(meta.stages || []), ...(meta.terminal || [])];
  const stageLabel = (key) => allStages.find(s => s.key === key)?.label || key;
  const canEdit = data?.can_edit;

  const setStageOn = async (row, next) => {
    setBusy(b => ({ ...b, [row.id]: true }));
    try {
      await window.KXApi.patch(`/admission-intents/${row.id}`, { stage: next });
      await load();
    } catch (e) {
      window.alert("Could not update: " + (e.message || e));
    } finally {
      setBusy(b => { const n = { ...b }; delete n[row.id]; return n; });
    }
  };

  const saveNote = async (row) => {
    setBusy(b => ({ ...b, [row.id]: true }));
    try {
      await window.KXApi.patch(`/admission-intents/${row.id}`, { office_note: noteDraft });
      await load();
    } catch (e) {
      window.alert("Could not save the note: " + (e.message || e));
    } finally {
      setBusy(b => { const n = { ...b }; delete n[row.id]; return n; });
    }
  };

  // The CSV endpoint is behind the Bearer token, so a plain <a href> would come
  // back 401. Fetch it with the token and hand the browser a blob instead.
  const exportCsv = async () => {
    try {
      const token = window.KXApi.getToken ? window.KXApi.getToken() : null;
      const r = await fetch(`/api/admission-intents/export.csv?intake_year=${data?.intake_year || ""}`, {
        headers: token ? { authorization: `Bearer ${token}` } : {},
      });
      if (!r.ok) throw new Error(`export failed (${r.status})`);
      const url = URL.createObjectURL(await r.blob());
      const a = document.createElement("a");
      a.href = url;
      a.download = `admission-intents-${data?.intake_year || "all"}.csv`;
      a.click();
      URL.revokeObjectURL(url);
    } catch (e) {
      window.alert("Could not export: " + (e.message || e));
    }
  };

  const rows = data?.items || [];
  const counts = data?.counts || {};

  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)" }}><AdmIcon name="flag" size={18}/></span>
        <div className="muted" style={{ fontSize: 11, letterSpacing: ".08em", textTransform: "uppercase" }}>
          Admissions · Intent to Apply
        </div>
      </div>
      <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 16, flexWrap: "wrap" }}>
        <div>
          <h1 style={{ margin: "4px 0 6px", color: "var(--ink-0)", fontFamily: "'Instrument Serif', serif", fontWeight: 400, fontSize: 32, letterSpacing: "-0.01em" }}>
            Intent register {data?.intake_year ? `· ${data.intake_year}` : ""}
          </h1>
          <p className="muted" style={{ margin: 0, fontSize: 13.5, maxWidth: 760 }}>
            Families who pressed <strong style={{ color: "var(--ink-1)" }}>Apply now</strong> on the website to say
            they intend to apply. These are <strong style={{ color: "var(--ink-1)" }}>not admission applications</strong> —
            each family is waiting for the office to tell them the process has opened.
          </p>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          {canEdit && (
            <button className="btn sm" onClick={exportCsv}>
              <AdmIcon name="upload" size={11}/> Export CSV
            </button>
          )}
          <button className="btn sm" onClick={load}>Refresh</button>
        </div>
      </div>

      {/* Filters */}
      <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", marginTop: 18 }}>
        <div style={{ position: "relative" }}>
          <span style={{ position: "absolute", left: 9, top: 8, color: "var(--ink-3)" }}><AdmIcon name="search" size={13}/></span>
          <input value={q} onChange={e => setQ(e.target.value)}
            placeholder="Child, guardian, phone or present school…"
            style={{ padding: "7px 10px 7px 28px", width: 300, background: "var(--bg-2)", border: "1px solid var(--line-soft)", borderRadius: 8, color: "var(--ink-0)", fontSize: 12.5 }}/>
        </div>
        <select value={klass} onChange={e => setKlass(e.target.value)}
          style={{ padding: "7px 10px", background: "var(--bg-2)", border: "1px solid var(--line-soft)", borderRadius: 8, color: "var(--ink-0)", fontSize: 12.5 }}>
          <option value="">All classes</option>
          {(meta.classes || []).map(c => <option key={c} value={c}>Class {c}</option>)}
        </select>
        {(data?.intake_years?.length > 1) && (
          <select value={year || data.intake_year} onChange={e => setYear(Number(e.target.value))}
            style={{ padding: "7px 10px", background: "var(--bg-2)", border: "1px solid var(--line-soft)", borderRadius: 8, color: "var(--ink-0)", fontSize: 12.5 }}>
            {data.intake_years.map(y => <option key={y} value={y}>Intake {y}</option>)}
          </select>
        )}
      </div>

      {/* Stage chips — counts are over the whole intake, so they stay put while
          you click through them. */}
      <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 12 }}>
        <button onClick={() => setStage("")}
          className="btn ghost sm"
          style={{ borderColor: stage === "" ? "var(--accent)" : "var(--line-soft)", color: stage === "" ? "var(--accent)" : "var(--ink-2)" }}>
          All <span style={{ opacity: .6, marginLeft: 4 }}>{data?.total ?? "—"}</span>
        </button>
        {allStages.map(s => (
          <button key={s.key} onClick={() => setStage(stage === s.key ? "" : s.key)}
            className="btn ghost sm"
            style={{
              borderColor: stage === s.key ? (ADM_STAGE_TINT[s.key]?.fg || "var(--accent)") : "var(--line-soft)",
              color: stage === s.key ? (ADM_STAGE_TINT[s.key]?.fg || "var(--accent)") : "var(--ink-2)",
            }}>
            {s.label} <span style={{ opacity: .6, marginLeft: 4 }}>{counts[s.key] || 0}</span>
          </button>
        ))}
      </div>

      {err && (
        <div style={{ marginTop: 16, padding: "10px 14px", borderRadius: 8, background: "rgba(255,107,107,.10)", border: "1px solid rgba(255,107,107,.3)", color: "#ff9b9b", fontSize: 12.5 }}>
          {err}
        </div>
      )}

      {loading && !data && <p className="muted" style={{ marginTop: 24 }}>Loading…</p>}

      {data && rows.length === 0 && !loading && (
        <div className="cx-emptycard" style={{ marginTop: 28 }}>
          <p className="muted" style={{ margin: 0, fontSize: 13.5 }}>
            {q || stage || klass
              ? "No intents match this filter."
              : "No one has registered an intent yet. The form lives at adarshabani.in/admission.html."}
          </p>
        </div>
      )}

      {rows.length > 0 && (
        <div className="cx-tablecard" style={{ marginTop: 18 }}><div className="cx-tablescroll">
          <table className="cx-table">
            <thead>
              <tr style={{ background: "var(--bg-2)", color: "var(--ink-2)", textAlign: "left" }}>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Ref</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Child</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Class</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Age on 1 Jan</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Guardian</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Contact</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Facilities</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Stage</th>
                <th style={{ padding: "9px 12px", fontWeight: 500 }}>Filed</th>
              </tr>
            </thead>
            <tbody>
              {rows.map(r => {
                const open = openId === r.id;
                return (
                  <React.Fragment key={r.id}>
                    <tr onClick={() => { setOpenId(open ? null : r.id); setNoteDraft(r.office_note || ""); }}
                      style={{ borderTop: "1px solid var(--line-soft)", cursor: "pointer", background: open ? "var(--bg-2)" : "transparent" }}>
                      <td style={{ padding: "9px 12px", fontFamily: "ui-monospace, monospace", color: "var(--ink-2)" }}>{r.ref_no}</td>
                      <td style={{ padding: "9px 12px", color: "var(--ink-0)" }}>{r.child_name}</td>
                      <td style={{ padding: "9px 12px" }}>{r.class_applied_for}</td>
                      <td style={{ padding: "9px 12px", color: "var(--ink-2)" }}>{admAge(r.age_months_on_jan1)}</td>
                      <td style={{ padding: "9px 12px", color: "var(--ink-2)" }}>{r.guardian_name}</td>
                      <td style={{ padding: "9px 12px", fontFamily: "ui-monospace, monospace", color: "var(--ink-2)" }}>
                        {r.contact_phone}{r.is_whatsapp && <span title="WhatsApp" style={{ color: "#4bc97b", marginLeft: 5 }}>●</span>}
                      </td>
                      <td style={{ padding: "9px 12px", color: "var(--ink-2)" }}>
                        {r.wants_boarding ? "Boarding" : (r.wants_bus ? "Bus" : "Day scholar")}
                      </td>
                      <td style={{ padding: "9px 12px" }}><AdmStagePill stage={r.stage} label={stageLabel(r.stage)}/></td>
                      <td style={{ padding: "9px 12px", color: "var(--ink-3)" }}>{admDate(r.created_at)}</td>
                    </tr>
                    {open && (
                      <tr style={{ background: "var(--bg-2)" }}>
                        <td colSpan={9} style={{ padding: "4px 12px 18px" }}>
                          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 22, padding: "12px 4px" }}>
                            <div>
                              <AdmField label="Date of birth" value={r.date_of_birth}/>
                              <AdmField label={`Age on 1 January ${r.intake_year}`} value={admAge(r.age_months_on_jan1)}/>
                              <AdmField label="Presently studying at" value={r.present_school}/>
                              <AdmField label="Address" value={r.address}/>
                              <AdmField label="Boarding requested" value={r.wants_boarding ? "Yes" : "No"}/>
                              {!r.wants_boarding && <AdmField label="Bus transport requested" value={r.wants_bus ? "Yes" : "No"}/>}
                              <AdmField label="Last updated" value={admDate(r.updated_at)}/>
                            </div>
                            <div>
                              <div className="muted" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".07em", marginBottom: 8 }}>
                                Move to stage
                              </div>
                              {canEdit ? (
                                <div style={{ display: "flex", flexWrap: "wrap", gap: 5, marginBottom: 16 }}>
                                  {allStages.map(s => (
                                    <button key={s.key} className="btn ghost sm"
                                      disabled={busy[r.id] || s.key === r.stage}
                                      onClick={() => setStageOn(r, s.key)}
                                      style={{
                                        opacity: s.key === r.stage ? .45 : 1,
                                        borderColor: s.key === r.stage ? (ADM_STAGE_TINT[s.key]?.fg || "var(--accent)") : "var(--line-soft)",
                                      }}>
                                      {s.label}
                                    </button>
                                  ))}
                                </div>
                              ) : (
                                <p className="muted" style={{ fontSize: 12, marginBottom: 16 }}>
                                  Only the admissions office (admin, HoD or coordinator) can move a family along.
                                </p>
                              )}

                              <div className="muted" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".07em", marginBottom: 6 }}>
                                Office note
                              </div>
                              {canEdit ? (
                                <>
                                  <textarea value={noteDraft} onChange={e => setNoteDraft(e.target.value)} rows={3}
                                    placeholder="Called on 12 Aug, asked to bring the birth certificate…"
                                    style={{ width: "100%", padding: 9, background: "var(--bg-1)", border: "1px solid var(--line-soft)", borderRadius: 8, color: "var(--ink-0)", fontSize: 12.5, resize: "vertical" }}/>
                                  <button className="btn sm primary" style={{ marginTop: 8 }}
                                    disabled={busy[r.id] || noteDraft === (r.office_note || "")}
                                    onClick={() => saveNote(r)}>
                                    Save note
                                  </button>
                                </>
                              ) : (
                                <p style={{ fontSize: 12.5, color: "var(--ink-1)", whiteSpace: "pre-wrap", margin: 0 }}>
                                  {r.office_note || "—"}
                                </p>
                              )}
                            </div>
                          </div>
                        </td>
                      </tr>
                    )}
                  </React.Fragment>
                );
              })}
            </tbody>
          </table>
          </div>
        </div>
      )}
    </div>
  );
};

const AdmField = ({ label, value }) => (
  <div style={{ marginBottom: 10 }}>
    <div className="muted" style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: ".07em" }}>{label}</div>
    <div style={{ fontSize: 13, color: "var(--ink-0)", whiteSpace: "pre-wrap" }}>{value || "—"}</div>
  </div>
);

window.AdmissionsScreen = AdmissionsScreen;
