/* global React, Icon, Button, Badge, Avatar, Card, StatusDot, BarChart, LineChart, ProgressBar, Logo, DATA, MenuItem,
   AdminTeam, AdminRoles, AdminIPhones, AdminTasks, AdminAnalytics */
// ============================================================
// Admin — shell + sidebar + Overview
// ============================================================
const NAV = [
  { id: "overview", label: "Overview",    icon: "chart" },
  { id: "requests", label: "Demandes",    icon: "user" },
  { id: "pilotage", label: "Pilotage iPhone", icon: "phone" },
  { id: "logs",     label: "Logs / Audit", icon: "list" },
  { id: "team",     label: "Team",        icon: "users" },
  { id: "messages", label: "Messages",    icon: "send" },
  { id: "iphones",  label: "iPhones",     icon: "phone" },
  { id: "onboarding", label: "Onboarding", icon: "bolt", ownerOnly: true },
  { id: "folders",  label: "Dossiers",    icon: "grid" },
  { id: "tasks",    label: "Tasks / SOP", icon: "clipboard" },
  { id: "analytics",label: "Analytics",   icon: "list" },
  { id: "roles",    label: "Roles",       icon: "shield",  ownerOnly: true },
  { id: "billing",  label: "Facturation", icon: "wallet",  ownerOnly: true },
];

