// ============================================================================
// TEXTES PAR PACK DE CONTENU SOURCE
//
// LE PROBLEME. Un VA demande du contenu ("Pack Mi-bombe"), recoit ses fichiers,
// puis doit ecrire a la main une bio, une legende de reel et le libelle du lien
// en story. Il improvise, ou il recopie le compte d'a cote. Les deux sont
// mauvais : le premier fait perdre du temps, le second fabrique un motif commun
// entre des comptes qui devraient etre etrangers l'un a l'autre.
//
// LA REPONSE. L'owner ecrit UNE FOIS une reserve de textes par pack. Le VA voit
// un badge portant le pack qu'il a demande, l'ouvre, et a ses trois textes prets
// a copier ou a envoyer sur le telephone.
//
// POURQUOI UN TIRAGE CALCULE ET NON UN random(). Deux exigences que Math.random
// ne peut pas tenir :
//   1. STABILITE. Un VA qui voit une bio, commence a la taper, rafraichit la page
//      et en voit une AUTRE ne sait plus laquelle il a posee. Le tirage doit donc
//      rendre le meme texte tant qu'on parle du meme compte.
//   2. DISPERSION. Deux comptes ne doivent pas tomber sur le meme texte par
//      hasard -- surtout deux comptes du MEME telephone, deja lies par l'appareil
//      et l'IP. Une bio identique par-dessus, c'est le motif qu'on cherche a fuir.
// Une empreinte de l'identifiant du compte donne les deux gratuitement, sans rien
// stocker et sans qu'aucune ecriture concurrente ne puisse se perdre.
// ============================================================================

const TextesPacks = (function () {
  const CLE = "phonelabs.textes_packs";      // miroir local : affichage immediat
  const TYPES = ["bio", "story", "reel"];
  const ETIQ = { bio: "Bio du compte", story: "Lien en story", reel: "Description du reel" };
  const ICONE = { bio: "👤", story: "🔗", reel: "🎬" };
  const abonnes = new Set();
  const emit = () => abonnes.forEach(f => { try { f(); } catch (e) {} });

  const lire = () => { try { return JSON.parse(localStorage.getItem(CLE) || "{}") || {}; } catch (e) { return {}; } };
  const ecrireLocal = (v) => { try { localStorage.setItem(CLE, JSON.stringify(v)); } catch (e) {} };

  // FNV-1a. On ne cherche pas une qualite cryptographique : on cherche qu'un meme
  // identifiant rende toujours le meme nombre, et que deux identifiants voisins
  // ("acc_41" / "acc_42") tombent LOIN l'un de l'autre. FNV-1a fait exactement ca
  // en cinq lignes, sans dependance.
  const empreinte = (s) => {
    let h = 2166136261 >>> 0;
    for (let i = 0; i < String(s).length; i++) {
      h ^= String(s).charCodeAt(i);
      h = Math.imul(h, 16777619) >>> 0;
    }
    return h >>> 0;
  };

  return {
    TYPES, ETIQ, ICONE,
    sub(cb) { abonnes.add(cb); return () => abonnes.delete(cb); },
    tout: lire,
    // Les trois listes d'un pack, toujours completes : l'appelant n'a jamais a
    // verifier si la cle existe.
    pack(nom) {
      const p = (lire() || {})[String(nom || "")] || {};
      const out = {};
      TYPES.forEach(t => { out[t] = Array.isArray(p[t]) ? p[t].filter(x => String(x || "").trim()) : []; });
      return out;
    },
    async charger() {
      if (!(window.Backend && window.Backend.getStore)) return lire();
      try {
        const r = await window.Backend.getStore("textespacks");
        if (r && r.value && typeof r.value === "object") { ecrireLocal(r.value); emit(); return r.value; }
      } catch (e) {}
      return lire();
    },
    // Ecriture reservee a l'owner cote worker (write: 2). Le VA ne fait que lire :
    // c'est ce qui rend ce magasin sur malgre son format en bloc unique.
    async enregistrer(nom, textes) {
      const tout = lire();
      tout[String(nom)] = { bio: textes.bio || [], story: textes.story || [], reel: textes.reel || [] };
      ecrireLocal(tout); emit();
      if (window.Backend && window.Backend.saveStore) {
        try { return await window.Backend.saveStore("textespacks", tout); } catch (e) { return false; }
      }
      return false;
    },
    // `decalage` = le bouton « autre » du VA. Il ne change pas le tirage des autres
    // comptes : il fait juste avancer CELUI-CI d'un cran dans la liste.
    tirer(nomPack, type, cleCompte, decalage) {
      const liste = this.pack(nomPack)[type] || [];
      if (!liste.length) return "";
      const i = (empreinte(String(cleCompte || "") + ":" + type) + (decalage || 0)) % liste.length;
      return liste[i];
    },
    combien(nomPack) {
      const p = this.pack(nomPack);
      return TYPES.reduce((n, t) => n + p[t].length, 0);
    },
  };
})();

