/* Cyclope — composants branchés sur l'API réelle (override les composants de démo). */
const { useState, useEffect, useMemo } = React;

// Catégories (le design en a 3 ; le backend peut en renvoyer 6) — on complète.
Object.assign(CATS, {
  juridique: { label: "Risque juridique", short: "Juridique", desc: "Formulation pouvant être opposée à l'entreprise." },
  confidentialite: { label: "Information sensible", short: "Confidentialité", desc: "Donnée sensible ou inappropriée communiquée." },
  commercial: { label: "Condition commerciale", short: "Commercial", desc: "Remise ou condition inhabituelle sans arbitrage." },
});
const CAT = (c) => CATS[c] || { label: c, short: c, desc: "" };
// Versions défensives : une catégorie/sévérité inattendue de l'IA ne doit pas casser le rendu.
function CatTag({ cat }) { return <span className="ct">{CAT(cat).short}</span>; }
function RiskTag({ level, small }) {
  const r = RISK[level] || RISK.faible;
  return <span className={`rt${small ? " rt-s" : ""}`} style={{ color: r.color, background: r.bg }}><span className="rt-bars">{[1, 2, 3].map(i => <i key={i} style={{ background: i <= r.n ? r.color : "currentColor", opacity: i <= r.n ? 1 : .22 }} />)}</span>{r.label}</span>;
}

function api(path, opts = {}) {
  const base = window.CYCLOPE_API || "";
  return window.CYCLOPE_AUTH.authedFetch(base + path, opts).then(r => r.json());
}
const eur = n => (Number(n) || 0).toFixed(2).replace(".", ",") + " €";
const num = n => (Number(n) || 0).toLocaleString("fr-FR");

/* ---------------- CONSULTANT ---------------- */
function Queue({ onOpen, onChange }) {
  const [data, setData] = useState(null);
  const [risk, setRisk] = useState("tous"), [cat, setCat] = useState("tous"), [sort, setSort] = useState("risque"), [tab, setTab] = useState("attente");
  const load = () => api("/api/queue?status=" + tab).then(d => { if (d.ok) { setData(d); onChange && onChange(); } }).catch(() => setData({ items: [], counts: {} }));
  useEffect(() => { setData(null); load(); }, [tab]);
  const items = (data && data.items) || [], counts = (data && data.counts) || {};
  const rows = useMemo(() => {
    let r = items.slice();
    if (risk !== "tous") r = r.filter(m => m.risk_level === risk);
    if (cat !== "tous") r = r.filter(m => (m.categories || []).includes(cat));
    r.sort((a, b) => sort === "risque" ? b.risk_score - a.risk_score : new Date(b.ts) - new Date(a.ts));
    return r;
  }, [items, risk, cat, sort]);
  return (
    <div className="page">
      <div className="page-h"><div><h1>File d'attente</h1><p>Les e-mails jugés risqués sont retenus ici. Tout le reste part immédiatement.</p></div><div className="live"><i />Interception active</div></div>
      <Seg value={tab} onChange={setTab} options={[{ v: "attente", l: "En attente", n: counts.attente || 0 }, { v: "valide", l: "Validés", n: counts.valide || 0 }, { v: "refuse", l: "Refusés", n: counts.refuse || 0 }]} />
      <div className="filters">
        <div className="fl-g"><span className="fl-l">Niveau</span>{["tous", "critique", "moyen", "faible"].map(v => <button key={v} className={`chip${risk === v ? " on" : ""}`} onClick={() => setRisk(v)}>{v === "tous" ? "Tous" : RISK[v].label}</button>)}</div>
        <div className="fl-g"><span className="fl-l">Catégorie</span>{["tous", "engagement", "garantie", "reputation", "juridique", "confidentialite", "commercial"].map(v => <button key={v} className={`chip${cat === v ? " on" : ""}`} onClick={() => setCat(v)}>{v === "tous" ? "Toutes" : CAT(v).short}</button>)}</div>
        <div className="fl-g fl-r"><span className="fl-l">Tri</span><select className="sel-s" value={sort} onChange={e => setSort(e.target.value)}><option value="risque">Risque décroissant</option><option value="date">Plus récents</option></select></div>
      </div>
      {!data ? <div className="empty-load">Chargement…</div> : rows.length === 0 ? <Empty title="Aucun e-mail dans cette vue" sub="Les nouveaux e-mails retenus apparaissent ici en temps réel." /> : (
        <div className="qlist">{rows.map(m => (
          <button key={m.id} className="qrow" onClick={() => onOpen(m.id)}>
            <span className="q-risk"><RiskTag level={m.risk_level} /></span>
            <span className="q-main">
              <span className="q-subj">{m.subject || "(sans objet)"}</span>
              <span className="q-meta">{m.sender_name || m.sender} <em>→</em> {m.recipient}</span>
              <span className="q-cats">{(m.categories || []).map(c => <CatTag key={c} cat={c} />)}<span className="q-cnt">{(m.risks || []).length} signalement{(m.risks || []).length > 1 ? "s" : ""}</span></span>
            </span>
            <span className="q-side"><span className="q-score">{m.risk_score}<em>/100</em></span><span className="q-time">{new Date(m.ts).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}</span>{m.decided_by ? <span className="q-by">{m.status === "valide" ? "Validé" : "Refusé"} par {m.decided_by}</span> : null}</span>
            <span className="q-go"><Ic d="arrow" s={17} /></span>
          </button>
        ))}</div>
      )}
    </div>
  );
}

