// ── Control-plane root ─────────────────────────────────────────────────────

// hash router: #/  #/tenants  #/tenants/<objectId>  #/tenants/<objectId>/branding  #/operators …
function useHashRoute() {
  const parse = () => {
    const h = (window.location.hash || "#/").replace(/^#\/?/, "");
    const p = h.split("/").filter(Boolean);
    return { section: p[0] || "", id: p[1] || null, tab: p[2] || null };
  };
  const [route, setRoute] = React.useState(parse);
  React.useEffect(() => {
    const on = () => setRoute(parse());
    window.addEventListener("hashchange", on);
    return () => window.removeEventListener("hashchange", on);
  }, []);
  return [route, (to) => { window.location.hash = to; }];
}

// ── Login (password → emailed code) ────────────────────────────────────────
function CpLogin({ onIn }) {
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const [step, setStep] = React.useState("password");
  const [ticket, setTicket] = React.useState(null);
  const [maskedEmail, setMaskedEmail] = React.useState("");
  const [code, setCode] = React.useState("");
  const [err, setErr] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const submitPassword = async (e) => {
    e.preventDefault(); setBusy(true); setErr(null);
    try {
      const r = await cpApi.login(email, password);
      if (r.otpRequired) { setTicket(r.ticket); setMaskedEmail(r.email); setStep("otp"); setBusy(false); }
      else if (r.operator) onIn(r.operator);
    } catch (e) { setErr(e.message); setBusy(false); }
  };
  const submitCode = async (e) => {
    e.preventDefault(); setBusy(true); setErr(null);
    try { onIn((await cpApi.verifyOtp(ticket, code.trim())).operator); }
    catch (e) { setErr(e.message); setBusy(false); }
  };
  const resend = async () => {
    setErr(null); setCode("");
    try { const r = await cpApi.login(email, password); if (r.otpRequired) { setTicket(r.ticket); setErr("New code sent."); } }
    catch (e) { setErr(e.message); }
  };

  return (
    <div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", padding: 20 }}>
      <div className="panel card-shadow" style={{ padding: 30, width: 380 }}>
        <h1 style={{ fontSize: 21, marginBottom: 4 }}>Admin Control Plane</h1>
        <p style={{ color: "var(--ink-mute)", marginTop: 0, marginBottom: 22, fontSize: 13 }}>Internal — tenant management for the LMS.</p>
        {step === "password" ? (
          <form onSubmit={submitPassword}>
            <Field label="Email"><input type="email" autoFocus value={email} onChange={(e) => setEmail(e.target.value)} required /></Field>
            <Field label="Password"><input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required /></Field>
            {err && <div style={{ color: "var(--danger)", fontSize: 13, marginBottom: 12 }}>{err}</div>}
            <button className="btn primary block" disabled={busy}>{busy ? <Spinner /> : "Continue"}</button>
            <div style={{ textAlign: "center", marginTop: 18, fontSize: 12, color: "var(--ink-mute)" }}>Powered by EpiqMinds</div>
          </form>
        ) : (
          <form onSubmit={submitCode}>
            <p style={{ fontSize: 13, color: "var(--ink-soft)", marginTop: 0 }}>We emailed a 6-digit code to <strong>{maskedEmail}</strong>.</p>
            <Field label="Sign-in code">
              <input autoFocus inputMode="numeric" maxLength={6} value={code}
                onChange={(e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6))}
                style={{ letterSpacing: "6px", fontFamily: "var(--mono)", fontSize: 18, textAlign: "center" }} required />
            </Field>
            {err && <div style={{ color: err === "New code sent." ? "var(--ok)" : "var(--danger)", fontSize: 13, marginBottom: 12 }}>{err}</div>}
            <button className="btn primary block" disabled={busy || code.length !== 6}>{busy ? <Spinner /> : "Sign in"}</button>
            <div style={{ marginTop: 12, display: "flex", justifyContent: "space-between" }}>
              <button type="button" className="btn sm ghost" onClick={resend}>Resend code</button>
              <button type="button" className="btn sm ghost" onClick={() => { setStep("password"); setErr(null); setCode(""); }}>Back</button>
            </div>
          </form>
        )}
      </div>
    </div>
  );
}

