// ============================================================
// Comptes par parc — vue Stockage comptes découpée par TYPE de téléphone
// ============================================================
// Trois branches : iPhone pirate / Phone Cloud / iPhone physique. Chacune a son total.
// La branche pirate est la plus riche : un entonnoir horizontal par iPhone (conteneurs créés →
// compte lié → plug validé), la répartition par catégorie, puis la liste détaillée des
// conteneurs avec l'auteur de la création et l'auteur du plug.
//
// ⚠️ `created_by` / `plug_by` n'ont été instrumentés que récemment : les conteneurs plus anciens
// affichent « — ». C'est voulu, on n'invente pas un auteur qu'on n'a jamais enregistré.

// device_view.jsx détient la clé ; on la relit sans coupler les deux fichiers
const CONTAINERS_KEY_RO = "phonelabs.containers";

const CP_KINDS = [
  { k: "pirate",   emoji: "🏴‍☠️", label: "iPhone pirate" },
  { k: "cloud",    emoji: "☁️",  label: "Phone Cloud" },
  { k: "physique", emoji: "📱", label: "iPhone physique" },
];

function cpIsPlug(v) {
  if (v === true) return true;
  return ["plug", "true", "1", "oui", "yes"].indexOf(String(v == null ? "" : v).trim().toLowerCase()) >= 0;
}
// Étapes d'entonnoir d'une FEUILLE (pirate/cloud/physique) : les catégories retenues, dans
// l'ordre PROPRE à cette feuille (FunnelSteps). Pas de config -> toutes, ordre global.
function cpFunnelCats(sheet) {
  const all = (window.loadCategories ? window.loadCategories() : []) || [];
  const byLabel = {}; all.forEach(c => { if (c && c.label) byLabel[c.label] = c; });
  const labels = window.FunnelSteps ? window.FunnelSteps.stepsOf(sheet, all.map(c => c.label)) : all.map(c => c.label);
  return labels.map(l => byLabel[l]).filter(Boolean);
}
function cpReadMap(key) { try { return JSON.parse(localStorage.getItem(key) || "{}") || {}; } catch (e) { return {}; } }

// Un compte appartient à une branche selon le téléphone auquel il est attribué.
function cpAccountKind(acc, pirateSet) {
  const u = String(acc.attributed_udid || acc.device_udid || "");
  if (!u) return null;                                   // pas attribué : ne compte dans aucune branche
  if (/^\d{13,}$/.test(u)) return "cloud";
  return pirateSet.has(u) ? "pirate" : "physique";
}