function AdminShell({ user, role = "admin", onLogout, onSwitchRole, theme, setTheme }) {
  const { useState } = React;
  const isOwner = role === "admin";
  const roles = useRoles();
  const msg = useMsg();
  const [page, setPage] = useState("overview");
  const [msgTarget, setMsgTarget] = useState(null);
  const [watchOp, setWatchOp] = useState(null);
  const goMessage = (opId) => { setMsgTarget(opId); setPage("messages"); };
  const onWatch = (opId) => setWatchOp(opId);
  const unreadMsg = msg.totalAdmin();
  // manager : billing visible seulement si le owner lui a coché la permission
  const canSee = (n) => isOwner || !n.ownerOnly || (n.id === "billing" && roles.can("Manager", "billing"));
  const nav = NAV.filter(canSee);
  // nb de comptes en attente (badge de nav)
  const { users: regUsers } = useRegUsers(6000);
  const pendingCount = (regUsers || []).filter(u => u.status === "pending").length;
  const pages = {
    overview: <Overview onGo={setPage} onMessage={goMessage} onWatch={onWatch} isOwner={isOwner} />,
    requests: <AdminRequests me={user} />,
    pilotage: <AdminPilotage user={user} />,
    logs: <AdminLogs />,
    team: <AdminTeam onMessage={goMessage} onWatch={onWatch} me={user} />,
    messages: <AdminMessages initialOp={msgTarget} onWatch={onWatch} />,
    roles: <AdminRoles />,
    iphones: <AdminIPhones />,
    onboarding: <EngineOnboard />,
    folders: <AdminFolders />,
    tasks: <AdminTasks />,
    analytics: <AdminAnalytics onWatch={onWatch} onMessage={goMessage} />,
    billing: <AdminBilling />,
  };
  // garde-fou : un manager ne peut atteindre une page owner-only (sauf billing si autorisé)
  const allowed = (id) => { const n = NAV.find(x => x.id === id); return n ? canSee(n) : true; };
  const safePage = allowed(page) ? page : "overview";
  const [collapsed, setCollapsed] = useState(false);
  return (
    <div style={{ display: "grid", gridTemplateColumns: collapsed ? "1fr" : "248px 1fr", minHeight: "100%" }}>
      {/* sidebar */}
      {!collapsed && <aside style={{ position: "sticky", top: 0, height: "100vh", background: "var(--bg-2)", borderRight: "1px solid var(--border)", display: "flex", flexDirection: "column", padding: "18px 14px" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "6px 8px 14px" }}>
          <Logo />
          <button onClick={() => setCollapsed(true)} title="Replier le menu" style={{ background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 8, width: 30, height: 30, display: "grid", placeItems: "center", color: "var(--muted)", flex: "none" }}>
            {React.createElement(Icon.left, { size: 16 })}
          </button>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 7, margin: "0 8px 14px", padding: "7px 10px", borderRadius: 9, background: isOwner ? "var(--accent-soft)" : "var(--cyan-soft)", border: "1px solid " + (isOwner ? "var(--accent-line)" : "rgba(34,211,238,.3)") }}>
          {React.createElement(Icon[isOwner ? "shield" : "eye"], { size: 14, color: isOwner ? "var(--accent)" : "var(--accent-2)" })}
          <span className="mono" style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: ".04em", color: isOwner ? "var(--accent)" : "var(--accent-2)" }}>{isOwner ? "ACCÈS PROPRIÉTAIRE" : "ACCÈS SUPERVISEUR"}</span>
        </div>
        <nav style={{ display: "grid", gap: 3, flex: 1 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "8px 10px 6px" }}>
            <span className="mono" style={{ fontSize: 10, color: "var(--faint)", letterSpacing: ".12em" }}>PILOTAGE</span>
          </div>
          {nav.map(({ id, label, icon }) => (
            <button key={id} onClick={() => setPage(id)} style={navBtn(safePage === id)}>
              {React.createElement(Icon[icon], { size: 18, color: safePage === id ? "var(--accent)" : "var(--muted)" })}
              <span>{label}</span>
              {id === "tasks" && <span className="num" style={{ marginLeft: "auto", fontSize: 10.5, padding: "1px 7px", borderRadius: 99, background: "rgba(245,185,66,.16)", color: "var(--busy)" }}>1</span>}
              {id === "requests" && pendingCount > 0 && <span className="num" style={{ marginLeft: "auto", fontSize: 10.5, padding: "1px 7px", borderRadius: 99, background: "var(--danger)", color: "#fff", fontWeight: 700 }}>{pendingCount}</span>}
              {id === "messages" && unreadMsg > 0 && <span className="num" style={{ marginLeft: "auto", fontSize: 10.5, padding: "1px 7px", borderRadius: 99, background: "var(--accent)", color: "#06281c", fontWeight: 700 }}>{unreadMsg}</span>}
              {safePage === id && <span style={{ position: "absolute", left: 0, top: 8, bottom: 8, width: 3, borderRadius: 99, background: "var(--accent)" }} />}
            </button>
          ))}
        </nav>
        <div style={{ display: "grid", gap: 3, borderTop: "1px solid var(--border)", paddingTop: 12 }}>
          <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")} style={navBtn(false)}>
            {React.createElement(Icon[theme === "dark" ? "sun" : "moon"], { size: 18, color: "var(--muted)" })}
            <span>Thème {theme === "dark" ? "clair" : "sombre"}</span>
          </button>
          <button onClick={onSwitchRole} style={navBtn(false)}>
            {React.createElement(Icon.user, { size: 18, color: "var(--muted)" })}<span>Vue Opérateur</span>
          </button>
          <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 10px", marginTop: 4, borderRadius: 10, background: "var(--surface)" }}>
            <Avatar user={user} size={32} />
            <div style={{ lineHeight: 1.25, flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{user.name}</div>
              <div className="mono" style={{ fontSize: 10, color: "var(--faint)" }}>{user.role}</div>
            </div>
            <button onClick={onLogout} title="Déconnexion" style={{ background: "none", border: "none", color: "var(--faint)", display: "grid", padding: 4 }}>{React.createElement(Icon.logout, { size: 17 })}</button>
          </div>
          <VersionBadge />
        </div>
      </aside>}

      {/* contenu */}
      <main style={{ minWidth: 0, overflow: "auto" }}>
        {collapsed && (
          <button onClick={() => setCollapsed(false)} title="Afficher le menu" style={{ position: "sticky", top: 20, marginLeft: 18, marginTop: 6, zIndex: 25, display: "inline-flex", alignItems: "center", gap: 8, background: "var(--surface-2)", border: "1px solid var(--border-2)", borderRadius: 9, padding: "8px 12px", color: "var(--text)", fontSize: 13, fontWeight: 600, boxShadow: "var(--shadow)" }}>
            {React.createElement(Icon.switcher, { size: 16 })} Menu
          </button>
        )}
        <div key={safePage} className="app-fade" style={{ padding: safePage === "pilotage" ? (collapsed ? "52px 12px 30px" : "10px 12px 30px") : (collapsed ? "60px 34px 60px" : "30px 34px 60px"), maxWidth: safePage === "pilotage" ? "none" : 1500, margin: "0 auto" }}>
          {pages[safePage]}
        </div>
      </main>
      {watchOp && <WatchOverlay opId={watchOp} onClose={() => setWatchOp(null)} me={user} />}
    </div>
  );
}
// Badge de version : compare le code CHARGÉ vs le code sur DISQUE (mtime) -> dit clairement
// si le moteur/backend tourne du vieux code (à redémarrer) ou est à jour. Survol = détail des 3.
function VersionBadge() {
  const { useState, useEffect } = React;
  const [v, setV] = useState(null);
  useEffect(() => {
    let alive = true;
    const load = async () => { const d = await Backend.version(); if (alive) setV(d); };
    load();
    const t = setInterval(load, 30000);
    return () => { alive = false; clearInterval(t); };
  }, []);
  if (!v) return null;
  const eng = v.engine || {}, bk = v.backend || {}, web = v.webapp || {};
  const stale = eng.stale || bk.stale;
  const unreach = !!eng.error || !bk.version;
  const color = stale ? "#f87171" : (unreach ? "var(--faint)" : "#34d399");
  const label = stale ? "⚠ redémarre le moteur" : (unreach ? "moteur injoignable" : "à jour");
  const title = `moteur ${eng.version || "?"}${eng.stale ? " — CODE MODIFIÉ, à redémarrer" : ""}\nbackend ${bk.version || "?"}${bk.stale ? " — modifié" : ""}\nwebapp ${web.version || "?"}`;
  return (
    <div className="mono" title={title} style={{ fontSize: 9.5, color, padding: "6px 10px 2px", display: "flex", alignItems: "center", gap: 5, letterSpacing: ".02em" }}>
      <span style={{ width: 6, height: 6, borderRadius: 6, background: color, flexShrink: 0 }} />
      <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{label} · m{eng.version || "?"}</span>
    </div>
  );
}
function navBtn(active) {
  return {
    position: "relative", display: "flex", alignItems: "center", gap: 12, padding: "10px 12px", width: "100%",
    background: active ? "var(--surface)" : "transparent", border: "1px solid " + (active ? "var(--border)" : "transparent"),
    borderRadius: 10, color: active ? "var(--text)" : "var(--muted)", fontSize: 13.5, fontWeight: 600, textAlign: "left",
  };
}

