// ── Control-plane shared UI primitives ─────────────────────────────────────
// House style (matches the LMS components): hooks as React.useX so nothing is
// re-declared across the separately-executed <script> tags.

// ── Toast ────────────────────────────────────────────────────────────────────
const ToastCtx = React.createContext(() => {});
function ToastProvider({ children }) {
  const [msg, setMsg] = React.useState(null);
  const notify = React.useCallback((text) => {
    setMsg(text);
    clearTimeout(notify._t);
    notify._t = setTimeout(() => setMsg(null), 2800);
  }, []);
  return (
    <ToastCtx.Provider value={notify}>
      {children}
      {msg && <div className="toast">{msg}</div>}
    </ToastCtx.Provider>
  );
}
const useToast = () => React.useContext(ToastCtx);

// ── Icons (inline, currentColor) ────────────────────────────────────────────
const CpIcon = {
  cloud: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 13v8" /><path d="m8 17 4-4 4 4" />
      <path d="M20 16.58A5 5 0 0 0 18 7h-1.26A8 8 0 1 0 4 15.25" />
    </svg>
  ),
  users: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" />
      <path d="M22 21v-2a4 4 0 0 0-3-3.87" /><path d="M16 3.13a4 4 0 0 1 0 7.75" />
    </svg>
  ),
  building: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect width="16" height="20" x="4" y="2" rx="2" /><path d="M9 22v-4h6v4" />
      <path d="M8 6h.01M16 6h.01M12 6h.01M12 10h.01M12 14h.01M16 10h.01M16 14h.01M8 10h.01M8 14h.01" />
    </svg>
  ),
};

// ── Bits ─────────────────────────────────────────────────────────────────────
function Pill({ tone = "mute", children }) {
  return <span className={"pill " + tone}>{children}</span>;
}

const CP_STATUS_TONE = {
  draft: "mute", active: "ok", provisioning: "accent", suspended: "warn",
  deleting: "warn", deleted: "mute", error: "danger",
};
const CP_STATUS_LABEL = { draft: "details incomplete" };
function StatusPill({ status }) {
  return <Pill tone={CP_STATUS_TONE[status] || "mute"}>{CP_STATUS_LABEL[status] || status}</Pill>;
}

function Spinner() { return <span className="spin" />; }

function Toggle({ on, onChange, disabled }) {
  return (
    <button type="button" className="toggle" data-on={!!on} disabled={disabled}
      aria-pressed={!!on} onClick={() => !disabled && onChange(!on)} />
  );
}

function Field({ label, hint, required, children, max, value }) {
  return (
    <div className="field">
      {label && <label>{label}{required && <span className="req">*</span>}</label>}
      {children}
      {max != null && <CharCount value={value || ""} max={max} />}
      {hint && <div className="hint">{hint}</div>}
    </div>
  );
}

function CharCount({ value, max }) {
  const n = (value || "").length;
  return <span className={"charcount" + (n > max ? " over" : "")}>{n}/{max}</span>;
}

function SectionHeader({ title, desc }) {
  return (
    <div className="section-head">
      <h3>{title}</h3>
      {desc && <p>{desc}</p>}
    </div>
  );
}

function TabBar({ tabs, active, onChange }) {
  return (
    <div className="tabbar" role="tablist">
      {tabs.map((t) => {
        const key = typeof t === "string" ? t : t.key;
        const label = typeof t === "string" ? t : t.label;
        return (
          <button key={key} role="tab" aria-selected={active === key}
            onClick={() => onChange(key)}>{label}</button>
        );
      })}
    </div>
  );
}

function EmptyState({ title, body, action }) {
  return (
    <div className="empty">
      {title && <div className="t">{title}</div>}
      {body && <div>{body}</div>}
      {action && <div style={{ marginTop: 14 }}>{action}</div>}
    </div>
  );
}

function KpiCard({ label, value, tone }) {
  return (
    <div className={"kpi" + (tone ? " " + tone : "")}>
      <div className="n">{value == null ? "—" : value}</div>
      <div className="l">{label}</div>
    </div>
  );
}

function CopyBox({ value }) {
  const notify = useToast();
  return (
    <div className="copybox">
      <input readOnly value={value} onFocus={(e) => e.target.select()} />
      <button className="btn sm" onClick={() => {
        navigator.clipboard?.writeText(value).then(() => notify("Copied"));
      }}>Copy</button>
    </div>
  );
}

function HealthDot({ state }) {
  const cls = state === "ok" ? "ok" : state === "provisioning" ? "warn" : "bad";
  return <span className={"hdot " + cls} title={state} />;
}

// ── Upload card (base64-in-JSON) ────────────────────────────────────────────
function UploadCard({ accept = "image/png,image/jpeg,image/svg+xml,image/webp", hint, preview, onFile, onClear, busy }) {
  const inputRef = React.useRef(null);
  const [drag, setDrag] = React.useState(false);

  const take = (file) => {
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => onFile({ file_name: file.name, file_data: String(reader.result) });
    reader.readAsDataURL(file);
  };

  if (preview) {
    return (
      <div className="upload-preview">
        <img src={preview} alt="logo preview" />
        <div style={{ flex: 1, fontSize: 13, color: "var(--ink-mute)" }}>Current logo</div>
        <button className="btn sm" disabled={busy} onClick={onClear}>Remove</button>
      </div>
    );
  }

  return (
    <div
      className={"upload-card" + (drag ? " drag" : "")}
      onClick={() => inputRef.current?.click()}
      onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
      onDragLeave={() => setDrag(false)}
      onDrop={(e) => { e.preventDefault(); setDrag(false); take(e.dataTransfer.files[0]); }}
    >
      <input ref={inputRef} type="file" accept={accept} hidden
        onChange={(e) => take(e.target.files[0])} />
      {busy ? <Spinner /> : CpIcon.cloud}
      <div className="t">Drop a file here or click to browse</div>
      <div className="s">{hint || "PNG, JPG, SVG or WebP"}</div>
    </div>
  );
}