function Review({ id, onBack }) {
  const [m, setM] = useState(null);
  const [text, setText] = useState("");
  const [busy, setBusy] = useState(false);
  useEffect(() => { api("/api/email/" + encodeURIComponent(id)).then(d => { if (d.ok) { setM(d.email); setText(d.email.corrected || d.email.body || ""); } }).catch(() => {}); }, [id]);
  if (!m) return <div className="page"><div className="empty-load">Chargement de l'e-mail…</div></div>;
  const done = m.status === "valide" || m.status === "refuse";
  const decide = (action) => { if (busy) return; setBusy(true); api("/api/email/" + encodeURIComponent(id), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action, body: text }) }).then(() => onBack()).catch(() => setBusy(false)); };
  return (
    <div className="page page-rev">
      <div className="rev-top"><button className="lnk" onClick={onBack}><Ic d="back" s={15} />File d'attente</button><span className="rev-id">{m.id}</span></div>
      <div className="rev-head">
        <div><h1>{m.subject || "(sans objet)"}</h1><div className="rev-people"><span><b>{m.sender_name || m.sender}</b> · {m.sender}</span><span>Destinataire <b>{m.recipient}</b>{m.cc ? ` · Copie ${m.cc}` : ""}</span></div></div>
        <div className="rev-risk"><RiskTag level={m.risk_level} /><div className="rev-score"><b>{m.risk_score}</b><span>score de risque</span></div></div>
      </div>
      {done ? <div className={`banner ${m.status === "valide" ? "ok" : "no"}`}><Ic d={m.status === "valide" ? "check" : "x"} s={16} />{m.status === "valide" ? "E-mail validé et transmis au destinataire." : "Envoi refusé définitivement. L'expéditeur a été notifié."}</div> : null}
      <Card title={`Risques détectés (${(m.risks || []).length})`} sub="Analysé par l'IA avant transmission." pad={false}>
        <div className="flags">{(m.risks || []).map((f, i) => (
          <div className="flag" key={i}>
            <div className="flag-h"><RiskTag level={f.severity} small /><CatTag cat={f.category} /></div>
            <blockquote>« {f.quote} »</blockquote>
            <p className="flag-w">{f.explanation}</p>
          </div>
        ))}{(m.risks || []).length === 0 ? <div style={{ padding: "18px 22px", color: "var(--ink-3)" }}>Aucun risque spécifique listé.</div> : null}</div>
      </Card>
      <Card title="Message d'origine" sub="Tel qu'écrit par l'expéditeur. Non transmis en l'état.">
        <div className="mail-body">{String(m.body || "").split("\n").filter(x => x.trim()).map((p, i) => <p key={i}>{p}</p>)}</div>
      </Card>
      <Card title="Version à envoyer" sub={done ? "Version transmise." : "Corrigée par l'IA. Modifiable avant validation."}>
        {done ? <div className="mail-body">{String(text).split("\n").filter(x => x.trim()).map((p, i) => <p key={i}>{p}</p>)}</div>
          : <textarea className="composer" value={text} onChange={e => setText(e.target.value)} spellCheck="false" />}
      </Card>
      {!done ? <div className="rev-actions"><div className="ra-l"><Btn kind="primary" icon="check" onClick={() => decide("valide")} disabled={busy}>Valider et envoyer</Btn><Btn kind="danger" icon="x" onClick={() => decide("refuse")} disabled={busy}>Refuser l'envoi</Btn></div><p>La validation transmet cette version au destinataire et consigne la décision.</p></div>
        : <div className="rev-actions"><Btn icon="back" onClick={onBack}>Retour à la file</Btn></div>}
    </div>
  );
}