// ---------- Overview ----------
function Overview({ onGo, onMessage, onWatch, isOwner }) {
  // KPI RÉELS : parc (backend), équipe inscrite, tâches du store
  const farm = useLiveDevices();
  const parc = (farm.online && farm.list) ? farm.list : [];
  const team = DATA.realTeam();
  const opsList = team.filter(o => o.role === "Opérateur");
  useTasks();   // charge/rafraîchit les tâches réelles (cloud)
  const tasks = TaskStore.list();
  const ovDays = useRealDays(14);   // série réelle 14 j (uptime + tâches)
  const tasksDone = tasks.filter(t => t.status === "done").length;
  const k = {
    devicesOnline: parc.filter(d => d.status === "online" || d.status === "busy").length,
    devicesTotal: parc.length,
    operatorsActive: opsList.filter(o => o.status === "active").length,
    operatorsTotal: opsList.length,
    tasksDone, tasksTotal: tasks.length,
    completion: tasks.length ? Math.round((tasksDone / tasks.length) * 100) : 0,
  };
  // supervision : seulement s'il y a un manager (réel si équipe inscrite)
  const manager = team.find(o => o.role === "Manager") || null;
  const managerIsReal = !!(manager && manager.registered);
  // à surveiller : RÉEL — opérateurs sans iPhone assigné ou suspendus (plus de fausses personnes)
  const watch = [];
  opsList.forEach(o => {
    if (o.status !== "active") watch.push({ who: o, why: "compte suspendu", tone: "red" });
    else if (farm.online && !parc.some(d => d.operator && d.operator.id === o.id)) watch.push({ who: o, why: "aucun iPhone assigné", tone: "amber" });
  });
  return (
    <div style={{ display: "grid", gap: 22 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", flexWrap: "wrap", gap: 14 }}>
        <div>
          <h1 style={{ margin: 0, fontSize: 24, fontWeight: 700, letterSpacing: "-.01em" }}>Overview</h1>
          <p style={{ margin: "6px 0 0", color: "var(--muted)", fontSize: 13.5 }}>{new Date().toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long", year: "numeric" })} · {farm.online ? "● parc synchronisé" : "parc non synchronisé"} · {isOwner ? "vue d'ensemble de l'agence" : "supervision de l'équipe opérateurs"}</p>
        </div>
        <div style={{ display: "flex", gap: 10 }}>
          <Button variant="outline" icon="download">Export CSV</Button>
          <Button variant="primary" icon="plus" onClick={() => onGo("team")}>Inviter un membre</Button>
        </div>
      </div>

      {/* KPI */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 16 }} className="kpi-grid">
        <KPI label="Devices en ligne" value={k.devicesOnline} sub={`/ ${k.devicesTotal} total`} icon="phone" tone="green" spark="online" />
        <KPI label="Opérateurs actifs" value={k.operatorsActive} sub={`/ ${k.operatorsTotal} aujourd'hui`} icon="users" tone="cyan" spark="ops" />
        <KPI label="Tâches faites" value={k.tasksDone} sub={`/ ${k.tasksTotal} du jour`} icon="check" tone="green" spark="tasks" />
        <KPI label="Taux de complétion" value={k.completion + "%"} sub="objectif 80%" icon="bolt" tone={k.completion >= 80 ? "green" : "amber"} ring={k.completion} />
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.6fr 1fr", gap: 16 }} className="ov-grid">
        {/* graphe usage */}
        <Card pad={20}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 18 }}>
            <div>
              <div style={{ fontSize: 14.5, fontWeight: 700 }}>Activité par jour</div>
              <div className="mono" style={{ fontSize: 11, color: "var(--faint)", marginTop: 3 }}>minutes connectées (parc) + tâches faites · 14 jours · réel</div>
            </div>
            <div style={{ display: "flex", gap: 14, fontSize: 11.5 }}>
              <Legend c="var(--accent)" label="minutes" />
              <Legend c="var(--accent-2)" label="tâches" />
            </div>
          </div>
          <BarChart data={ovDays} keys={["active", "tasksDone"]} colors={["var(--accent)", "var(--accent-2)"]} h={186} />
          <div style={{ display: "flex", justifyContent: "space-between", marginTop: 8 }}>
            {ovDays.filter((_, i) => i % 3 === 0).map((d, i) => <span key={i} className="mono" style={{ fontSize: 9.5, color: "var(--faint)" }}>{d.label}</span>)}
          </div>
        </Card>

        {/* à surveiller */}
        <Card pad={20}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 16 }}>
            <span style={{ width: 7, height: 7, borderRadius: 99, background: "var(--busy)" }} />
            <div style={{ fontSize: 14.5, fontWeight: 700 }}>À surveiller</div>
          </div>
          <div style={{ display: "grid", gap: 11 }}>
            {watch.length === 0 && <div style={{ color: "var(--faint)", fontSize: 12.5, padding: "16px 0", textAlign: "center" }}>Rien à signaler — tous les opérateurs sont actifs et équipés. ✓</div>}
            {watch.map((w, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 11, padding: "10px 12px", borderRadius: 11, background: "var(--surface-2)", border: "1px solid var(--border)" }}>
                <Avatar user={w.who} size={32} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{w.who.name}</div>
                  <div style={{ fontSize: 11.5, color: w.tone === "red" ? "var(--danger)" : "var(--busy)" }}>{w.why}</div>
                </div>
                <div style={{ display: "flex", gap: 7 }}>
                  <button onClick={() => onWatch && onWatch(w.who.id)} title="Voir son écran" style={{ display: "grid", placeItems: "center", width: 32, height: 32, borderRadius: 9, background: "var(--cyan-soft)", color: "var(--accent-2)", border: "1px solid rgba(34,211,238,.3)" }}>{React.createElement(Icon.eye, { size: 15 })}</button>
                  <button onClick={() => onMessage && onMessage(w.who.id)} title="Message in-app" style={{ display: "grid", placeItems: "center", width: 32, height: 32, borderRadius: 9, background: "var(--accent-soft)", color: "var(--accent)", border: "1px solid var(--accent-line)" }}>{React.createElement(Icon.send, { size: 15 })}</button>
                  <WhatsAppBtn user={w.who} variant="icon" />
                  <TelegramBtn user={w.who} variant="icon" />
                </div>
              </div>
            ))}
          </div>
        </Card>
      </div>

      {/* Supervision du manager — OWNER UNIQUEMENT (masquée s'il n'y a pas de manager) */}
      {isOwner && manager && (
        <Card pad={20}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: 12, marginBottom: 16 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              {React.createElement(Icon.eye, { size: 17, color: "var(--accent-2)" })}
              <div>
                <div style={{ fontSize: 14.5, fontWeight: 700 }}>Supervision du manager</div>
                <div className="mono" style={{ fontSize: 11, color: "var(--faint)", marginTop: 2 }}>activité récente · visible par vous seul (propriétaire)</div>
              </div>
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 11, padding: "7px 12px 7px 8px", borderRadius: 99, background: "var(--surface-2)", border: "1px solid var(--border)" }}>
              <Avatar user={manager} size={30} />
              <div style={{ lineHeight: 1.2 }}>
                <div style={{ fontSize: 12.5, fontWeight: 600 }}>{manager.name}</div>
                <div style={{ fontSize: 10.5, color: "var(--accent)", display: "flex", alignItems: "center", gap: 5 }}><StatusDot status="online" pulse /> en ligne</div>
              </div>
              <button onClick={() => onMessage && onMessage(manager.id)} title="Message" style={{ display: "grid", placeItems: "center", width: 30, height: 30, borderRadius: 8, background: "var(--accent-soft)", color: "var(--accent)", border: "1px solid var(--accent-line)" }}>{React.createElement(Icon.send, { size: 14 })}</button>
              <WhatsAppBtn user={manager} variant="icon" />
              <TelegramBtn user={manager} variant="icon" />
            </div>
          </div>
          <div style={{ display: "grid", gap: 0 }}>
            {managerIsReal && <div style={{ color: "var(--faint)", fontSize: 12.5, padding: "12px 4px" }}>Aucune activité enregistrée pour l'instant — le journal se remplira avec les actions réelles du manager.</div>}
            {!managerIsReal && DATA.MANAGER_LOG.map((l, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 4px", borderTop: i ? "1px solid var(--border)" : "none" }}>
                <span style={{ width: 6, height: 6, borderRadius: 99, background: "var(--accent-2)", flex: "none" }} />
                <span style={{ fontSize: 13, fontWeight: 600, flex: 1 }}>{l.action}</span>
                <span className="mono" style={{ fontSize: 11, color: "var(--faint)" }}>{l.meta}</span>
                <span className="mono" style={{ fontSize: 11, color: "var(--muted)", minWidth: 110, textAlign: "right" }}>{l.at}</span>
              </div>
            ))}
          </div>
        </Card>
      )}
    </div>
  );
}