function ComptesParc() {
  const { useState, useEffect } = React;
  const store = window.useAccountStore ? window.useAccountStore() : null;
  const accounts = store ? store.list() : [];
  const [kind, setKind] = useState("pirate");
  const [devices, setDevices] = useState([]);
  const [cloudDevs, setCloudDevs] = useState([]);        // profils cloud (MoreLogin) — pour compter le parc cloud
  const [pick, setPick] = useState("all");               // "all" ou un udid
  const [, force] = React.useReducer(x => x + 1, 0);
  const [q, setQ] = useState("");

  useEffect(() => {
    let on = true;
    const load = () => {
      if (window.Backend && window.Backend.devices) window.Backend.devices().then(l => { if (on) setDevices(l || []); }).catch(() => {});
      if (window.Backend && window.Backend.cloudphones) window.Backend.cloudphones().then(l => { if (on) setCloudDevs(l || []); }).catch(() => {});
    };
    load();
    const iv = setInterval(load, 15000);
    const r = () => force();
    window.addEventListener("pl-containers", r);
    return () => { on = false; clearInterval(iv); window.removeEventListener("pl-containers", r); };
  }, []);

  const pirateDevs = devices.filter(d => d.pirate);
  const pirateSet = new Set(pirateDevs.map(d => String(d.udid)));
  const allContainers = cpReadMap(CONTAINERS_KEY_RO);

  // ---- totaux par branche ----
  // comptes attribués (sert au tooltip)
  const totals = {};
  CP_KINDS.forEach(k => { totals[k.k] = 0; });
  accounts.forEach(a => { const k = cpAccountKind(a, pirateSet); if (k) totals[k]++; });
  // nombre de TÉLÉPHONES par parc = le chiffre du badge (un iPhone pirate porte plusieurs conteneurs,
  // « comptes attribués » induisait en erreur : badge « 10 » pour 1 seul tél).
  const deviceTotals = {
    pirate: pirateDevs.length,
    cloud: cloudDevs.length,
    physique: devices.filter(d => !d.pirate && !/^\d{13,}$/.test(String(d.udid))).length,
  };

  const tabBtn = (on) => ({ display: "inline-flex", alignItems: "center", gap: 7, padding: "8px 14px", borderRadius: 10,
    border: "1px solid " + (on ? "var(--accent-line)" : "var(--border)"), background: on ? "var(--accent-soft)" : "var(--surface-2)",
    color: on ? "var(--accent)" : "var(--muted)", fontSize: 13, fontWeight: 700, cursor: "pointer" });

  return (
    <div style={{ padding: "26px 22px 40px", maxWidth: 1180, margin: "0 auto" }}>
      <PageHead title="Comptes par parc" icon="list"
        subtitle="Les comptes du Stockage, répartis par type de téléphone — et le détail des conteneurs pour les iPhones pirates." />

      <div style={{ display: "flex", flexWrap: "wrap", gap: 9, margin: "18px 0 20px" }}>
        {CP_KINDS.map(k => (
          <button key={k.k} onClick={() => { setKind(k.k); setPick("all"); }} style={tabBtn(kind === k.k)}>
            <span style={{ fontSize: 15 }}>{k.emoji}</span>{k.label}
            <span title={deviceTotals[k.k] + " téléphone" + (deviceTotals[k.k] > 1 ? "s" : "") + " · " + totals[k.k] + " compte" + (totals[k.k] > 1 ? "s" : "") + " attribué" + (totals[k.k] > 1 ? "s" : "")}
              style={{ fontSize: 12, opacity: .8, fontWeight: 800 }}>{deviceTotals[k.k]}</span>
          </button>
        ))}
      </div>

      {kind === "pirate"
        ? <CpPirate devices={pirateDevs} containers={allContainers} accounts={accounts} pick={pick} setPick={setPick} q={q} setQ={setQ} />
        : <CpSimple kind={kind} accounts={accounts.filter(a => cpAccountKind(a, pirateSet) === kind)} q={q} setQ={setQ} />}
    </div>
  );
}