function Corrections() {
  const [items, setItems] = useState(null);
  const [open, setOpen] = useState(null);
  useEffect(() => { api("/api/corrections").then(d => { if (d.ok) setItems(d.items); }).catch(() => setItems([])); }, []);
  return (
    <div className="page">
      <div className="page-h"><div><h1>Corrections automatiques</h1><p>Ces e-mails ne présentaient aucun risque : corrigés puis envoyés sans intervention.</p></div>{items ? <div className="live"><i />{items.length} corrections</div> : null}</div>
      {!items ? <div className="empty-load">Chargement…</div> : items.length === 0 ? <Empty title="Aucune correction pour l'instant" sub="Les e-mails corrigés automatiquement s'afficheront ici." /> : (
        <div className="fixlist">{items.map(f => (
          <div className={`fix${open === f.id ? " on" : ""}`} key={f.id}>
            <button className="fix-h" onClick={() => setOpen(open === f.id ? null : f.id)}>
              <span className="fix-t">{new Date(f.ts).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}</span>
              <span className="fix-m"><b>{f.subject || "(sans objet)"}</b><em>{f.sender} → {f.recipient}</em></span>
              <span className="fix-c"><Ic d="check" s={14} />Envoyé</span>
            </button>
            {open === f.id ? <div className="fix-b"><div className="diff"><div className="diff-r" style={{ flexDirection: "column", alignItems: "stretch", gap: 8 }}><div><span className="kk">Avant</span><s style={{ display: "block", marginTop: 4 }}>{f.before}</s></div><div><span className="kk">Après</span><ins style={{ display: "block", marginTop: 4 }}>{f.after}</ins></div></div></div></div> : null}
          </div>
        ))}</div>
      )}
    </div>
  );
}

/* ---------------- ADMIN ---------------- */
function AdminOverview() {
  const [v, setV] = useState(null);
  const [dec, setDec] = useState([]);
  useEffect(() => {
    api("/api/volumes").then(d => { if (d.ok) setV(d); });
    Promise.all([api("/api/queue?status=valide"), api("/api/queue?status=refuse"), api("/api/queue?status=attente")]).then(([a, b, c]) => {
      const all = [...(a.items || []), ...(b.items || []), ...(c.items || [])].sort((x, y) => new Date(y.ts) - new Date(x.ts)).slice(0, 10);
      setDec(all);
    }).catch(() => {});
  }, []);
  const vol = (v && v.volumes) || {}, cost = (v && v.cost) || {};
  const stats = [
    { k: "Analysés", v: num(vol.analyzed || 0), s: "e-mails" },
    { k: "Corrigés", v: num(vol.corrected || 0), s: "sans intervention" },
    { k: "En attente", v: num(vol.held || 0), s: "à revoir" },
    { k: "Validés", v: num(vol.validated || 0), s: "transmis" },
  ];
  return (
    <div className="page">
      <div className="page-h"><div><h1>Vue d'ensemble</h1><p>Activité de votre espace de travail</p></div><div className="live"><i />Interception active</div></div>
      <div className="stats">{stats.map(s => <Stat key={s.k} k={s.k} v={s.v} s={s.s} />)}</div>
      <div className="cols">
        <Card title="Coût d'utilisation de l'IA" sub="Depuis le début">
          <div className="cost-tot"><b>{eur(cost.total)}</b><span>{num(cost.calls || 0)} analyses</span></div>
          <div className="cost-list">
            <div className="cost-row"><div><b>Jetons en entrée</b></div><div className="cost-n"><b>{num(cost.in_tokens || 0)}</b></div></div>
            <div className="cost-row"><div><b>Jetons en sortie</b></div><div className="cost-n"><b>{num(cost.out_tokens || 0)}</b></div></div>
          </div>
          <p className="foot">Coût moyen par analyse : <b>{eur(cost.calls ? cost.total / cost.calls : 0)}</b></p>
        </Card>
        <Card title="Répartition" sub="Traitement des e-mails">
          <div className="cost-list">
            {[["Retenus (en attente)", vol.held], ["Validés", vol.validated], ["Refusés", vol.refused], ["Envoyés directement", vol.sent], ["Corrigés auto", vol.corrected]].map(([l, n]) =>
              <div className="cost-row" key={l}><div><b>{l}</b></div><div className="cost-n"><b>{num(n || 0)}</b></div></div>)}
          </div>
        </Card>
      </div>
      <Card title="Journal des décisions" sub="Dernières revues effectuées">
        <table className="tbl"><thead><tr><th>E-mail</th><th>Expéditeur</th><th>Niveau</th><th>Décision</th><th>Consultant</th></tr></thead>
          <tbody>{dec.length === 0 ? <tr><td colSpan={5} style={{ color: "var(--ink-3)" }}>Aucune décision pour l'instant.</td></tr> : dec.map(m => (
            <tr key={m.id}><td><b>{m.subject || "(sans objet)"}</b><em>{m.id}</em></td><td>{m.sender_name || m.sender}</td><td><RiskTag level={m.risk_level} small /></td>
              <td>{m.status === "valide" ? <Pill tone="ok">Validé</Pill> : m.status === "refuse" ? <Pill tone="no">Refusé</Pill> : <Pill tone="mid">En attente</Pill>}</td><td>{m.decided_by || "—"}</td></tr>
          ))}</tbody></table>
      </Card>
    </div>
  );
}