function KPI({ label, value, sub, icon, tone, ring }) {
  const c = { green: "var(--accent)", cyan: "var(--accent-2)", amber: "var(--busy)" }[tone];
  return (
    <Card pad={18}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
        <div style={{ width: 38, height: 38, borderRadius: 11, background: "color-mix(in srgb, " + c + " 14%, transparent)", display: "grid", placeItems: "center", color: c }}>
          {React.createElement(Icon[icon], { size: 19 })}
        </div>
        {ring != null
          ? <Ring pct={ring} color={c} />
          : <span className="mono" style={{ fontSize: 11, color: "var(--accent)" }}>▲ +{4 + (label.length % 6)}%</span>}
      </div>
      <div className="num" style={{ fontSize: 30, fontWeight: 700, marginTop: 14, letterSpacing: "-.02em" }}>{value}</div>
      <div style={{ fontSize: 12.5, color: "var(--text)", fontWeight: 600, marginTop: 2 }}>{label}</div>
      <div className="mono" style={{ fontSize: 11, color: "var(--faint)", marginTop: 2 }}>{sub}</div>
    </Card>
  );
}
function Ring({ pct, color }) {
  const r = 16, c = 2 * Math.PI * r;
  return (
    <svg width="42" height="42" viewBox="0 0 42 42">
      <circle cx="21" cy="21" r={r} fill="none" stroke="var(--surface-3)" strokeWidth="4" />
      <circle cx="21" cy="21" r={r} fill="none" stroke={color} strokeWidth="4" strokeLinecap="round"
        strokeDasharray={c} strokeDashoffset={c * (1 - pct / 100)} transform="rotate(-90 21 21)" />
    </svg>
  );
}
function Legend({ c, label }) {
  return <span style={{ display: "inline-flex", alignItems: "center", gap: 6, color: "var(--muted)" }}><span style={{ width: 9, height: 9, borderRadius: 3, background: c }} />{label}</span>;
}