window.TextesPacks = TextesPacks;

// ── EDITEUR (owner) : Dossiers -> Contenu Source -> un pack -> « ✍️ Textes » ──
// Une ligne = un texte. C'est volontairement pauvre : l'owner colle depuis son
// bloc-notes, et une zone de saisie par type se remplit en dix secondes. Un
// tableau a lignes avec des boutons « + » couterait dix fois plus cher a utiliser.
function ModaleTextesPack({ pack, onClose }) {
  const { useState, useEffect } = React;
  const T = window.TextesPacks;
  const [val, setVal] = useState(() => {
    const p = T.pack(pack);
    const o = {}; T.TYPES.forEach(t => { o[t] = (p[t] || []).join("\n"); }); return o;
  });
  const [etat, setEtat] = useState("");
  useEffect(() => {
    T.charger().then(() => {
      const p = T.pack(pack);
      setVal(v => {
        // On ne remplace PAS ce que l'owner est en train de taper : si un champ a deja
        // ete touche, la version distante ne doit pas l'ecraser sous ses doigts.
        const o = {}; T.TYPES.forEach(t => { o[t] = (v[t] && v[t].length) ? v[t] : (p[t] || []).join("\n"); });
        return o;
      });
    }).catch(() => {});
  }, []);
  const enregistrer = async () => {
    setEtat("envoi");
    const textes = {};
    T.TYPES.forEach(t => { textes[t] = String(val[t] || "").split("\n").map(s => s.trim()).filter(Boolean); });
    const ok = await T.enregistrer(pack, textes);
    setEtat(ok === false ? "erreur" : "ok");
    setTimeout(() => setEtat(""), 2200);
  };
  const compte = (t) => String(val[t] || "").split("\n").filter(s => s.trim()).length;
  const fond = { position: "fixed", inset: 0, background: "rgba(0,0,0,.55)", zIndex: 9000,
    display: "flex", alignItems: "center", justifyContent: "center", padding: 20 };
  const boite = { background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 16,
    padding: 20, width: "min(760px,96vw)", maxHeight: "90vh", overflowY: "auto" };
  const zone = { width: "100%", minHeight: 104, background: "var(--surface-2)", color: "var(--text)",
    border: "1px solid var(--border)", borderRadius: 10, padding: "9px 11px", fontSize: 12.5,
    fontFamily: "inherit", lineHeight: 1.55, resize: "vertical" };
  return (
    <div style={fond} onClick={onClose}>
      <div style={boite} onClick={e => e.stopPropagation()}>
        <div style={{ fontSize: 16, fontWeight: 800, marginBottom: 3 }}>✍️ Textes du pack « {pack} »</div>
        <div style={{ fontSize: 12, color: "var(--muted)", marginBottom: 16, lineHeight: 1.6 }}>
          Une ligne = une proposition. Quand un VA demande ce pack, l'app lui en attribue une de
          chaque type. <b>Plus la réserve est fournie, moins deux comptes se ressemblent</b> — et
          c'est précisément ce qui évite qu'ils soient rapprochés les uns des autres.
        </div>
        {T.TYPES.map(t => (
          <div key={t} style={{ marginBottom: 14 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}>
              <span style={{ fontSize: 13 }}>{T.ICONE[t]}</span>
              <span style={{ fontSize: 12, fontWeight: 800, letterSpacing: ".05em", textTransform: "uppercase", color: "var(--muted)" }}>{T.ETIQ[t]}</span>
              <span style={{ marginLeft: "auto", fontSize: 11, color: compte(t) ? "var(--accent)" : "var(--faint)" }}>
                {compte(t)} proposition{compte(t) > 1 ? "s" : ""}</span>
            </div>
            <textarea style={zone} value={val[t] || ""} spellCheck={false}
              onChange={e => setVal(v => ({ ...v, [t]: e.target.value }))} />
          </div>
        ))}
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 6 }}>
          <button onClick={onClose} style={{ background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 9, padding: "9px 15px", color: "var(--text)", fontSize: 12.5, fontWeight: 700, cursor: "pointer" }}>Fermer</button>
          <button onClick={enregistrer} disabled={etat === "envoi"}
            style={{ marginLeft: "auto", background: "var(--accent)", border: "none", borderRadius: 9, padding: "9px 17px", color: "#04130c", fontSize: 12.5, fontWeight: 800, cursor: "pointer" }}>
            {etat === "envoi" ? "Enregistrement…" : etat === "ok" ? "✓ Enregistré" : etat === "erreur" ? "⚠️ Échec — réessaie" : "Enregistrer"}</button>
        </div>
      </div>
    </div>
  );
}