function AdminAI() {
  const [cfg, setCfg] = useState(null);
  const [prov, setProv] = useState("anthropic"), [model, setModel] = useState(""), [key, setKey] = useState(""), [company, setCompany] = useState("");
  const [th, setTh] = useState(55), [saving, setSaving] = useState(false), [msg, setMsg] = useState("");
  useEffect(() => { api("/api/config").then(d => { if (d.ok) { const c = d.config; setCfg(c); setProv(c.ai.provider || "anthropic"); setModel(c.ai.model || ""); setCompany((c.company || {}).name || ""); setTh(c.risk_threshold || 55); } }); }, []);
  const models = { anthropic: ["claude-sonnet-4-5", "claude-opus-4-1", "claude-haiku-4-5"], openai: ["gpt-4o", "gpt-4o-mini", "gpt-4.1"] };
  const save = () => {
    setSaving(true); setMsg("");
    const body = { config: { ai: { provider: prov, model: model || models[prov][0] }, company: { name: company } }, risk_threshold: th };
    if (key && !key.startsWith("••••")) body.config.ai.api_key = key;
    api("/api/config", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })
      .then(d => { setSaving(false); setMsg(d.ok ? "Enregistré ✓" : "Erreur"); setKey(""); api("/api/config").then(x => x.ok && setCfg(x.config)); }).catch(() => { setSaving(false); setMsg("Erreur réseau"); });
  };
  if (!cfg) return <div className="page"><div className="empty-load">Chargement…</div></div>;
  return (
    <div className="page">
      <div className="page-h"><div><h1>Intelligence artificielle</h1><p>Fournisseur, clé d'accès, modèle et seuil de mise en attente.</p></div></div>
      <div className="cols">
        <Card title="Fournisseur d'IA" sub="Le modèle qui analyse et corrige les e-mails" right={<Pill tone={cfg.ai.api_key_set ? "ok" : "no"}>{cfg.ai.api_key_set ? "Clé enregistrée" : "Aucune clé"}</Pill>}>
          <Field label="Fournisseur"><select value={prov} onChange={e => { setProv(e.target.value); setModel(""); }}><option value="anthropic">Anthropic (Claude)</option><option value="openai">OpenAI (GPT)</option></select></Field>
          <Field label="Modèle"><select value={model || models[prov][0]} onChange={e => setModel(e.target.value)}>{models[prov].map(m => <option key={m} value={m}>{m}</option>)}</select></Field>
          <Field label="Clé API" mono hint={cfg.ai.api_key_set ? "Laissez vide pour conserver la clé actuelle. Saisissez-en une nouvelle pour la remplacer." : "Requise pour activer l'analyse."}>
            <input type="password" value={key} onChange={e => setKey(e.target.value)} placeholder={cfg.ai.api_key_set ? "•••• clé enregistrée" : (prov === "anthropic" ? "sk-ant-…" : "sk-…")} />
          </Field>
          {cfg.ai.api_key_set
            ? <div className="keystate ok"><Ic d="check" s={15} />Clé {prov === "anthropic" ? "Anthropic" : "OpenAI"} enregistrée et chiffrée — l'analyse est active.</div>
            : <div className="keystate no"><Ic d="alert" s={15} />Aucune clé enregistrée — l'analyse des e-mails est inactive.</div>}
          <Field label="Nom de l'entreprise" hint="Utilisé par l'IA pour contextualiser l'analyse."><input value={company} onChange={e => setCompany(e.target.value)} placeholder="Ex. Nordis Conseil" /></Field>
        </Card>
        <Card title="Seuil de mise en attente" sub="Score de risque au-delà duquel l'e-mail est retenu pour revue">
          <div className="slider"><div className="sl-h"><span>Seuil de rétention</span><b>{th}</b></div><input type="range" min="20" max="90" value={th} onChange={e => setTh(+e.target.value)} /></div>
          <p className="foot">Au-dessus de <b>{th}/100</b>, l'e-mail est retenu. En dessous, il est corrigé puis envoyé immédiatement. Un e-mail contenant un risque <b>critique</b> est toujours retenu.</p>
          <div className="cats-cfg">{["engagement", "garantie", "reputation", "juridique", "confidentialite", "commercial"].map(k => <div className="cc" key={k}><div><b>{CAT(k).label}</b><p>{CAT(k).desc}</p></div><Toggle on={true} onChange={() => {}} label={CAT(k).label} /></div>)}</div>
        </Card>
      </div>
      <div className="row-end" style={{ maxWidth: 1080, margin: "0 auto", width: "100%" }}>{msg ? <span style={{ color: msg.includes("✓") ? "var(--r-low)" : "var(--r-crit)", fontWeight: 600, fontSize: 13 }}>{msg}</span> : null}<Btn kind="primary" icon="check" onClick={save} disabled={saving}>{saving ? "Enregistrement…" : "Enregistrer"}</Btn></div>
    </div>
  );
}