function CpChangePassword({ onDone }) {
  const notify = useToast();
  const [cur, setCur] = React.useState("");
  const [next, setNext] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const submit = async (e) => {
    e.preventDefault(); setBusy(true);
    try { await cpApi.changePassword(cur, next); notify("Password changed"); onDone(); }
    catch (e) { notify(e.message); setBusy(false); }
  };
  return (
    <Modal title="Set a new password" onClose={() => {}}>
      <form onSubmit={submit}>
        <Field label="Current password"><input type="password" value={cur} onChange={(e) => setCur(e.target.value)} required /></Field>
        <Field label="New password" hint="At least 8 characters."><input type="password" value={next} onChange={(e) => setNext(e.target.value)} required /></Field>
        <button className="btn primary" disabled={busy}>{busy ? <Spinner /> : "Save"}</button>
      </form>
    </Modal>
  );
}

// ── New tenant ────────────────────────────────────────────────────────────
function CpCreateTenant({ onClose, onCreated }) {
  const notify = useToast();
  const [name, setName] = React.useState("");
  const [slug, setSlug] = React.useState("");
  const [plan, setPlan] = React.useState("standard");
  const [touched, setTouched] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const auto = (v) => v.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
  const submit = async (e) => {
    e.preventDefault(); setBusy(true); setErr(null);
    try { const { tenant } = await cpApi.createTenant({ name, slug, plan }); notify(`Tenant "${tenant.slug}" registered`); onCreated(tenant); }
    catch (e) { setErr(e.message); setBusy(false); }
  };
  return (
    <Modal title="New tenant" onClose={onClose}>
      <form onSubmit={submit}>
        <Field label="School / client name" required>
          <input autoFocus value={name} onChange={(e) => { setName(e.target.value); if (!touched) setSlug(auto(e.target.value)); }} required />
        </Field>
        <Field label="Slug" required hint={`Subdomain: ${slug || "<slug>"}.<base domain>`}>
          <input value={slug} onChange={(e) => { setTouched(true); setSlug(auto(e.target.value)); }} required />
        </Field>
        <Field label="Plan">
          <select value={plan} onChange={(e) => setPlan(e.target.value)}>
            <option value="standard">standard</option><option value="pro">pro</option><option value="trial">trial</option>
          </select>
        </Field>
        <div style={{ background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: "10px 12px", fontSize: 12, color: "var(--ink-mute)", marginBottom: 16 }}>
          Records the tenant + a queued provisioning job. Automated provisioning (create DB → migrate → seed) lands in Milestone B. Add branding, contacts and the admin email on the tenant page after creating.
        </div>
        {err && <div style={{ color: "var(--danger)", fontSize: 13, marginBottom: 12 }}>{err}</div>}
        <button className="btn primary" disabled={busy}>{busy ? <Spinner /> : "Create tenant"}</button>
      </form>
    </Modal>
  );
}