// Page ONBOARDING : vue brute du MOTEUR — tous les iPhones détectés (prêts OU à équiper).
// Permet d'installer WDA sur un nouveau tel pour le rendre pilotable. Auto-refresh.
function EngineOnboard() {
  const { useState, useEffect } = React;
  const [devs, setDevs] = useState([]);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState("");
  const [msg, setMsg] = useState(null);
  const [signing, setSigning] = useState(null);
  const [assign, setAssign] = useState({ default: "internal", devices: {} });
  const [web, setWeb] = useState(null);   // état de l'accès web (tunnel VA)
  const load = async () => { const d = await window.Backend.engineDevices(); setDevs(Array.isArray(d) ? d : []); setLoading(false); };
  const loadAssign = async () => { const a = await window.Backend.engineAssignment(); if (a) setAssign(a); };
  const setDefaultEngine = async (v) => { setAssign(p => ({ ...p, default: v })); await window.Backend.setEngine({ default: v }); setTimeout(load, 400); };
  const setDevEngine = async (udid, v) => { setAssign(p => { const dv = { ...p.devices }; if (v === "inherit") delete dv[udid]; else dv[udid] = v; return { ...p, devices: dv }; }); await window.Backend.setEngine({ udid, engine: v }); setTimeout(load, 600); };
  const startWeb = async () => { setWeb(w => ({ ...(w || {}), starting: true })); await window.Backend.webSyncStart(); setTimeout(() => window.Backend.webSync().then(setWeb), 4000); };
  // Nom (label) + ordre des iPhones = MÊME store cloud que le Parc (devicesMeta/patchMeta)
  // -> un seul nom par tel, cohérent partout. L'app Electron a le token (login Google) -> patchMeta OK.
  const [meta, setMeta] = useState({});   // {udid: {label, ord, ...}}
  const onMetaName = (udid, v) => setMeta(m => ({ ...m, [udid]: { ...(m[udid] || {}), label: v } }));
  const onMetaOrd = (udid, v) => setMeta(m => ({ ...m, [udid]: { ...(m[udid] || {}), ord: v === "" ? null : Number(v) } }));
  const saveMeta = (udid, patch) => { window.Backend.patchMeta(udid, patch); };   // -> cloud (sur blur)
  // tri par n° d'ordre (#1..#N), puis par UDID pour les non-numérotés
  const ordered = [...devs].sort((a, b) => {
    const oa = meta[a.udid] && meta[a.udid].ord, ob = meta[b.udid] && meta[b.udid].ord;
    if (oa != null && ob != null) return oa - ob;
    if (oa != null) return -1; if (ob != null) return 1;
    return String(a.udid).localeCompare(String(b.udid));
  });
  useEffect(() => { load(); loadAssign(); window.Backend.signingStatus().then(setSigning); window.Backend.webSync().then(setWeb); window.Backend.devicesMeta().then(m => setMeta(m || {})); const id = setInterval(() => { load(); loadAssign(); window.Backend.webSync().then(setWeb); }, 4000); return () => clearInterval(id); }, []);
  const onboard = async (udid, name) => {
    setBusy(udid); setMsg({ ok: null, t: (signing && signing.ready) ? "Signature + installation… (~10-40 s)" : "Installation…" });
    // auto-signature si configurée (register Apple + signe + installe), sinon install brute
    const r = (signing && signing.ready) ? await window.Backend.onboard(udid, name) : await window.Backend.installWda(udid);
    setBusy("");
    const inner = r && (r.r || r.result || r);
    const ok = !!(r && !r.error && (inner.ok === true || inner.signed || r.ok === true));
    setMsg({ ok, t: ok
      ? "✓ Onboardé — sur le tel : Réglages > Général > VPN et gestion de l'appareil > Se fier au développeur. Puis il devient pilotable."
      : ("Échec : " + ((r && r.error) || (inner && inner.stderr) || "voir logs moteur")) });
    setTimeout(load, 2000);
  };
  const restart = async (udid) => {
    setMsg(null);
    const r = await window.Backend.restartEngine(udid);
    setMsg({ ok: !!(r && r.ok), t: (r && r.ok)
      ? "✓ Moteur relancé — le tel redevient pilotable dans quelques secondes (inutile de débrancher)."
      : ("Relance refusée : " + ((r && (r.error || r.reason)) || "inconnue")) });
    setTimeout(load, 6000);
  };
  const statusOf = (d) => (d.engine === "hcbox" || d.released) ? { c: "#22d3ee", t: "🔵 Sur HC BOX (notre moteur l'a lâché)" }
    : d.unstable ? { c: "var(--danger, #ff6b6b)", t: "⏸ En pause (WDA instable — rebranche-le)" }
    : d.ready ? { c: "var(--accent)", t: "● Prêt — pilotable" }
    : d.needs_wda ? { c: "var(--busy, #f5b942)", t: "WDA à installer" }
    : { c: "#22d3ee", t: "● démarrage…" };
  const card = { background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 14, padding: 16, display: "flex", alignItems: "center", gap: 14 };
  return (
    <div style={{ padding: "26px 30px", maxWidth: 1000, margin: "0 auto" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 6 }}>
        <h1 style={{ fontSize: 22, fontWeight: 800, margin: 0 }}>Onboarding</h1>
        <span style={{ fontSize: 12, color: "var(--faint)" }}>{devs.length} iPhone{devs.length > 1 ? "s" : ""} vu{devs.length > 1 ? "s" : ""} par le moteur</span>
        {/* ACCÈS WEB (VA) : lance le tunnel -> publie l'URL -> la page hébergée des VA se synchronise. */}
        {web && web.running
          ? <div title={web.url || ""} style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 8, background: "var(--accent-soft)", border: "1px solid var(--accent-line)", borderRadius: 9, padding: "7px 12px" }}>
              <span style={{ fontSize: 13, fontWeight: 700, color: "var(--accent)" }}>🌐 Accès web actif — VA synchronisés</span>
              {web.url && <span style={{ fontSize: 11, color: "var(--muted)", maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{web.url.replace(/^https?:\/\//, "")}</span>}
            </div>
          : <button onClick={startWeb} disabled={!!(web && web.starting)} title="Lance le tunnel Cloudflare + publie l'URL dans le cloud -> la page des VA se synchronise toute seule (laisse l'app ouverte)"
              style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 7, background: (web && web.starting) ? "var(--surface-2)" : "var(--accent)", color: (web && web.starting) ? "var(--muted)" : "#04130c", border: "none", borderRadius: 9, padding: "8px 14px", fontSize: 13, fontWeight: 700, cursor: (web && web.starting) ? "default" : "pointer" }}>
              {(web && web.starting) ? "⏳ Démarrage du tunnel…" : "🌐 Activer l'accès web (VA)"}
            </button>}
        <button onClick={() => { load(); window.Backend.signingStatus().then(setSigning); }} title="Vérifier l'état maintenant"
          style={{ display: "inline-flex", alignItems: "center", gap: 6, background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 9, padding: "7px 13px", color: "var(--text)", fontSize: 13, fontWeight: 600, cursor: "pointer" }}>
          ↻ Rafraîchir
        </button>
      </div>
      <p style={{ color: "var(--muted)", fontSize: 13.5, marginTop: 0, marginBottom: 14 }}>Branche un iPhone (déverrouillé). S'il n'a pas WebDriverAgent, clique <b>Onboarder</b> : on l'enregistre chez Apple, on signe WDA pour CE tel et on l'installe — automatiquement.</p>
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, padding: "11px 14px", borderRadius: 12, background: "var(--surface)", border: "1px solid var(--border)", flexWrap: "wrap" }}>
        <span style={{ fontSize: 13, fontWeight: 700 }}>⚙ Moteur du parc par défaut :</span>
        {["internal", "hcbox"].map(v => (
          <button key={v} onClick={() => setDefaultEngine(v)} style={{
            background: assign.default === v ? (v === "hcbox" ? "#22d3ee" : "var(--accent)") : "var(--surface-2)",
            color: assign.default === v ? "#04130c" : "var(--text)", border: "1px solid var(--border)", borderRadius: 9,
            padding: "7px 15px", fontWeight: 700, fontSize: 13, cursor: "pointer" }}>
            {v === "hcbox" ? "HC BOX" : "Moteur interne"}
          </button>
        ))}
        <span style={{ fontSize: 11.5, color: "var(--faint)", flexBasis: "100%" }}>Les tels « HC BOX » sont <b>lâchés</b> par notre moteur (pilote-les dans l'app HC BOX) — pas de conflit. Override par tel ci-dessous.</span>
      </div>
      {signing && (
        <div style={{ marginBottom: 18, padding: "9px 13px", borderRadius: 10, fontSize: 12.5, display: "flex", alignItems: "center", gap: 8,
          background: signing.ready ? "var(--accent-soft)" : "rgba(245,185,66,.12)",
          color: signing.ready ? "var(--accent)" : "var(--busy, #f5b942)",
          border: "1px solid " + (signing.ready ? "var(--accent-line)" : "rgba(245,185,66,.3)") }}>
          {signing.ready ? "✓ Auto-signature prête (ton certif Apple) — onboarding en 1 clic"
            : "⚠ Auto-signature non configurée (signing.json) — installation brute seulement" + (signing.reason ? " · " + signing.reason : "")}
        </div>
      )}
      {msg && <div style={{ marginBottom: 16, padding: "10px 14px", borderRadius: 10, fontSize: 13, background: msg.ok ? "var(--accent-soft)" : "rgba(255,107,107,.12)", color: msg.ok ? "var(--accent)" : "#ff6b6b", border: "1px solid " + (msg.ok ? "var(--accent-line)" : "rgba(255,107,107,.3)") }}>{msg.t}</div>}
      {loading ? <div className="mono" style={{ color: "var(--faint)" }}>chargement…</div>
        : !devs.length ? (
          <div style={{ ...card, justifyContent: "center", flexDirection: "column", padding: "48px 20px", textAlign: "center" }}>
            <div style={{ fontSize: 34 }}>🔌</div>
            <div style={{ fontWeight: 700, marginTop: 10 }}>Aucun iPhone détecté par le moteur</div>
            <div style={{ color: "var(--muted)", fontSize: 13, marginTop: 6, maxWidth: 440 }}>Branche + déverrouille un iPhone (et accepte « Se fier à cet ordinateur »). Il apparaîtra ici automatiquement.</div>
          </div>
        ) : (
          <div style={{ display: "grid", gap: 12 }}>
            {ordered.map(d => { const s = statusOf(d); const md = meta[d.udid] || {}; return (
              <div key={d.udid} style={card}>
                {/* n° d'ordre (#1..#N) — éditable, sert au TRI des iPhones */}
                <input type="number" min="1" value={md.ord == null ? "" : md.ord} placeholder="#"
                  onChange={e => onMetaOrd(d.udid, e.target.value)} onBlur={e => saveMeta(d.udid, { ord: e.target.value === "" ? null : Number(e.target.value) })}
                  title="N° d'ordre (#1..#N) — pour trier les iPhones"
                  style={{ width: 48, flex: "none", textAlign: "center", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 8, padding: "9px 4px", fontSize: 14, fontWeight: 800 }} />
                <div style={{ fontSize: 26 }}>📱</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  {/* nom éditable (ex: iPhone 15) — au-dessus de l'UDID */}
                  <input value={md.label || ""} placeholder="iPhone…"
                    onChange={e => onMetaName(d.udid, e.target.value)} onBlur={e => saveMeta(d.udid, { label: e.target.value })}
                    title="Renomme cet iPhone"
                    style={{ fontWeight: 700, fontSize: 15, background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", width: "100%", maxWidth: 240 }} />
                  <div className="mono" style={{ fontSize: 11, color: "var(--faint)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 3, paddingLeft: 2 }}>{d.udid}{d.ios ? " · iOS " + d.ios : ""}</div>
                </div>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: s.c, flex: "none" }}>{s.t}</div>
                <select value={assign.devices[d.udid] || "inherit"} onChange={e => setDevEngine(d.udid, e.target.value)} title="Moteur de CE iPhone"
                  style={{ flex: "none", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 8, padding: "6px 8px", fontSize: 12, cursor: "pointer" }}>
                  <option value="inherit">Hérite ({assign.default === "hcbox" ? "HC BOX" : "interne"})</option>
                  <option value="internal">Interne</option>
                  <option value="hcbox">HC BOX</option>
                </select>
                {/* tel en pause (WDA instable) sur le moteur interne -> relance en 1 clic (plus besoin de débrancher) */}
                {d.unstable && d.engine !== "hcbox" && !d.released && (
                  <button onClick={() => restart(d.udid)} title="Redémarre WebDriverAgent sur ce tel"
                    style={{ flex: "none", background: "rgba(245,185,66,.15)", color: "var(--busy, #f5b942)", border: "1px solid rgba(245,185,66,.4)", borderRadius: 10, padding: "10px 16px", fontWeight: 700, fontSize: 13.5, cursor: "pointer" }}>
                    ↻ Relancer le moteur
                  </button>
                )}
                {d.needs_wda && (
                  <button onClick={() => onboard(d.udid, "iPhone " + d.udid.slice(-6))} disabled={!!busy}
                    style={{ flex: "none", background: "var(--accent)", color: "#04130c", border: "none", borderRadius: 10, padding: "10px 16px", fontWeight: 700, fontSize: 13.5, cursor: busy ? "default" : "pointer", opacity: busy ? 0.6 : 1 }}>
                    {busy === d.udid ? "⏳ Onboarding…" : "💉 Onboarder"}
                  </button>
                )}
              </div>
            ); })}
          </div>
        )}
    </div>
  );
}

Object.assign(window, { AdminShell, Overview, KPI, Ring, Legend, navBtn, EngineOnboard });