// ── BADGE (VA) : dans l'en-tete du bloc « Saisie de texte », a cote du 📋 ──
function BadgeTextesPack({ nomPack, cleCompte, udid, dead }) {
  const { useState, useEffect } = React;
  const T = window.TextesPacks;
  const [ouvert, setOuvert] = useState(false);
  const [, refaire] = React.useReducer(x => x + 1, 0);
  const [decal, setDecal] = useState({});
  const [flash, setFlash] = useState("");
  useEffect(() => { T.charger().then(refaire).catch(() => {}); return T.sub(refaire); }, []);
  if (!nomPack) return null;                       // aucun contenu demande -> pas de badge
  const n = T.combien(nomPack);
  const dire = (m) => { setFlash(m); setTimeout(() => setFlash(""), 1500); };
  const copier = (v) => { try { navigator.clipboard.writeText(String(v)); dire("📋 Copié"); } catch (e) { dire("⚠️ Copie impossible"); } };
  const envoyer = async (v) => {
    if (!v || dead) return;
    try { await window.Backend.action(udid, "inputText", { text: String(v), content: String(v) }); dire("📲 Envoyé"); }
    catch (e) { dire("⚠️ Échec de l'envoi"); }
  };
  const puce = {
    display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11, fontWeight: 700,
    padding: "3px 9px", borderRadius: 99, cursor: "pointer", maxWidth: 190,
    background: ouvert ? "rgba(190,150,255,.18)" : "var(--surface-2)",
    border: "1px solid " + (ouvert ? "rgba(190,150,255,.55)" : "var(--border)"),
    color: ouvert ? "#c9a9ff" : "var(--muted)",
  };
  return (
    <>
      <button onClick={() => setOuvert(o => !o)} style={puce}
        title={"Contenu demandé : " + nomPack + (n ? " — " + n + " textes disponibles" : " — aucun texte enregistré")}>
        <span>🎬</span>
        <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{nomPack}</span>
      </button>
      {ouvert && (
        <div style={{ flexBasis: "100%", marginTop: 9, padding: "9px 11px", borderRadius: 9,
          background: "var(--surface-2)", border: "1px solid var(--border)" }}>
          {flash && <div style={{ fontSize: 11, color: "var(--accent)", marginBottom: 6 }}>{flash}</div>}
          {n === 0
            ? <div style={{ fontSize: 11.5, color: "var(--faint)", lineHeight: 1.6 }}>
                Aucun texte enregistré pour « {nomPack} ».
                <br />L'owner les ajoute dans <b>Dossiers → Contenu Source → {nomPack} → ✍️ Textes</b>.
              </div>
            : T.TYPES.map(t => {
                const v = T.tirer(nomPack, t, cleCompte, decal[t] || 0);
                const dispo = (T.pack(nomPack)[t] || []).length;
                return (
                  <div key={t} style={{ marginBottom: 9 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }}>
                      <span style={{ fontSize: 11 }}>{T.ICONE[t]}</span>
                      <span style={{ fontSize: 10.5, fontWeight: 800, letterSpacing: ".05em", textTransform: "uppercase", color: "var(--muted)" }}>{T.ETIQ[t]}</span>
                      {dispo > 1 && <button onClick={() => setDecal(d => ({ ...d, [t]: (d[t] || 0) + 1 }))}
                        title="Proposer un autre texte" style={{ marginLeft: "auto", background: "none", border: "none", color: "var(--faint)", fontSize: 11, cursor: "pointer" }}>🎲 autre</button>}
                    </div>
                    {!dispo
                      ? <div style={{ fontSize: 11, color: "var(--faint)" }}>— rien pour ce type —</div>
                      : <div style={{ display: "flex", alignItems: "stretch", gap: 5 }}>
                          <div style={{ flex: 1, minWidth: 0, padding: "7px 9px", borderRadius: 7, background: "var(--surface)",
                            border: "1px solid var(--border)", fontSize: 12, color: "var(--text)", lineHeight: 1.5, whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{v}</div>
                          <button onClick={() => copier(v)} title="Copier" style={{ flex: "none", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 7, padding: "5px 8px", fontSize: 12, cursor: "pointer", color: "var(--muted)" }}>📋</button>
                          <button onClick={() => envoyer(v)} disabled={dead} title="Envoyer sur le téléphone"
                            style={{ flex: "none", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 7, padding: "5px 8px", fontSize: 12, cursor: dead ? "not-allowed" : "pointer", opacity: dead ? .45 : 1, color: "var(--muted)" }}>📲</button>
                        </div>}
                  </div>
                );
              })}
        </div>
      )}
    </>
  );
}

Object.assign(window, { ModaleTextesPack, BadgeTextesPack });