function AdminMail() {
  const [cfg, setCfg] = useState(null);
  const [on, setOn] = useState(false), [saving, setSaving] = useState(false), [copied, setCopied] = useState(false);
  const [test, setTest] = useState(null), [testing, setTesting] = useState(false), [scriptCopied, setScriptCopied] = useState(false);
  const runTest = () => { setTesting(true); setTest(null); api("/api/selftest", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" }).then(d => { setTesting(false); setTest(d); }).catch(() => { setTesting(false); setTest({ ok: false, net: true }); }); };
  const copyScript = () => fetch("connector-google.gs").then(r => r.text()).then(t => { navigator.clipboard.writeText(t); setScriptCopied(true); setTimeout(() => setScriptCopied(false), 2500); }).catch(() => {});
  useEffect(() => { api("/api/config").then(d => { if (d.ok) { setCfg(d.config); setOn(!!d.config.interception_on); } }); }, []);
  const genWebhook = () => { api("/api/config", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ gen_webhook: true }) }).then(() => api("/api/config").then(d => d.ok && setCfg(d.config))); };
  const toggleOn = (v) => { setOn(v); api("/api/config", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ interception_on: v }) }); };
  if (!cfg) return <div className="page"><div className="empty-load">Chargement…</div></div>;
  const tenant = (window.CYCLOPE_AUTH.user() || {}).email ? window.CYCLOPE_AUTH.user().email.split("@")[1] : "";
  const ingestUrl = (window.CYCLOPE_API || "") + "/ingest";
  return (
    <div className="page">
      <div className="page-h"><div><h1>Messagerie</h1><p>Cyclope s'intercale entre la messagerie de l'entreprise et le destinataire final.</p></div><div className="theme" style={{ border: 0 }}>Interception<Toggle on={on} onChange={toggleOn} label="Interception" /></div></div>
      <RelayGmail cfg={cfg} setCfg={setCfg} />
      <AnalysisScope cfg={cfg} setCfg={setCfg} />
      <Card title="Point d'entrée d'interception (méthodes alternatives)" sub="Utilisé par le connecteur Apps Script ou la passerelle SMTP ci-dessous. Inutile si vous utilisez le relais Gmail.">
        <Field label="URL d'ingestion" mono><div className="inp-row"><input readOnly value={ingestUrl} /><button className="ib" onClick={() => { navigator.clipboard.writeText(ingestUrl); setCopied(true); setTimeout(() => setCopied(false), 1500); }} aria-label="Copier"><Ic d="copy" s={15} /></button></div></Field>
        <Field label="Espace de travail (tenant)" mono><input readOnly value={tenant} /></Field>
        <Field label="Clé de webhook" mono hint="À joindre à chaque requête d'ingestion. Gardez-la secrète.">
          <div className="inp-row"><input readOnly value={cfg.webhook_key || "— non générée —"} /><button className="ib" onClick={genWebhook} aria-label="Régénérer"><Ic d="gear" s={15} /></button></div>
        </Field>
        {copied ? <p className="foot" style={{ color: "var(--r-low)" }}>URL copiée.</p> : null}
      </Card>
      <Card title="Alternative légère — connecteur Apps Script" sub="Sans compte de service, mais ne couvre que Gmail sur navigateur (pas le mobile) et n'attrape pas le bouton Envoyer natif." right={<Pill tone="n">Alternative</Pill>}>
        <div className="rules">
          <div className="rule"><div><b>1. Récupérez la clé de webhook ci-dessus</b><p>C'est la seule valeur secrète à reporter dans le script.</p></div></div>
          <div className="rule"><div><b>2. Installez le script fourni</b><p>Fichier <code>connector-google-appsscript/</code> (Code.gs + README). Collez-le dans script.google.com, renseignez domaine + clé, déployez en application web.</p></div></div>
          <div className="rule"><div><b>3. C'est branché</b><p>Vos e-mails sont analysés/corrigés avant envoi ; les validés partent automatiquement.</p></div><Pill tone="ok">≈ 5 min</Pill></div>
        </div>
        <div className="row-end" style={{ flexWrap: "wrap", gap: 10 }}>
          <a className="btn b-ghost" href="https://script.google.com/home/projects/create" target="_blank" rel="noopener"><Ic d="arrow" s={16} />Ouvrir Google Apps Script</a>
          <Btn icon="copy" onClick={copyScript}>{scriptCopied ? "Script copié ✓" : "Copier le script"}</Btn>
          <a className="btn b-ghost" href="connector-google.gs" download="Code.gs"><Ic d="arrow" s={16} />Télécharger le script</a>
          <Btn kind="primary" icon="check" onClick={runTest} disabled={testing}>{testing ? "Test en cours…" : "Tester la connexion"}</Btn>
        </div>
        {test ? <div className="testres">
          {test.net ? <div className="keystate no"><Ic d="alert" s={15} />Impossible de joindre Cyclope (réseau).</div> : <React.Fragment>
            <div className={"tr-line " + (test.webhook_ok ? "ok" : "no")}><Ic d={test.webhook_ok ? "check" : "x"} s={15} />Clé de webhook {test.webhook_ok ? "générée" : "manquante — cliquez sur l'engrenage pour la créer"}</div>
            <div className={"tr-line " + (test.ai_configured ? "ok" : "no")}><Ic d={test.ai_configured ? "check" : "x"} s={15} />Service d'IA {test.ai_configured ? "configuré" : "non configuré (onglet Intelligence artificielle)"}</div>
            {test.ai_configured ? <div className={"tr-line " + (test.ai_ok ? "ok" : "no")}><Ic d={test.ai_ok ? "check" : "x"} s={15} />{test.ai_ok ? `IA joignable — e-mail de test analysé (${test.sample.provider}/${test.sample.model}, verdict : ${test.sample.action === "hold" ? "retenu" : "envoyé"}, score ${test.sample.score}/100)` : `IA injoignable : ${test.detail || "erreur"}`}</div> : null}
            <div className="foot" style={{ marginTop: 8 }}>{test.webhook_ok && test.ai_ok ? "✓ Cyclope est prêt à recevoir les e-mails du connecteur." : "Corrigez les points ci-dessus puis relancez le test."}</div>
          </React.Fragment>}
        </div> : null}
        <p className="foot">Ce test vérifie la configuration Cyclope (clé + IA). Pour tester le script lui-même après installation, lancez la fonction <code>cyclopeSelfTest</code> depuis l'éditeur Apps Script, ou envoyez un e-mail depuis votre page Cyclope. Interception 100 % silencieuse de tout le flux : voir la passerelle avancée ci-dessous.</p>
      </Card>
      <Card title="Passerelle avancée (optionnelle)" sub="Interception silencieuse de tout le flux via smart-host SMTP. Identifiants chiffrés côté serveur (AES-256), jamais exposés aux consultants. À cadrer avec l'IT (délivrabilité).">
        <MailConn cfg={cfg} setCfg={setCfg} prov="google" label="Google Workspace" />
        <div style={{ height: 14 }} />
        <MailConn cfg={cfg} setCfg={setCfg} prov="microsoft" label="Microsoft 365" />
      </Card>
      <Card title="Comportement du filtrage" sub="Ce que Cyclope fait des e-mails analysés">
        <div className="rules">
          <div className="rule"><div><b>Aucun risque</b><p>Correction rédactionnelle puis envoi immédiat, sans intervention.</p></div><Pill tone="ok">Automatique</Pill></div>
          <div className="rule"><div><b>Risque ≥ seuil</b><p>Mise en attente dans l'espace consultant.</p></div><Pill tone="mid">Revue</Pill></div>
          <div className="rule"><div><b>Risque critique</b><p>Toujours retenu, quel que soit le seuil.</p></div><Pill tone="no">Revue</Pill></div>
          <div className="rule"><div><b>IA indisponible</b><p>L'e-mail n'est pas retenu (repli), l'incident est consigné.</p></div><Pill tone="n">Repli</Pill></div>
        </div>
      </Card>
    </div>
  );
}
function RelayGmail({ cfg, setCfg }) {
  const r = cfg.relay || {};
  const [mbx, setMbx] = useState(r.control_mailbox || "");
  const [sa, setSa] = useState("");
  const [saving, setSaving] = useState(false), [msg, setMsg] = useState("");
  const [test, setTest] = useState(null), [testing, setTesting] = useState(false);
  const save = () => {
    setSaving(true); setMsg("");
    const relay = { control_mailbox: mbx }; if (sa.trim()) relay.service_account = sa.trim();
    api("/api/config", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ config: { relay } }) })
      .then(d => { setSaving(false); if (d.ok) { setMsg("Enregistré ✓"); setSa(""); api("/api/config").then(x => x.ok && setCfg(x.config)); } else setMsg(d.error === "sa_invalid_json" ? "JSON du compte de service invalide" : d.error === "sa_missing_fields" ? "JSON incomplet (client_email / private_key manquants)" : "Erreur"); })
      .catch(() => { setSaving(false); setMsg("Erreur réseau"); });
  };
  const doTest = () => { setTesting(true); setTest(null); api("/api/relay-test", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" }).then(d => { setTesting(false); setTest(d); }).catch(() => { setTesting(false); setTest({ ok: false, error: "réseau" }); }); };
  return (
    <Card title="Relais Gmail — méthode retenue" sub="Interception réelle de tous les envois (web + mobile), sans serveur : Google redirige vers une boîte de contrôle → Cyclope analyse → renvoie via l'API Gmail au nom de l'expéditeur." right={<Pill tone={r.sa_set ? "ok" : "no"}>{r.sa_set ? "Compte de service OK" : "À configurer"}</Pill>}>
      <Field label="Boîte de contrôle" hint="Adresse interne vers laquelle Google redirige les envois à analyser."><input value={mbx} onChange={e => setMbx(e.target.value)} placeholder="controle@groupeverlaine.fr" /></Field>
      <Field label="Compte de service Google (JSON)" mono hint={r.sa_set ? "Enregistré (chiffré). Laissez vide pour le conserver ; recollez un JSON pour le remplacer." : "Collez le fichier JSON du compte de service (chiffré côté serveur, jamais réaffiché)."}>
        <textarea className="composer" style={{ minHeight: 110 }} value={sa} onChange={e => setSa(e.target.value)} placeholder={r.sa_set ? "•••• compte de service enregistré" : '{ "type":"service_account", "client_email":"…@….iam.gserviceaccount.com", "private_key":"-----BEGIN PRIVATE KEY-----\\n…" }'} spellCheck="false" />
      </Field>
      {r.sa_set ? <div className="keystate ok"><Ic d="check" s={15} />Compte de service enregistré ({r.sa_client_email}).</div> : <div className="keystate no"><Ic d="alert" s={15} />Aucun compte de service — le renvoi via Gmail est inactif.</div>}
      <div className="row-end" style={{ flexWrap: "wrap", gap: 10 }}>
        {msg ? <span style={{ color: msg.includes("✓") ? "var(--r-low)" : "var(--r-crit)", fontWeight: 600, fontSize: 13 }}>{msg}</span> : null}
        <Btn kind="primary" icon="check" onClick={save} disabled={saving}>{saving ? "…" : "Enregistrer"}</Btn>
        <Btn icon="mail" onClick={doTest} disabled={testing || !r.sa_set}>{testing ? "Test…" : "Tester le relais (m'envoyer un mail)"}</Btn>
      </div>
      {test ? (test.ok
        ? <div className="keystate ok"><Ic d="check" s={15} />Test réussi : un e-mail vient de t'être envoyé ({test.sent_to}) via l'API Gmail. Le renvoi « au nom de » fonctionne — vérifie ta boîte.</div>
        : <div className="keystate no"><Ic d="alert" s={15} />Échec du test : {test.error}</div>) : null}
    </Card>
  );
}
function AnalysisScope({ cfg, setCfg }) {
  const sc = cfg.analysis_scope || { mode: "all", list: [] };
  const [mode, setMode] = useState(sc.mode || "all");
  const [list, setList] = useState((sc.list || []).join("\n"));
  const [saving, setSaving] = useState(false), [msg, setMsg] = useState("");
  const save = () => {
    setSaving(true); setMsg("");
    const arr = list.split(/[\n,;]+/).map(x => x.trim()).filter(Boolean);
    api("/api/config", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ analysis_scope: { mode, list: arr } }) })
      .then(d => { setSaving(false); setMsg(d.ok ? "Enregistré ✓" : "Erreur"); api("/api/config").then(x => x.ok && setCfg(x.config)); })
      .catch(() => { setSaving(false); setMsg("Erreur réseau"); });
  };
  return (
    <Card title="Périmètre d'analyse" sub="Choisissez sur quels e-mails l'analyse s'applique. Les autres sont renvoyés tels quels, sans analyse.">
      <div className="rules">
        <label className="rule" style={{ cursor: "pointer" }}><div><b>Tous les e-mails</b><p>Chaque envoi sortant capté est analysé.</p></div><input type="radio" name="scope" checked={mode === "all"} onChange={() => setMode("all")} /></label>
        <label className="rule" style={{ cursor: "pointer" }}><div><b>Seulement ces expéditeurs</b><p>Analyse uniquement les adresses / domaines listés — idéal pour démarrer sur ton adresse seule.</p></div><input type="radio" name="scope" checked={mode === "include"} onChange={() => setMode("include")} /></label>
        <label className="rule" style={{ cursor: "pointer" }}><div><b>Tout le monde sauf ceux-ci</b><p>Analyse tout, sauf les adresses / domaines listés.</p></div><input type="radio" name="scope" checked={mode === "exclude"} onChange={() => setMode("exclude")} /></label>
      </div>
      {mode !== "all" ? <Field label="Adresses ou domaines (une par ligne)" mono hint="Ex. dylan.attia@groupeverlaine.fr, ou un domaine entier : groupeverlaine.fr"><textarea className="composer" style={{ minHeight: 90 }} value={list} onChange={e => setList(e.target.value)} placeholder={"dylan.attia@groupeverlaine.fr\ncommercial@groupeverlaine.fr"} spellCheck="false" /></Field> : null}
      <div className="row-end">{msg ? <span style={{ color: msg.includes("✓") ? "var(--r-low)" : "var(--r-crit)", fontWeight: 600, fontSize: 13 }}>{msg}</span> : null}<Btn kind="primary" icon="check" onClick={save} disabled={saving}>{saving ? "…" : "Enregistrer le périmètre"}</Btn></div>
    </Card>
  );
}
function MailConn({ cfg, setCfg, prov, label }) {
  const c = cfg[prov] || {};
  const [open, setOpen] = useState(false), [dom, setDom] = useState(c.domain || ""), [cid, setCid] = useState(c.client_id || ""), [sec, setSec] = useState(""), [saving, setSaving] = useState(false);
  const save = () => { setSaving(true); const b = { config: { [prov]: { domain: dom, client_id: cid } } }; if (sec) b.config[prov].client_secret = sec;
    api("/api/config", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(b) }).then(() => api("/api/config").then(d => { if (d.ok) setCfg(d.config); setSaving(false); setOpen(false); setSec(""); })); };
  const connected = c.secret_set && c.client_id;
  return (
    <div className="conn-blk">
      <div className="conn-hd"><div><b>{label}</b><span>{connected ? `Connecté · ${c.domain || ""}` : "Non configuré"}</span></div>{connected ? <Pill tone="ok">Connecté</Pill> : <Pill tone="n">Inactif</Pill>}<Btn size="s" onClick={() => setOpen(!open)}>{open ? "Fermer" : connected ? "Reconfigurer" : "Configurer"}</Btn></div>
      {open ? <div className="grid2" style={{ marginTop: 12 }}>
        <Field label="Domaine"><input value={dom} onChange={e => setDom(e.target.value)} placeholder="exemple.fr" /></Field>
        <Field label="Identifiant client (client ID)" mono><input value={cid} onChange={e => setCid(e.target.value)} placeholder={prov === "google" ? "…apps.googleusercontent.com" : "GUID application"} /></Field>
        <Field label="Secret client" mono hint={c.secret_set ? "Un secret est enregistré (chiffré). Laissez vide pour le conserver." : "Stocké chiffré (AES-256)."}><input type="password" value={sec} onChange={e => setSec(e.target.value)} placeholder={c.secret_set ? "•••• enregistré" : "••••••••"} /></Field>
        <div style={{ display: "flex", alignItems: "flex-end" }}><Btn kind="primary" icon="check" onClick={save} disabled={saving}>{saving ? "…" : "Enregistrer"}</Btn></div>
      </div> : null}
    </div>
  );
}