// ── Person card (point-of-contact) ─────────────────────────────────────────
const CONTACT_ROLES = ["Principal", "Director", "Owner", "IT admin", "Academic coordinator", "Teacher", "Finance / billing", "Other"];
function RoleSelect({ value, onChange }) {
  const opts = value && !CONTACT_ROLES.includes(value) ? [value, ...CONTACT_ROLES] : CONTACT_ROLES; // keep legacy free-text roles
  return (
    <select value={value || ""} onChange={onChange}>
      <option value="">Select a role…</option>
      {opts.map((r) => <option key={r} value={r}>{r}</option>)}
    </select>
  );
}

function PersonCard({ person, onSave, onDelete, onMakePrimary }) {
  const [editing, setEditing] = React.useState(!person.id);
  const [f, setF] = React.useState({
    name: person.name || "", role: person.role || "",
    email: person.email || "", phone: person.phone || "",
  });
  const set = (k) => (e) => setF({ ...f, [k]: e.target.value });

  if (editing) {
    return (
      <div className="person-card">
        <Field label="Name" required><input value={f.name} onChange={set("name")} autoFocus /></Field>
        <div className="row2">
          <Field label="Role"><RoleSelect value={f.role} onChange={set("role")} /></Field>
          <Field label="Phone"><input value={f.phone} onChange={set("phone")} /></Field>
        </div>
        <Field label="Email"><input type="email" value={f.email} onChange={set("email")} /></Field>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn sm primary" disabled={!f.name.trim()}
            onClick={() => { onSave({ ...person, ...f }); if (person.id) setEditing(false); }}>Save</button>
          {person.id && <button className="btn sm" onClick={() => setEditing(false)}>Cancel</button>}
        </div>
      </div>
    );
  }

  return (
    <div className="person-card" style={{ display: "flex", alignItems: "flex-start", gap: 12 }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontWeight: 700, display: "flex", alignItems: "center", gap: 8 }}>
          {person.name}
          {person.is_primary && <Pill tone="accent">Primary</Pill>}
        </div>
        <div style={{ fontSize: 13, color: "var(--ink-mute)", marginTop: 2 }}>
          {[person.role, person.email, person.phone].filter(Boolean).join(" · ") || "—"}
        </div>
      </div>
      <div style={{ display: "flex", gap: 6 }}>
        {!person.is_primary && onMakePrimary &&
          <button className="btn sm ghost" onClick={() => onMakePrimary(person)}>Make primary</button>}
        <button className="btn sm ghost" onClick={() => setEditing(true)}>Edit</button>
        <button className="btn sm ghost" style={{ color: "var(--danger)" }}
          onClick={() => onDelete(person)}>Delete</button>
      </div>
    </div>
  );
}

// ── Wizard footer ─────────────────────────────────────────────────────────
function WizardFooter({ step, busy, canNext = true, nextLabel = "Next", status, onCancel, onBack, onNext }) {
  return (
    <div className="wizard-foot">
      <button className="btn ghost" disabled={busy} onClick={onCancel}>Cancel</button>
      {step > 0 && <button className="btn" disabled={busy} onClick={onBack}>Back</button>}
      <div className="spacer" />
      {status && <span style={{ fontSize: 12, color: "var(--ink-mute)" }}>{status}</span>}
      <button className="btn primary" disabled={busy || !canNext} onClick={onNext}>{busy ? <Spinner /> : nextLabel}</button>
    </div>
  );
}

// ── Modal ────────────────────────────────────────────────────────────────────
function Modal({ title, onClose, children, wide }) {
  React.useEffect(() => {
    const h = (e) => e.key === "Escape" && onClose();
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [onClose]);
  return (
    <div onClick={onClose}
      style={{ position: "fixed", inset: 0, background: "oklch(0.2 0.03 258 / 0.4)",
        display: "flex", alignItems: "flex-start", justifyContent: "center",
        padding: "7vh 16px", zIndex: 50, overflowY: "auto" }}>
      <div onClick={(e) => e.stopPropagation()} className="panel card-shadow"
        style={{ width: wide ? 700 : 460, maxWidth: "100%", padding: 26 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 18 }}>
          <h2 style={{ fontSize: 19 }}>{title}</h2>
          <button className="btn sm ghost" onClick={onClose} aria-label="Close">✕</button>
        </div>
        {children}
      </div>
    </div>
  );
}

function timeAgo(iso) {
  if (!iso) return "—";
  const s = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
  if (s < 60) return "just now";
  if (s < 3600) return Math.floor(s / 60) + "m ago";
  if (s < 86400) return Math.floor(s / 3600) + "h ago";
  return Math.floor(s / 86400) + "d ago";
}