// ---- Entonnoir HORIZONTAL ----
// Silhouette continue qui se rétrécit de gauche à droite, remplie d'un SEUL dégradé étalé sur
// toute la largeur (gradientUnits="userSpaceOnUse") : sans ça chaque section repartirait à zéro
// et on verrait 3 dégradés collés au lieu d'un seul mouvement.
// Derrière, en gris, le volume de DÉPART : l'écart entre le fantôme et la forme EST la perte.
// Les sections sont cliquables -> elles filtrent le tableau des conteneurs en dessous.
const CP_INK = ["#04241b", "#eafff6", "#eafff6"];
function CpFunnel({ steps, active, onPick }) {
  const n = steps.length;
  // largeur totale élastique : au-delà de 4 étapes on s'étale au lieu d'écraser chaque section
  const PER = 176, X0 = 24, W = Math.max(712, n * PER), X1 = X0 + W;
  const CY = 128, MAXH = 92, MINH = 7, GAP = 4, R = 14;
  const w = (X1 - X0) / n;
  const FS = n > 6 ? 20 : n > 4 ? 24 : 30;      // le chiffre doit tenir dans sa section
  const LS = n > 6 ? 11 : 13;
  const top = Math.max(1, ...steps.map(s => s.n));
  // une étape à 0 a une hauteur nulle : sinon la hauteur mini dessinait une barre fine
  const hs = steps.map(s => s.n > 0 ? Math.max(MINH, (s.n / top) * MAXH) : 0);
  const seg = (xa, xb, hA, hB) => {
    const fe = xa + (xb - xa) * 0.34, c = fe + (xb - fe) * 0.5;
    return `M${xa},${CY - hA}L${fe},${CY - hA}`
      + `C${c},${CY - hA} ${c},${CY - hB} ${xb},${CY - hB}`
      + `L${xb},${CY + hB}`
      + `C${c},${CY + hB} ${c},${CY + hA} ${fe},${CY + hA}`
      + `L${xa},${CY + hA}Z`;
  };
  // l'animation se rejoue à chaque changement de filtre grâce à la clé du <svg>
  const runKey = steps.map(s => s.n).join("-") + ":" + (active || "");
  return (
    <div style={{ overflowX: "auto" }}>
      <svg key={runKey} viewBox={"0 0 " + (X1 + 24) + " 320"} width="100%" style={{ minWidth: Math.min(X1 + 24, 1100), display: "block" }}
        role="img" aria-label="Entonnoir des conteneurs">
        <defs>
          <linearGradient id="cpFunnelGrad" gradientUnits="userSpaceOnUse" x1={X0} y1="0" x2={X1} y2="0">
            <stop offset="0%" stopColor="#8ef0c4" />
            <stop offset="52%" stopColor="#00e08a" />
            <stop offset="100%" stopColor="#0b6b53" />
          </linearGradient>
          {/* déroulé de gauche à droite au chargement */}
          <clipPath id="cpFunnelWipe">
            <rect x={X0} y="0" height="320" width="0">
              <animate attributeName="width" from="0" to={X1 - X0} dur="0.75s" fill="freeze" calcMode="spline"
                keySplines="0.25 0.8 0.35 1" keyTimes="0;1" values={"0;" + (X1 - X0)} />
            </rect>
          </clipPath>
        </defs>

        <rect x={X0} y={CY - hs[0]} width={X1 - X0} height={hs[0] * 2} rx={R} fill="var(--surface-2)" />

        <g clipPath="url(#cpFunnelWipe)">
          {steps.map((s, i) => {
            const xa = X0 + i * w + (i ? GAP / 2 : 0);
            const xb = X0 + (i + 1) * w - (i < n - 1 ? GAP / 2 : 0);
            const hA = hs[i], hB = i < n - 1 ? hs[i + 1] : hs[i];
            const dim = active && active !== s.key;
            if (s.n <= 0) return null;      // étape vide : on ne dessine rien du tout
            return (
              <path key={s.key} d={seg(xa, xb, hA, hB)} fill="url(#cpFunnelGrad)"
                onClick={() => onPick && onPick(active === s.key ? null : s.key)}
                style={{ cursor: onPick ? "pointer" : "default", opacity: dim ? 0.28 : 1, transition: "opacity .2s" }}>
                <title>{s.label} — {s.n}. Cliquer pour filtrer.</title>
              </path>
            );
          })}
        </g>

        {steps.map((s, i) => {
          const xa = X0 + i * w + (i ? GAP / 2 : 0);
          const xb = X0 + (i + 1) * w - (i < n - 1 ? GAP / 2 : 0);
          const mid = xa + (xb - xa) * 0.17;
          const dim = active && active !== s.key;
          const nxt = i < n - 1 ? steps[i + 1].n : null;
          const rate = (nxt != null && s.n > 0) ? Math.round((nxt / s.n) * 100) : null;
          const lost = nxt != null ? s.n - nxt : 0;
          const low = rate != null && rate < 50;
          return (
            <g key={s.key + "-t"} style={{ opacity: dim ? 0.35 : 1, transition: "opacity .2s", pointerEvents: "none" }}>
              <text x={mid} y={CY + (s.n > 0 ? FS / 3 : 4)} textAnchor="middle" fontSize={s.n > 0 ? FS : 13} fontWeight={s.n > 0 ? "800" : "400"}
                fill={s.n === 0 ? "var(--faint)" : (hs[i] > 26 && i < 2 ? CP_INK[0] : "#eafff6")}>{s.n}</text>
              <line x1={mid} y1={CY + hs[0] + 12} x2={mid} y2={CY + hs[0] + 26} stroke="var(--border)" strokeWidth="1" />
              <text x={mid} y={CY + hs[0] + 44} textAnchor="middle" fontSize={LS}
                fill={active === s.key ? "var(--accent,#00e08a)" : (s.n === 0 ? "var(--faint)" : "var(--muted)")}
                fontWeight={active === s.key ? "700" : "400"}>{s.label}</text>
              {rate != null && <>
                <circle cx={xb} cy={CY - hs[0] - 26} r="15" fill={low ? "rgba(240,160,32,.18)" : "rgba(0,224,138,.15)"} />
                <text x={xb} y={CY - hs[0] - 21} textAnchor="middle" fontSize="12" fontWeight="700"
                  fill={low ? "#f0a020" : "var(--accent,#00e08a)"}>{rate}%</text>
                {lost > 0 && <text x={xb} y={CY - hs[0] - 4} textAnchor="middle" fontSize="10.5" fill="var(--faint)">{"\u2212" + lost}</text>}
              </>}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

// Quelles catégories comptent dans l'entonnoir. On écrit le drapeau SUR la catégorie elle-même :
// elle voyage alors avec le reste vers le cloud, sans nouveau mécanisme de synchro à maintenir.
// L'ORDRE, lui, reste celui du Stockage comptes (glisser-déposer) — un seul ordre, pas deux.
// Éditeur des étapes de l'entonnoir POUR UNE FEUILLE. Chaque étape a une croix pour la retirer
// (de cette feuille seulement), se glisse pour se ranger, et un « + » propose les catégories pas
// encore dans l'entonnoir. Tout est propre à `sheet` — l'ordre global du Stockage n'est pas touché.
function CpCatPicker({ sheet }) {
  const [, force] = React.useReducer(x => x + 1, 0);
  const drag = React.useRef(null);
  const [over, setOver] = React.useState(null);
  const [adding, setAdding] = React.useState(false);
  React.useEffect(() => {
    const r = () => force();
    window.addEventListener("pl-categories", r);
    const u = (window.FunnelSteps && window.FunnelSteps.sub) ? window.FunnelSteps.sub(r) : null;
    return () => { window.removeEventListener("pl-categories", r); if (u) u(); };
  }, []);
  const all = (window.loadCategories ? window.loadCategories() : []) || [];
  const F = window.FunnelSteps;
  if (!all.length || !F) return null;

  const byLabel = {}; all.forEach(c => { byLabel[c.label] = c; });
  const allLabels = all.map(c => c.label);
  const steps = F.stepsOf(sheet, allLabels);                 // labels retenus, dans l'ordre de la feuille
  const available = allLabels.filter(l => steps.indexOf(l) < 0);

  const removeStep = (label) => {
    if (steps.length <= 1) return;                            // au moins une étape doit rester
    F.set(sheet, steps.filter(l => l !== label)); force();
  };
  const addStep = (label) => { F.set(sheet, [...steps, label]); setAdding(false); force(); };
  const moveTo = (to) => {
    const from = drag.current;
    if (from == null || from === to) { setOver(null); return; }
    const next = [...steps];
    const [m] = next.splice(from, 1);
    next.splice(to, 0, m);
    F.set(sheet, next); force();
    drag.current = null; setOver(null);
  };

  const pill = (label, extra) => {
    const c = byLabel[label] || { label, emoji: "" };
    return (c.emoji ? c.emoji + " " : "") + c.label;
  };

  return (
    <div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 6, marginTop: 14, paddingTop: 13, borderTop: "1px solid var(--border)" }}>
      <span style={{ fontSize: 11, color: "var(--faint)", marginRight: 3 }}>Étapes de l'entonnoir :</span>
      {steps.map((label, i) => {
        const isOver = over === i;
        return (
          <span key={label} draggable
            onDragStart={() => { drag.current = i; }}
            onDragOver={e => { e.preventDefault(); setOver(i); }}
            onDragLeave={() => setOver(o => o === i ? null : o)}
            onDrop={e => { e.preventDefault(); moveTo(i); }}
            onDragEnd={() => { drag.current = null; setOver(null); }}
            title="Glisser pour ranger"
            style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "4px 6px 4px 10px", borderRadius: 99, fontSize: 11.5, fontWeight: 700, cursor: "grab", userSelect: "none",
              border: "1px solid " + (isOver ? "var(--accent)" : "var(--accent-line)"),
              background: isOver ? "var(--accent-soft)" : "var(--accent-soft)", color: "var(--accent)" }}>
            <span style={{ fontSize: 9, opacity: .4 }}>⠿</span>
            {pill(label)}
            <span onClick={(e) => { e.stopPropagation(); removeStep(label); }} title="Retirer de l'entonnoir"
              style={{ marginLeft: 2, width: 15, height: 15, borderRadius: 99, display: "inline-flex", alignItems: "center", justifyContent: "center",
                cursor: steps.length > 1 ? "pointer" : "not-allowed", opacity: steps.length > 1 ? .7 : .25, fontSize: 12 }}>×</span>
          </span>
        );
      })}

      {available.length > 0 && (adding ? (
        <select autoFocus defaultValue="" onChange={e => e.target.value && addStep(e.target.value)} onBlur={() => setAdding(false)}
          style={{ padding: "4px 8px", borderRadius: 8, border: "1px solid var(--accent-line)", background: "var(--surface-2)", color: "var(--text)", fontSize: 12 }}>
          <option value="" disabled>Choisir une catégorie…</option>
          {available.map(l => <option key={l} value={l}>{pill(l)}</option>)}
        </select>
      ) : (
        <button onClick={() => setAdding(true)} title="Ajouter une catégorie à l'entonnoir de cette feuille"
          style={{ padding: "4px 11px", borderRadius: 99, fontSize: 11.5, fontWeight: 700, cursor: "pointer",
            border: "1px dashed var(--border)", background: "var(--surface-2)", color: "var(--muted)" }}>+ Ajouter une étape</button>
      ))}

      <span style={{ fontSize: 10.5, color: "var(--faint)", marginLeft: "auto" }}>propre à cette feuille · glisse pour ranger</span>
    </div>
  );
}

function CpPirate({ devices, containers, accounts, pick, setPick, q, setQ }) {
  const [step, setStep] = React.useState(null);   // étape de l'entonnoir sur laquelle on a cliqué
  const byId = {}; accounts.forEach(a => { byId[a.id] = a; });
  const rowsFor = (udid) => (containers[udid] || []).map(c => ({ ...c, udid, acct: c.account_id ? byId[c.account_id] : null }));
  const scope = pick === "all" ? devices : devices.filter(d => String(d.udid) === String(pick));
  const rows = scope.flatMap(d => rowsFor(String(d.udid)).map(r => ({ ...r, tag: d.tag || d.label })));

  const created = rows.length;
  const linkedRows = rows.filter(r => r.acct);
  const pluggedRows = linkedRows.filter(r => r.plug || cpIsPlug(r.acct.ig_plug));
  const linked = linkedRows.length, plugged = pluggedRows.length;

  // Les catégories sont EXCLUSIVES : empilées telles quelles, les chiffres ne s'emboîteraient
  // plus et le « % de l'étape précédente » n'aurait aucun sens. On compte donc en CUMULÉ :
  // l'étape « Jour 1 » = ceux qui ont atteint Jour 1 OU AU-DELÀ, dans l'ordre défini par
  // le Stockage comptes. Un compte dont la catégorie est décochée (ou absente) sort de
  // l'entonnoir dès la 1re étape de catégorie — c'est visible dans la chute, et c'est honnête.
  const fcats = cpFunnelCats("pirate");
  const idxOf = (r) => fcats.findIndex(c => c.label === ((r.acct && r.acct.category) || ""));
  // Catégories « bannissantes » = tout label contenant « ban » (Compte Ban, Shadowban…) : c'est un
  // état terminal d'ÉCHEC, pas une étape de progression. On les traite à part (toutes, pas juste la 1re).
  const banIdxs = fcats.reduce((a, c, j) => { if (/ban/i.test((c && c.label) || "")) a.push(j); return a; }, []);
  const isBan = (i) => banIdxs.indexOf(i) >= 0;
  const catSteps = fcats.map((c, j) => ({
    key: "cat:" + c.label,
    label: (c.emoji ? c.emoji + " " : "") + c.label,
    // étape ban = comptée EXACTEMENT (un ban est terminal, pas « X ou au-delà ») ; étape de
    // progression = cumulée (comptes à cette catégorie OU plus mature).
    n: isBan(j) ? pluggedRows.filter(r => idxOf(r) === j).length : pluggedRows.filter(r => idxOf(r) >= j).length,
  }));

  // --- Taux de maturation (winrate) : part des conteneurs créés qui arrivent au bout du process,
  //     c.-à-d. à la DERNIÈRE étape utile (non-« Ban ») et NON bannis. La dernière étape utile =
  //     plus grand index de catégorie qui n'est pas bannissante. ---
  let lastUsefulIdx = -1;
  for (let j = 0; j < fcats.length; j++) { if (!isBan(j)) lastUsefulIdx = j; }
  const lastUsefulCat = lastUsefulIdx >= 0 ? fcats[lastUsefulIdx] : null;
  const banned = pluggedRows.filter(r => isBan(idxOf(r))).length;   // idxOf=-1 (hors entonnoir) -> isBan false
  const matured = lastUsefulIdx >= 0
    ? pluggedRows.filter(r => idxOf(r) >= lastUsefulIdx && !isBan(idxOf(r))).length
    : plugged;   // pas d'étape catégorie utile -> on retombe sur « plugués »
  const winrate = created > 0 ? Math.round((matured / created) * 100) : 0;
  const banRate = plugged > 0 ? Math.round((banned / plugged) * 100) : 0;

  // répartition par catégorie des comptes liés, dans l'ORDRE défini (le même que l'entonnoir) —
  // les catégories inconnues (compte dans une catégorie supprimée) vont à la fin
  const cats = {};
  rows.forEach(r => { if (r.acct) { const c = r.acct.category || "—"; cats[c] = (cats[c] || 0) + 1; } });
  const catOrder = (window.loadCategories ? window.loadCategories() : []).map(c => c.label);
  const catList = Object.keys(cats).sort((a, b) => {
    const ia = catOrder.indexOf(a), ib = catOrder.indexOf(b);
    if (ia !== ib) return (ia < 0 ? 1e9 : ia) - (ib < 0 ? 1e9 : ib);
    return cats[b] - cats[a];
  });

  const ql = q.trim().toLowerCase();
  // l'étape cliquée dans l'entonnoir restreint le tableau à CE sous-ensemble
  const stepCatIdx = (step && step.indexOf("cat:") === 0) ? fcats.findIndex(c => "cat:" + c.label === step) : -1;
  const byStep = step === "linked" ? linkedRows
    : step === "plugged" ? pluggedRows
    : stepCatIdx >= 0
      // étape ban -> exactement ses comptes ; étape de progression -> cette catégorie et au-delà
      ? pluggedRows.filter(r => isBan(stepCatIdx) ? idxOf(r) === stepCatIdx : idxOf(r) >= stepCatIdx)
      : rows;
  const shown = ql ? byStep.filter(r => [r.name, r.acct && r.acct.username, r.acct && r.acct.nom, r.created_by, r.plug_by, r.tag]
    .some(v => String(v || "").toLowerCase().includes(ql))) : byStep;

  const card = { background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 14, padding: 18 };
  const th = { textAlign: "left", padding: "9px 10px", fontSize: 11, fontWeight: 700, color: "var(--muted)", textTransform: "uppercase", letterSpacing: ".04em", whiteSpace: "nowrap" };
  const td = { padding: "9px 10px", fontSize: 12.5, borderTop: "1px solid var(--border)", whiteSpace: "nowrap" };

  if (!devices.length) return <div style={{ ...card, color: "var(--faint)", fontSize: 13 }}>Aucun iPhone pirate dans le parc.</div>;

  return (
    <div style={{ display: "grid", gap: 16 }}>
      <div style={card}>
        <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 14 }}>Entonnoir {pick === "all" ? "· tout le parc pirate" : "· " + (scope[0] ? (scope[0].tag || scope[0].label) : "")}</div>

        {/* Bandeau KPI : taux de maturation (winrate) — combien de comptes arrivent au bout, vivants */}
        <div style={{ display: "flex", alignItems: "center", gap: 16, flexWrap: "wrap", marginBottom: 16, padding: "13px 16px", borderRadius: 12, border: "1px solid var(--border)", background: "var(--surface-2)" }}>
          <div style={{ display: "flex", alignItems: "baseline", gap: 8, flex: "none" }}>
            <span style={{ fontSize: 32, fontWeight: 800, lineHeight: 1, color: winrate >= 50 ? "var(--accent,#00e08a)" : winrate > 0 ? "#f0a020" : "var(--faint)" }}>{winrate}%</span>
            <span style={{ fontSize: 12.5, fontWeight: 700, color: "var(--muted)" }}>taux de<br />maturation</span>
          </div>
          <div style={{ fontSize: 12, color: "var(--faint)", lineHeight: 1.5, flex: 1, minWidth: 180 }}>
            <b style={{ color: "var(--text)" }}>{matured}</b> compte{matured > 1 ? "s" : ""} arrivé{matured > 1 ? "s" : ""} au bout{lastUsefulCat ? " (" + (lastUsefulCat.emoji ? lastUsefulCat.emoji + " " : "") + lastUsefulCat.label + ")" : ""}
            {" "}sur <b style={{ color: "var(--text)" }}>{created}</b> conteneur{created > 1 ? "s" : ""} créé{created > 1 ? "s" : ""}.
          </div>
          {banIdxs.length > 0 && (
            <span title="Comptes bannis, en part des comptes plugués" style={{ flex: "none", fontSize: 11.5, fontWeight: 700, padding: "5px 11px", borderRadius: 99, background: banned ? "rgba(248,81,73,.1)" : "var(--surface)", color: banned ? "#f87171" : "var(--faint)", border: "1px solid " + (banned ? "rgba(248,81,73,.3)" : "var(--border)") }}>
              🚫 {banned} banni{banned > 1 ? "s" : ""}{plugged ? " · " + banRate + "%" : ""}
            </span>
          )}
        </div>

        <CpFunnel active={step} onPick={setStep} steps={[
          { key: "created", label: "Conteneurs créés", n: created },
          { key: "linked",  label: "Compte lié",       n: linked  },
          { key: "plugged", label: "Plug IG validé",   n: plugged },
          ...catSteps,
        ]} />
        <CpCatPicker sheet="pirate" />
        {created > 0 && plugged < created && (
          <div style={{ fontSize: 11.5, color: "var(--faint)", marginTop: 11 }}>
            {created - plugged} conteneur{created - plugged > 1 ? "s" : ""} n'{created - plugged > 1 ? "ont" : "a"} pas encore le plug validé.
          </div>
        )}
      </div>

      {catList.length > 0 && (
        <div style={card}>
          <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 11 }}>Catégories</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
            {catList.map(c => (
              <span key={c} style={{ fontSize: 12, fontWeight: 700, padding: "5px 11px", borderRadius: 99, background: "var(--surface-2)", border: "1px solid var(--border)", color: "var(--text)" }}>
                {c} <span style={{ color: "var(--accent)" }}>{cats[c]}</span>
              </span>
            ))}
          </div>
        </div>
      )}

      <div style={card}>
        <div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 9, marginBottom: 13 }}>
          <div style={{ fontSize: 13, fontWeight: 700 }}>Conteneurs</div>
          {step && (
            <button onClick={() => setStep(null)} title="Revenir à tous les conteneurs"
              style={{ padding: "4px 10px", borderRadius: 99, fontSize: 11.5, fontWeight: 700, cursor: "pointer",
                border: "1px solid var(--accent-line)", background: "var(--accent-soft)", color: "var(--accent)" }}>
              {step === "linked" ? "avec un compte" : step === "plugged" ? "plug validé" : (step || "").replace("cat:", "") + " et au-delà"} ✕
            </button>
          )}
          <button onClick={() => setPick("all")} style={{ padding: "5px 11px", borderRadius: 99, fontSize: 12, fontWeight: 700, cursor: "pointer",
            border: "1px solid " + (pick === "all" ? "var(--accent-line)" : "var(--border)"), background: pick === "all" ? "var(--accent-soft)" : "var(--surface-2)", color: pick === "all" ? "var(--accent)" : "var(--muted)" }}>Tout</button>
          {devices.map(d => {
            const on = String(pick) === String(d.udid);
            const n = (containers[String(d.udid)] || []).length;
            return (
              <button key={d.udid} onClick={() => setPick(String(d.udid))} style={{ padding: "5px 11px", borderRadius: 99, fontSize: 12, fontWeight: 700, cursor: "pointer",
                border: "1px solid " + (on ? "var(--accent-line)" : "var(--border)"), background: on ? "var(--accent-soft)" : "var(--surface-2)", color: on ? "var(--accent)" : "var(--muted)" }}>
                {d.tag || d.label} <span style={{ opacity: .7 }}>{n}</span>
              </button>
            );
          })}
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Rechercher…"
            style={{ marginLeft: "auto", padding: "6px 11px", borderRadius: 8, border: "1px solid var(--border)", background: "var(--surface-2)", color: "var(--text)", fontSize: 12.5, minWidth: 170 }} />
        </div>

        {shown.length === 0 ? (
          <div style={{ fontSize: 12.5, color: "var(--faint)", padding: "10px 0" }}>Aucun conteneur.</div>
        ) : (
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", minWidth: 900 }}>
              <thead><tr>
                <th style={th}>iPhone</th><th style={th}>Conteneur</th><th style={th}>Compte</th><th style={th}>Catégorie</th>
                <th style={th}>Plug</th><th style={th}>Créé par</th><th style={th}>Plug validé par</th><th style={th}>Créé le</th>
              </tr></thead>
              <tbody>
                {shown.map(r => {
                  const plug = !!(r.acct && (r.plug || cpIsPlug(r.acct.ig_plug)));
                  return (
                    <tr key={r.udid + r.id}>
                      <td style={{ ...td, color: "var(--muted)" }}>{r.tag}</td>
                      <td style={{ ...td, fontWeight: 700 }}>{r.name}</td>
                      <td style={td}>{r.acct ? <span className="mono">@{r.acct.username || "—"}</span> : <span style={{ color: "var(--faint)" }}>aucun</span>}</td>
                      <td style={{ ...td, color: "var(--muted)" }}>{(r.acct && r.acct.category) || "—"}</td>
                      <td style={td}>
                        <span style={{ fontSize: 11, fontWeight: 700, padding: "2px 8px", borderRadius: 99,
                          background: plug ? "var(--accent-soft)" : "var(--surface-2)", color: plug ? "var(--accent)" : "var(--faint)",
                          border: "1px solid " + (plug ? "var(--accent-line)" : "var(--border)") }}>{plug ? "🔌 Plug" : "—"}</span>
                      </td>
                      <td style={{ ...td, color: r.created_by ? "var(--text)" : "var(--faint)" }}>{r.created_by || "—"}</td>
                      <td style={{ ...td, color: r.plug_by ? "var(--text)" : "var(--faint)" }}>{r.plug_by || "—"}</td>
                      <td style={{ ...td, color: "var(--faint)" }}>{r.created ? String(r.created).slice(0, 10) : "—"}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </div>
  );
}

// Branches cloud / physique : la même lecture que le Stockage, restreinte à la branche.
function CpSimple({ kind, accounts, q, setQ }) {
  const ql = q.trim().toLowerCase();
  const shown = ql ? accounts.filter(a => [a.username, a.nom, a.category, a.operator, a.iphone_attribue, a.cloud_attribue]
    .some(v => String(v || "").toLowerCase().includes(ql))) : accounts;
  const cats = {};
  accounts.forEach(a => { const c = a.category || "—"; cats[c] = (cats[c] || 0) + 1; });
  const catList = Object.keys(cats).sort((a, b) => cats[b] - cats[a]);

  const card = { background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 14, padding: 18 };
  const th = { textAlign: "left", padding: "9px 10px", fontSize: 11, fontWeight: 700, color: "var(--muted)", textTransform: "uppercase", letterSpacing: ".04em", whiteSpace: "nowrap" };
  const td = { padding: "9px 10px", fontSize: 12.5, borderTop: "1px solid var(--border)", whiteSpace: "nowrap" };

  return (
    <div style={{ display: "grid", gap: 16 }}>
      {catList.length > 0 && (
        <div style={card}>
          <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 11 }}>Catégories</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
            {catList.map(c => (
              <span key={c} style={{ fontSize: 12, fontWeight: 700, padding: "5px 11px", borderRadius: 99, background: "var(--surface-2)", border: "1px solid var(--border)", color: "var(--text)" }}>
                {c} <span style={{ color: "var(--accent)" }}>{cats[c]}</span>
              </span>
            ))}
          </div>
        </div>
      )}
      <div style={card}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 13 }}>
          <div style={{ fontSize: 13, fontWeight: 700, flex: 1 }}>{accounts.length} compte{accounts.length > 1 ? "s" : ""}</div>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Rechercher…"
            style={{ padding: "6px 11px", borderRadius: 8, border: "1px solid var(--border)", background: "var(--surface-2)", color: "var(--text)", fontSize: 12.5, minWidth: 170 }} />
        </div>
        {shown.length === 0 ? (
          <div style={{ fontSize: 12.5, color: "var(--faint)" }}>Aucun compte attribué à ce type de téléphone.</div>
        ) : (
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", minWidth: 760 }}>
              <thead><tr>
                <th style={th}>Nom</th><th style={th}>Username</th><th style={th}>Catégorie</th>
                <th style={th}>{kind === "cloud" ? "Profil cloud" : "iPhone"}</th><th style={th}>Plug</th>
                <th style={th}>Opérateur</th><th style={th}>Créé le</th>
              </tr></thead>
              <tbody>
                {shown.map(a => {
                  const plug = cpIsPlug(a.ig_plug);
                  return (
                    <tr key={a.id}>
                      <td style={{ ...td, fontWeight: 700 }}>{a.nom || "—"}</td>
                      <td style={td}><span className="mono">@{a.username || "—"}</span></td>
                      <td style={{ ...td, color: "var(--muted)" }}>{a.category || "—"}</td>
                      <td style={{ ...td, color: "var(--muted)" }}>{(kind === "cloud" ? a.cloud_attribue : a.iphone_attribue) || "—"}</td>
                      <td style={td}>
                        <span style={{ fontSize: 11, fontWeight: 700, padding: "2px 8px", borderRadius: 99,
                          background: plug ? "var(--accent-soft)" : "var(--surface-2)", color: plug ? "var(--accent)" : "var(--faint)",
                          border: "1px solid " + (plug ? "var(--accent-line)" : "var(--border)") }}>{plug ? "🔌 Plug" : "—"}</span>
                      </td>
                      <td style={{ ...td, color: a.operator ? "var(--text)" : "var(--faint)" }}>{a.operator || "—"}</td>
                      <td style={{ ...td, color: "var(--faint)" }}>{a.saved_at ? String(a.saved_at).slice(0, 10) : "—"}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </div>
  );
}


Object.assign(window, { ComptesParc });