function AdminPeople() {
  const [list, setList] = useState(null);
  const [invite, setInvite] = useState(false), [em, setEm] = useState(""), [nm, setNm] = useState(""), [role, setRole] = useState("consultant"), [busy, setBusy] = useState(false);
  const load = () => api("/api/consultants").then(d => { if (d.ok) setList(d.users); });
  useEffect(() => { load(); }, []);
  const setRoleFor = (email, r) => api("/api/consultants", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "set_role", email, role: r }) }).then(load);
  const remove = (email) => api("/api/consultants", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "remove", email }) }).then(load);
  const add = () => { if (!em.includes("@")) return; setBusy(true); api("/api/consultants", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "set_role", email: em, name: nm, role }) }).then(() => { setBusy(false); setInvite(false); setEm(""); setNm(""); load(); }); };
  return (
    <div className="page">
      <div className="page-h"><div><h1>Consultants et accès</h1><p>Les consultants revoient les e-mails retenus. Ils n'ont accès à aucun réglage technique. Tous les comptes de votre domaine partagent cet espace.</p></div><Btn kind="primary" icon="users" onClick={() => setInvite(!invite)}>Ajouter un accès</Btn></div>
      {invite ? <Card title="Ajouter / promouvoir un accès" sub="Renseignez l'e-mail (même domaine que votre espace).">
        <div className="grid2">
          <Field label="Nom et prénom"><input value={nm} onChange={e => setNm(e.target.value)} placeholder="Prénom Nom" /></Field>
          <Field label="Adresse e-mail professionnelle"><input value={em} onChange={e => setEm(e.target.value)} placeholder="prenom.nom@votredomaine.fr" /></Field>
          <Field label="Rôle"><select value={role} onChange={e => setRole(e.target.value)}><option value="consultant">Consultant</option><option value="admin">Administrateur</option></select></Field>
        </div>
        <div className="row-end"><Btn onClick={() => setInvite(false)}>Annuler</Btn><Btn kind="primary" icon="check" onClick={add} disabled={busy}>Enregistrer</Btn></div>
      </Card> : null}
      {!list ? <div className="empty-load">Chargement…</div> : (
        <Card pad={false}><table className="tbl tbl-p"><thead><tr><th>Compte</th><th>Rôle</th><th>Depuis</th><th>Modifier</th></tr></thead>
          <tbody>{list.map(c => (
            <tr key={c.email}>
              <td><div className="who"><span className="av">{(c.name || c.email).split(/[\s.@]+/).filter(Boolean).slice(0, 2).map(w => w[0]).join("").toUpperCase()}</span><div><b>{c.name || c.email}</b><em>{c.email}</em></div></div></td>
              <td>{c.role === "admin" ? <Pill tone="pri">Administrateur</Pill> : <Pill tone="n">Consultant</Pill>}</td>
              <td>{c.created ? new Date(c.created).toLocaleDateString("fr-FR") : "—"}</td>
              <td><div style={{ display: "flex", gap: 8 }}>
                {c.role === "admin" ? <Btn size="s" onClick={() => setRoleFor(c.email, "consultant")}>Passer consultant</Btn> : <Btn size="s" onClick={() => setRoleFor(c.email, "admin")}>Passer admin</Btn>}
                <Btn size="s" kind="danger" onClick={() => remove(c.email)}>Retirer</Btn>
              </div></td>
            </tr>
          ))}</tbody></table></Card>
      )}
    </div>
  );
}

Object.assign(window, { Queue, Review, Corrections, AdminOverview, AdminAI, AdminMail, AdminPeople, CatTag, RiskTag });