// ── Tenants list ──────────────────────────────────────────────────────────
function CpTenantsList({ nav, refreshKey, onNew, onResume }) {
  const [rows, setRows] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const [filter, setFilter] = React.useState("all");
  const [search, setSearch] = React.useState("");
  const [deleting, setDeleting] = React.useState(null); // tenant row
  const [reload, setReload] = React.useState(0);
  React.useEffect(() => { cpApi.listTenants().then((d) => setRows(d.data)).catch((e) => setErr(e.message)); }, [refreshKey, reload]);

  if (err) return <div className="panel pad" style={{ color: "var(--danger)" }}>{err}</div>;

  const shown = (rows || []).filter((t) => {
    if (filter === "all" ? t.status === "deleted" : t.status !== filter) return false; // deleted only under its own filter
    if (search && !(`${t.name} ${t.slug}`.toLowerCase().includes(search.toLowerCase()))) return false;
    return true;
  });

  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", marginBottom: 16, gap: 12, flexWrap: "wrap" }}>
        <h1 style={{ fontSize: 24, flex: 1 }}>Tenants</h1>
        <input placeholder="Search…" value={search} onChange={(e) => setSearch(e.target.value)}
          style={{ padding: "8px 12px", borderRadius: 7, border: "1px solid var(--line-strong)", background: "var(--panel)", width: 180 }} />
        <select value={filter} onChange={(e) => setFilter(e.target.value)}
          style={{ padding: "8px 12px", borderRadius: 7, border: "1px solid var(--line-strong)", background: "var(--panel)" }}>
          <option value="all">All statuses</option><option value="active">Active</option>
          <option value="suspended">Suspended</option><option value="provisioning">Provisioning</option>
          <option value="draft">Draft</option><option value="error">Error</option><option value="deleted">Deleted</option>
        </select>
        <button className="btn primary" onClick={onNew}>+ New tenant</button>
      </div>
      <div className="panel tablewrap">
        <table className="grid">
          <thead><tr><th>Name</th><th>Subdomain</th><th>Status</th><th>Plan</th><th>Flags off</th><th>Created</th><th></th></tr></thead>
          <tbody>
            {!rows && <tr><td colSpan={7}><Spinner /> Loading…</td></tr>}
            {rows && shown.length === 0 && <tr><td colSpan={7} style={{ color: "var(--ink-mute)" }}>No tenants match.</td></tr>}
            {shown.map((t) => {
              const nOff = Object.values(t.featureFlags || {}).filter((v) => v === false).length;
              return (
                <tr key={t.id} className="row" onClick={() => nav(`#/tenants/${t.id}`)}>
                  <td><div style={{ fontWeight: 600 }}>{t.name} {t.isTenantZero && <Pill tone="accent">zero</Pill>}</div>
                    {t.deletion && <div style={{ fontSize: 12, color: "var(--ink-mute)" }}>
                      was {t.slug} · {DELETION_LABEL[t.deletion.stage] || t.deletion.stage}</div>}</td>
                  <td className="mono" style={{ fontSize: 12, color: "var(--ink-soft)" }}>{t.primarySubdomain || "—"}
                    {t.status !== "deleted" && <span title={t.live ? "Serving traffic" : "Not serving traffic"}
                      style={{ marginLeft: 8, color: t.live ? "var(--ok)" : "var(--ink-mute)" }}>{t.live ? "● live" : "○ offline"}</span>}</td>
                  <td><StatusPill status={t.status} /></td>
                  <td style={{ color: "var(--ink-soft)" }}>{t.plan}</td>
                  <td style={{ color: "var(--ink-mute)" }}>{nOff || "—"}</td>
                  <td style={{ color: "var(--ink-mute)" }}>{timeAgo(t.createdAt)}</td>
                  <td style={{ textAlign: "right" }} onClick={(e) => e.stopPropagation()}>
                    {t.status === "draft" && (
                      <button className="btn sm" style={{ marginRight: 6 }} onClick={() => onResume(t)}>
                        Continue setup{t.setupStep != null ? ` (${["Basics", "Contact", "Admin"][t.setupStep] || "Admin"})` : ""}
                      </button>
                    )}
                    {!t.isTenantZero && (t.status !== "deleted" || hasDb(t)) && (
                      <button className="btn sm ghost" style={{ color: "var(--danger)" }} onClick={() => setDeleting(t)}>
                        {t.status === "deleted" ? "Delete data" : "Delete"}
                      </button>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      {deleting && (
        <DeleteTenantModal tenant={deleting} mode={deleting.status === "deleted" ? "atlas" : "delete"}
          onClose={() => setDeleting(null)}
          onDone={() => { setDeleting(null); setReload((n) => n + 1); }} />
      )}
    </div>
  );
}
// A deleted tenant whose data is still there (deletion.stage "offline").
const hasDb = (t) => !!(t.deletion && t.deletion.stage === "offline");
const DELETION_LABEL = { offline: "offline, data kept", deleting_data: "deleting data…", data_deleted: "data deleted" };

function CpAuditView() {
  const [state, setState] = React.useState({ loading: true });
  const [opts, setOpts] = React.useState({ actions: [], actors: [], tenants: [] });
  const [pageSize, setPageSize] = React.useState(10);
  const [page, setPage] = React.useState(0);
  const [f, setF] = React.useState({ action: "", actor: "", tenantId: "", since: "" });

  React.useEffect(() => { cpApi.auditFilters().then(setOpts).catch(() => {}); }, []);

  const load = React.useCallback(() => {
    setState((s) => ({ ...s, loading: true }));
    cpApi.audit({ limit: pageSize, offset: page * pageSize, ...f })
      .then((d) => setState({ loading: false, ...d }))
      .catch((e) => setState({ loading: false, error: e.message }));
  }, [pageSize, page, f]);
  React.useEffect(() => { load(); }, [load]);

  const setFilter = (k, v) => { setPage(0); setF((x) => ({ ...x, [k]: v })); };
  const rows = state.data || [];
  const total = state.total || 0;
  const pages = Math.max(1, Math.ceil(total / pageSize));
  const active = Object.values(f).some(Boolean);

  const sel = { padding: "7px 10px", borderRadius: 7, border: "1px solid var(--line-strong)", background: "var(--panel)", fontSize: 13 };

  return (
    <div>
      <h1 style={{ fontSize: 24, marginBottom: 16 }}>Audit log</h1>

      <div className="panel pad" style={{ marginBottom: 14, display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center" }}>
        <span style={{ fontSize: 12, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--ink-mute)", fontWeight: 600 }}>Filters</span>
        <select style={sel} value={f.action} onChange={(e) => setFilter("action", e.target.value)}>
          <option value="">Any action</option>
          {opts.actions.map((a) => <option key={a} value={a}>{a}</option>)}
        </select>
        <select style={sel} value={f.actor} onChange={(e) => setFilter("actor", e.target.value)}>
          <option value="">Any operator</option>
          {opts.actors.map((a) => <option key={a} value={a}>{a}</option>)}
        </select>
        <select style={sel} value={f.tenantId} onChange={(e) => setFilter("tenantId", e.target.value)}>
          <option value="">Any tenant</option>
          {opts.tenants.map((t) => <option key={t.id} value={t.id}>{t.slug}</option>)}
        </select>
        <input type="date" style={sel} value={f.since} onChange={(e) => setFilter("since", e.target.value)} title="From date" />
        {active && <button className="btn sm ghost" onClick={() => { setPage(0); setF({ action: "", actor: "", tenantId: "", since: "" }); }}>Clear</button>}
      </div>

      <div className="panel tablewrap">
        <table className="grid">
          <thead><tr><th>When</th><th>Operator</th><th>Action</th><th>Tenant</th><th>Detail</th></tr></thead>
          <tbody>
            {state.loading && !state.data && <tr><td colSpan={5}><Spinner /> Loading…</td></tr>}
            {!state.loading && rows.length === 0 && <tr><td colSpan={5} style={{ color: "var(--ink-mute)" }}>{active ? "No entries match these filters." : "Nothing logged yet."}</td></tr>}
            {rows.map((r) => (
              <tr key={r.id}>
                <td style={{ color: "var(--ink-mute)", whiteSpace: "nowrap" }} title={new Date(r.created_at).toLocaleString()}>{timeAgo(r.created_at)}</td>
                <td style={{ fontSize: 13 }}>{r.actor_email || "—"}</td>
                <td className="mono" style={{ fontSize: 12 }}>{r.action}</td>
                <td style={{ fontSize: 13 }}>{r.tenant_slug || "—"}</td>
                <td className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", maxWidth: 340, overflow: "hidden", textOverflow: "ellipsis" }}>
                  {r.detail && Object.keys(r.detail).length ? JSON.stringify(r.detail) : "—"}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 14, flexWrap: "wrap" }}>
        <span style={{ fontSize: 13, color: "var(--ink-mute)" }}>Rows per page</span>
        <select style={sel} value={pageSize} onChange={(e) => { setPage(0); setPageSize(+e.target.value); }}>
          {[10, 25, 50].map((n) => <option key={n} value={n}>{n}</option>)}
        </select>
        <div style={{ flex: 1 }} />
        <span style={{ fontSize: 13, color: "var(--ink-mute)" }}>
          {total === 0 ? "0" : `${page * pageSize + 1}–${Math.min((page + 1) * pageSize, total)}`} of {total}
        </span>
        <button className="btn sm" disabled={page === 0} onClick={() => setPage(0)}>«</button>
        <button className="btn sm" disabled={page === 0} onClick={() => setPage((p) => p - 1)}>‹ Prev</button>
        <span style={{ fontSize: 13 }}>Page {page + 1} / {pages}</span>
        <button className="btn sm" disabled={page + 1 >= pages} onClick={() => setPage((p) => p + 1)}>Next ›</button>
        <button className="btn sm" disabled={page + 1 >= pages} onClick={() => setPage(pages - 1)}>»</button>
      </div>
    </div>
  );
}


// ── Shell ─────────────────────────────────────────────────────────────────
function CpShell({ operator, onOut }) {
  const [route, nav] = useHashRoute();
  const [wizard, setWizard] = React.useState(null); // null | { resume?: tenant row }
  const [refreshKey, setRefreshKey] = React.useState(0);
  const [flagsCatalog, setFlagsCatalog] = React.useState([]);
  React.useEffect(() => { cpApi.flagsCatalog().then((d) => setFlagsCatalog(d.flags)).catch(() => {}); }, []);
  const bump = () => setRefreshKey((k) => k + 1);

  // Old bookmarks used numeric tenant ids (#/tenants/1). Tenants now have an
  // ObjectId and keep the old number as legacyId — swap the URL in place.
  const legacyId = route.section === "tenants" && /^\d+$/.test(route.id || "") ? Number(route.id) : null;
  React.useEffect(() => {
    if (legacyId == null) return;
    cpApi.listTenants()
      .then((d) => {
        const t = (d.data || []).find((x) => x.legacyId === legacyId);
        window.location.replace(t ? `#/tenants/${t.id}${route.tab ? `/${route.tab}` : ""}` : "#/tenants");
      })
      .catch(() => window.location.replace("#/tenants"));
  }, [legacyId]);

  const NavLink = ({ to, label, match }) => {
    const active = (match || []).includes(route.section);
    return (
      <a href={to} style={{
        padding: "8px 12px", borderRadius: 7, fontWeight: 600, fontSize: 13.5,
        color: active ? "var(--accent)" : "var(--ink-soft)",
        background: active ? "var(--accent-soft)" : "transparent",
      }}>{label}</a>
    );
  };

  return (
    <div style={{ display: "grid", gridTemplateColumns: "212px 1fr", minHeight: "100vh" }}>
      <aside style={{ position: "relative", borderRight: "1px solid var(--line)", padding: 18, background: "var(--panel)" }}>
        <div style={{ fontFamily: "var(--display)", fontWeight: 700, fontSize: 15, marginBottom: 22 }}>
          Admin <span style={{ color: "var(--ink-mute)", fontWeight: 500 }}>console</span>
        </div>
        <nav style={{ display: "flex", flexDirection: "column", gap: 4 }}>
          <NavLink to="#/" label="Dashboard" match={[""]} />
          <NavLink to="#/tenants" label="Tenants" match={["tenants"]} />
          <NavLink to="#/operators" label="Operators" match={["operators"]} />
          <NavLink to="#/templates" label="Templates" match={["templates"]} />
          <NavLink to="#/audit" label="Audit log" match={["audit"]} />
        </nav>
        <div style={{ position: "absolute", bottom: 18, left: 18, right: 18, fontSize: 12, color: "var(--ink-mute)" }}>
          <div style={{ marginBottom: 6, overflow: "hidden", textOverflow: "ellipsis" }}>{operator.email}</div>
          <button className="btn sm" onClick={onOut}>Sign out</button>
        </div>
      </aside>

      <main style={{ padding: "30px 34px", maxWidth: 1120 }}>
        {route.section === "" && <CpDashboard nav={nav} />}
        {route.section === "tenants" && !route.id && (
          <CpTenantsList nav={nav} refreshKey={refreshKey} onNew={() => setWizard({})} onResume={(t) => setWizard({ resume: t })} />
        )}
        {legacyId != null && <div style={{ color: "var(--ink-mute)" }}><Spinner /> Opening tenant…</div>}
        {route.section === "tenants" && route.id && legacyId == null && (
          <TenantDetail
            tenantId={route.id}
            tab={route.tab}
            onTab={(t) => nav(`#/tenants/${route.id}/${t}`)}
            flagsCatalog={flagsCatalog}
            onBack={() => nav("#/tenants")}
            onChanged={bump}
            onResume={(t) => setWizard({ resume: t })}
          />
        )}
        {route.section === "operators" && <CpOperators />}
        {route.section === "templates" && <CpTemplates />}
        {route.section === "audit" && <CpAuditView />}
      </main>

      {wizard && (
        <CpCreateWizard resume={wizard.resume} onClose={() => { setWizard(null); bump(); }}
          onProvisioned={() => bump()} />
      )}
    </div>
  );
}

function CpApp() {
  const [state, setState] = React.useState({ loading: true, operator: null });
  React.useEffect(() => {
    cpApi.me().then((d) => setState({ loading: false, operator: d.operator }))
      .catch(() => setState({ loading: false, operator: null }));
  }, []);
  const signOut = async () => { try { await cpApi.logout(); } finally { setState({ loading: false, operator: null }); } };

  if (state.loading) return <div className="boot"><Spinner /> Loading control plane…</div>;
  if (!state.operator) return <CpLogin onIn={(operator) => setState({ loading: false, operator })} />;
  if (state.operator.mustChangePassword)
    return <CpChangePassword onDone={() => setState((s) => ({ ...s, operator: { ...s.operator, mustChangePassword: false } }))} />;
  return <CpShell operator={state.operator} onOut={signOut} />;
}

ReactDOM.createRoot(document.getElementById("cp-root")).render(
  <ToastProvider><CpApp /></ToastProvider>
);
