/* Pilotage — diagnostic sobre + bibliothèque (Liste, Familles sino, Phrases audio). */

function Donut({ data, size = 120 }) {
  const total = data.reduce((a, d) => a + d.n, 0);
  const colors = ["var(--accent)", "var(--ochre)", "var(--good)", "var(--warn)", "var(--ink-faint)"];
  let acc = 0;
  const segs = data.map((d, i) => { const start = acc / total; acc += d.n; return { ...d, start, end: acc / total, color: colors[i % colors.length] }; });
  const grad = segs.map(s => `${s.color} ${s.start * 360}deg ${s.end * 360}deg`).join(", ");
  return (
    <div style={{ display: "flex", gap: 16, alignItems: "center", flexWrap: "wrap" }}>
      <div style={{ width: size, height: size, borderRadius: 999, background: `conic-gradient(${grad})`, flexShrink: 0, position: "relative" }}>
        <div style={{ position: "absolute", inset: "26%", borderRadius: 999, background: "var(--card)", display: "grid", placeItems: "center" }}><span style={{ fontFamily: "var(--serif)", fontSize: 20, color: "var(--ink)" }}>{total}</span></div>
      </div>
      <div style={{ display: "grid", gap: 6 }}>{segs.map((s, i) => <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}><span style={{ width: 10, height: 10, borderRadius: 3, background: s.color }} /><span style={{ flex: 1 }}>{s.t || s.m}</span><span className="faint" style={{ fontFamily: "var(--mono)" }}>{s.n}</span></div>)}</div>
    </div>
  );
}

/* ---- Bibliothèque : Liste (réelle, sur vocab_freq + état SRS) ---- */
const STATUS_FR = { tous: "Tous", vus: "Vus", nonvus: "Non vus" };
function ListeView({ onBack, initialQuery = "", initialStatus = "" }) {
  const settings = window.LangStore ? window.LangStore.settings.get() : {};
  const pageSize = Math.max(10, Number(settings.listSize) || 50);
  const [payload, setPayload] = useState(null);
  const [q, setQ] = useState(initialQuery || "");
  const [status, setStatus] = useState(() => initialStatus || STATUS_FR[settings.statusFilter] || "Tous");
  useEffect(() => {
    if (!window.LangData || !window.LangData.loadVocabFreq) { setPayload({ items: [] }); return undefined; }
    let alive = true;
    window.LangData.loadVocabFreq().then(p => { if (alive) setPayload(p); }).catch(() => { if (alive) setPayload({ items: [] }); });
    return () => { alive = false; };
  }, []);
  useEffect(() => { setQ(initialQuery || ""); }, [initialQuery]);
  useEffect(() => { if (initialStatus) setStatus(initialStatus); }, [initialStatus]);

  if (!payload) return <div style={{ display: "grid", gap: "var(--gap)" }}><Btn kind="ghost" size="sm" icon="prev" onClick={onBack}>Pilotage</Btn><LoadingCard lines={5} /></div>;

  const cards = window.LangStore && window.LangStore.cards ? window.LangStore.cards : null;
  const rows = (payload.items || []).map(it => {
    const st = cards ? cards.state(it.id) : null;
    return {
      kr: it.kr, ro: it.reading || it.ro || "",
      fr: it.fr || it.meaning_fr || "",
      level: it.level || it.niveau_cible || "—",
      pos: it.classe || it.pos || it.part_of_speech || "",
      box: st ? (st.box || 1) : null,
      seen: !!st,
    };
  });
  const matched = rows.filter(it => (status === "Tous" || (status === "Vus" ? it.seen : !it.seen)) && (q === "" || it.kr.includes(q) || it.fr.toLowerCase().includes(q.toLowerCase())));
  const shown = matched.slice(0, pageSize);
  return (
    <div style={{ display: "grid", gap: "var(--gap)" }}>
      <div style={{ display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap" }}>
        <Btn kind="ghost" size="sm" icon="prev" onClick={onBack}>Pilotage</Btn>
        <div style={{ display: "flex", alignItems: "center", gap: 8, flex: 1, minWidth: 180, padding: "8px 12px", borderRadius: 999, border: "1px solid var(--rule)", background: "var(--card)" }}>
          <Icon name="search" size={16} style={{ color: "var(--ink-faint)" }} />
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Rechercher un mot…" style={{ border: "none", background: "transparent", outline: "none", flex: 1, fontFamily: "var(--sans)", fontSize: 14, color: "var(--ink)" }} />
        </div>
        <FilterChips options={["Tous", "Vus", "Non vus"]} value={status} onChange={setStatus} />
      </div>
      <div className="card" style={{ overflow: "hidden" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1.2fr 1fr 0.8fr 0.6fr 0.7fr", gap: 8, padding: "11px 16px", borderBottom: "1px solid var(--rule)", background: "var(--card-2)" }}>
          {["Coréen", "Sens", "Niveau", "Type", "Boîte"].map(h => <span key={h} className="eyebrow" style={{ fontSize: 10.5 }}>{h}</span>)}
        </div>
        {shown.length === 0 ? <EmptyState title="Aucun résultat" body="Essayez un autre terme ou changez le filtre." /> : shown.map((it, i) => (
          <div key={i} style={{ display: "grid", gridTemplateColumns: "1.2fr 1fr 0.8fr 0.6fr 0.7fr", gap: 8, padding: "11px 16px", borderBottom: "1px solid var(--rule-soft)", alignItems: "center" }}>
            <span className="kr" style={{ fontSize: 17, color: "var(--accent-deep)" }}>{it.kr} <span className="faint" style={{ fontFamily: "var(--mono)", fontSize: 11 }}>{it.ro}</span></span>
            <span style={{ fontSize: 14 }}>{it.fr}</span>
            <span className="faint" style={{ fontSize: 12.5 }}>{it.level}</span>
            <span className="faint" style={{ fontSize: 12.5 }}>{it.pos}</span>
            {it.box ? <span className="chip" style={{ fontSize: 11, justifySelf: "start" }}>B{it.box}</span> : <span className="faint" style={{ fontSize: 12, justifySelf: "start" }}>—</span>}
          </div>
        ))}
      </div>
      <p className="faint" style={{ fontSize: 12.5, margin: 0 }}>{shown.length} affichés sur {matched.length} résultats · {(payload.items || []).length.toLocaleString("fr-FR")} entrées au total.</p>
    </div>
  );
}

/* ---- Bibliothèque : Familles sino (réelles, sur chunk_index.roots) ---- */
const SINO_MAX_FAMILIES = 40;
const SINO_MAX_WORDS = 8;
function SinoView({ onBack }) {
  const [fams, setFams] = useState(null);
  useEffect(() => {
    if (!window.LangData || !window.LangData.loadChunkIndex) { setFams([]); return undefined; }
    let alive = true;
    Promise.all([window.LangData.loadChunkIndex(), window.LangData.loadVocabFreq()])
      .then(([idx, voc]) => {
        if (!alive) return;
        const byId = Object.create(null);
        (voc.items || []).forEach(it => { byId[it.id] = it; });
        const built = Object.keys(idx.roots || {})
          .map(root => ({
            root,
            words: (idx.roots[root] || []).map(id => byId[id]).filter(Boolean).map(it => [it.kr, it.fr || it.meaning_fr || ""]),
          }))
          .filter(f => f.words.length >= 2)
          .sort((a, b) => b.words.length - a.words.length)
          .slice(0, SINO_MAX_FAMILIES);
        setFams(built);
      })
      .catch(() => { if (alive) setFams([]); });
    return () => { alive = false; };
  }, []);

  if (!fams) return <div style={{ display: "grid", gap: "var(--gap)" }}><Btn kind="ghost" size="sm" icon="prev" onClick={onBack}>Pilotage</Btn><LoadingCard lines={5} /></div>;

  return (
    <div style={{ display: "grid", gap: "var(--gap)" }}>
      <div style={{ display: "flex", gap: 12, alignItems: "center" }}><Btn kind="ghost" size="sm" icon="prev" onClick={onBack}>Pilotage</Btn><span className="faint" style={{ fontSize: 13 }}>Racines hanja partagées · {fams.length} familles (les plus larges)</span></div>
      {fams.length === 0 ? <EmptyState icon="book" title="Familles indisponibles" body="L'index de chunks n'a pas pu être chargé." /> : (
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(260px,1fr))", gap: "var(--gap)" }}>
          {fams.map((f, i) => (
            <div key={i} className="card" style={{ padding: "var(--pad-card)", display: "grid", gap: 12 }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}><span className="kr" style={{ fontSize: 26, color: "var(--accent-deep)" }}>{f.root}</span><span className="muted" style={{ fontSize: 13 }}>{f.words.length} mots</span></div>
              <div style={{ display: "grid", gap: 6 }}>{f.words.slice(0, SINO_MAX_WORDS).map((w, k) => <div key={k} style={{ display: "flex", justifyContent: "space-between", gap: 8, padding: "7px 11px", borderRadius: 8, background: "var(--card-2)", border: "1px solid var(--rule)" }}><span className="kr" style={{ fontSize: 15 }}>{w[0]}</span><span className="faint" style={{ fontSize: 13, textAlign: "right" }}>{w[1]}</span></div>)}</div>
              {f.words.length > SINO_MAX_WORDS && <span className="faint" style={{ fontSize: 12 }}>+ {f.words.length - SINO_MAX_WORDS} autres</span>}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ---- Bibliothèque : Phrases audio / Audio validé ---- */
function PhrasesView({ onBack }) {
  const phrases = window.LANGUES_DATA.audioPhrases;
  const [onlyValid, setOnlyValid] = useState(false);
  const [active, setActive] = useState(null);
  const [toast, setToast] = useState(null);
  const shown = onlyValid ? phrases.filter(p => p.validated) : phrases;
  const play = (p, i) => {
    if (active === i) {
      setActive(null);
      if (window.LangAudio) window.LangAudio.stop();
      return;
    }
    setActive(i);
    if (window.LangAudio) window.LangAudio.speak(p.kr);
  };
  const report = (p) => { setToast(`Phrase signalée : ${p.kr}`); setTimeout(() => setToast(null), 2200); };
  return (
    <div style={{ display: "grid", gap: "var(--gap)" }}>
      <div style={{ display: "flex", gap: 12, alignItems: "center", justifyContent: "space-between", flexWrap: "wrap" }}>
        <Btn kind="ghost" size="sm" icon="prev" onClick={onBack}>Pilotage</Btn>
        <button onClick={() => setOnlyValid(v => !v)} className="chip" style={{ cursor: "pointer", padding: "8px 13px", background: onlyValid ? "var(--accent-tint)" : "var(--card-2)", color: onlyValid ? "var(--accent-deep)" : "var(--ink-soft)" }}><Icon name="check" size={14} /> Audio validé seulement</button>
      </div>
      <p className="faint" style={{ fontSize: 13, margin: 0 }}>{window.LANGUES_DATA.corpus.audioPhrases} phrases audio · l'audio est intégré au fil de l'apprentissage, sans validation globale bloquante.</p>
      <div style={{ display: "grid", gap: 8 }}>
        {shown.map((p, i) => (
          <div key={i} className="card" style={{ padding: "12px 16px", display: "flex", gap: 12, alignItems: "center" }}>
            <button onClick={() => play(p, i)} style={{ width: 38, height: 38, borderRadius: 999, border: "none", background: active === i ? "var(--accent)" : "var(--accent-tint)", color: active === i ? "#fff" : "var(--accent-deep)", cursor: "pointer", display: "grid", placeItems: "center", flexShrink: 0 }}><Icon name={active === i ? "pause" : "play"} size={17} /></button>
            <div style={{ flex: 1 }}><div className="kr" style={{ fontSize: 17 }}>{p.kr}</div><div className="faint" style={{ fontSize: 12.5 }}>{p.fr}</div></div>
            {p.validated ? <span className="chip" style={{ fontSize: 11, background: "var(--good-tint)", color: "var(--good)", borderColor: "transparent" }}><Icon name="check" size={12} /> validé</span> : <Btn kind="ghost" size="sm" icon="flag" onClick={() => report(p)}>Signaler</Btn>}
          </div>
        ))}
      </div>
      {toast && <div className="fade-up" style={{ position: "fixed", bottom: 24, left: "50%", transform: "translateX(-50%)", background: "var(--ink)", color: "var(--paper)", padding: "11px 18px", borderRadius: 999, fontSize: 14, fontWeight: 600, zIndex: 60, boxShadow: "var(--shadow-pop)" }}>{toast}</div>}
    </div>
  );
}

function Panel({ title, hint, children }) {
  return <div className="card" style={{ padding: "var(--pad-card)" }}><div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}><Eyebrow>{title}</Eyebrow>{hint && <span className="faint" style={{ fontSize: 12 }}>{hint}</span>}</div>{children}</div>;
}

const STRAND_LADDER = ["study", "input", "output", "fluency", "meta"];
const STRAND_FR = { study: "Étude", input: "Input", output: "Production", fluency: "Fluidité", meta: "Méta" };
const INTENTION_FR = { reviser: "Révision", decouvrir: "Découverte", ecouter: "Écoute", dialoguer: "Dialogue", produire: "Production", reparer: "Réparation" };

/* Lit l'état réel : journal de séances (aggregate) + cartes FSRS (boîtes, dues). */
function liveStats() {
  const store = window.LangStore;
  const Log = window.LangSessionLog;
  if (!store || !Log) return null;
  const agg = Log.aggregate(store);
  const cards = store.cards.all();
  const ids = Object.keys(cards);
  const now = Date.now();
  const due = store.cards.due ? store.cards.due(now).length : ids.filter(id => (cards[id].due || 0) <= now).length;
  const boxFor = (st) => st.box || (window.FSRS && window.FSRS.boxFromStability ? window.FSRS.boxFromStability(st.stability) : 1);
  const boxes = [1, 2, 3, 4, 5].map(box => ({ box, n: ids.filter(id => boxFor(cards[id]) === box).length }));
  const recent = Log.recent(store, 8).slice().reverse().map(s => {
    const sum = s.summary || {};
    return {
      date: s.finishedAt ? new Date(s.finishedAt).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit" }) : "—",
      kind: INTENTION_FR[s.intention] || s.templateId || "Séance",
      min: Math.round((sum.minutesActive) || 0),
      cards: (sum.counts && sum.counts.reviewed) || 0,
    };
  });
  return { agg, cardsTracked: ids.length, due, boxes, recent };
}

function PilotageEmpty({ go, setView }) {
  return (
    <div style={{ maxWidth: 1040, margin: "0 auto", display: "grid", gap: "var(--gap-lg)" }}>
      <header className="fade-up">
        <Eyebrow>Tableau de bord</Eyebrow>
        <h1 style={{ fontSize: 44, margin: "6px 0 0" }}>Pilotage</h1>
      </header>
      <EmptyState icon="chart" title="Pas encore de données" body="Lancez une séance depuis Aujourd'hui : vos cartes revues, votre temps par brin et vos erreurs nommées s'afficheront ici." action={<Btn kind="primary" size="sm" icon="today" onClick={() => go("today")}>Lancer une séance</Btn>} />
      <div>
        <Eyebrow style={{ marginBottom: 12 }}>Bibliothèque</Eyebrow>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(230px,1fr))", gap: 10 }}>
          <Tile icon="list" label="Liste des items" meta="Paginée · filtres · recherche" onClick={() => setView("liste")} />
          <Tile icon="book" label="Familles sino" meta="Racines partagées · lecture seule" onClick={() => setView("sino")} />
          <Tile icon="speaker" label="Phrases audio" meta="Audio au fil de l'apprentissage" onClick={() => setView("phrases")} />
        </div>
      </div>
    </div>
  );
}

function PilotagePage({ go, routeParams = {} }) {
  const D = window.LANGUES_DATA;
  const [view, setView] = useState(routeParams.view || "dashboard");
  useEffect(() => { if (routeParams.view) setView(routeParams.view); }, [routeParams.view]);
  if (view === "liste") return <div style={{ maxWidth: 1000, margin: "0 auto" }}><ListeView onBack={() => setView("dashboard")} initialQuery={routeParams.query || ""} initialStatus={routeParams.status || ""} /></div>;
  if (view === "sino") return <div style={{ maxWidth: 1000, margin: "0 auto" }}><SinoView onBack={() => setView("dashboard")} /></div>;
  if (view === "phrases") return <div style={{ maxWidth: 1000, margin: "0 auto" }}><PhrasesView onBack={() => setView("dashboard")} /></div>;

  const live = liveStats();
  const hasData = live && (live.agg.sessions > 0 || live.cardsTracked > 0);
  if (!hasData) return <PilotageEmpty go={go} setView={setView} />;

  const agg = live.agg;
  const maxBox = Math.max(...live.boxes.map(b => b.n), 1);
  const maxStrand = Math.max(...STRAND_LADDER.map(k => agg.strands[k] || 0), 1);
  const resultDonut = [
    { t: "Réussites", n: agg.correct },
    { t: "Erreurs", n: agg.errors },
  ].filter(d => d.n > 0);
  const accuracyPct = agg.accuracy != null ? Math.round(agg.accuracy * 100) : null;

  return (
    <div style={{ maxWidth: 1040, margin: "0 auto", display: "grid", gap: "var(--gap-lg)" }}>
      <header className="fade-up">
        <Eyebrow>Tableau de bord</Eyebrow>
        <h1 style={{ fontSize: 44, margin: "6px 0 0" }}>Pilotage</h1>
        <p className="muted" style={{ fontSize: 16, margin: "10px 0 0", maxWidth: 560 }}>Lecture réelle de votre pratique — issue de vos séances et de l'état SRS de vos cartes.</p>
      </header>

      <StatStrip items={[
        { value: live.cardsTracked.toLocaleString("fr-FR"), label: "Cartes en SRS" },
        { value: live.due, label: "Dues maintenant", accent: true },
        { value: agg.sessions, label: "Séances" },
        { value: `${agg.minutes} min`, label: "Temps d'étude" },
      ]} />

      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(300px,1fr))", gap: "var(--gap)" }}>
        <Panel title="Temps par brin" hint="Four Strands">
          {STRAND_LADDER.every(k => !agg.strands[k]) ? <p className="faint" style={{ fontSize: 13, margin: 0 }}>Pas encore de temps enregistré.</p> : (
            <div style={{ display: "grid", gap: 13 }}>
              {STRAND_LADDER.map(k => (
                <div key={k}>
                  <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 5, gap: 8 }}><span style={{ fontSize: 14, fontWeight: 600 }}>{STRAND_FR[k]}</span><span className="faint" style={{ fontSize: 13, fontFamily: "var(--mono)" }}>{agg.strands[k] || 0}′</span></div>
                  <Progress value={(agg.strands[k] || 0) / maxStrand} height={6} />
                </div>
              ))}
            </div>
          )}
        </Panel>
        <Panel title="Répartition Leitner" hint="état FSRS réel">
          {live.cardsTracked === 0 ? <p className="faint" style={{ fontSize: 13, margin: 0 }}>Aucune carte encore notée.</p> : (
            <div style={{ display: "flex", alignItems: "flex-end", gap: 12, height: 150 }}>
              {live.boxes.map(b => (
                <div key={b.box} style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 8, height: "100%", justifyContent: "flex-end" }}>
                  <span className="faint" style={{ fontSize: 12, fontFamily: "var(--mono)" }}>{b.n}</span>
                  <div style={{ width: "100%", height: `${(b.n / maxBox) * 100}%`, minHeight: b.n ? 3 : 0, background: b.box >= 4 ? "var(--good)" : b.box >= 2 ? "var(--accent)" : "var(--ochre)", borderRadius: "5px 5px 0 0", transition: "height .5s" }} />
                  <span className="faint" style={{ fontSize: 12 }}>B{b.box}</span>
                </div>
              ))}
            </div>
          )}
        </Panel>
        <Panel title="Réussite" hint={accuracyPct != null ? `${accuracyPct}%` : ""}>
          {resultDonut.length ? <Donut data={resultDonut} /> : <p className="faint" style={{ fontSize: 13, margin: 0 }}>Pas encore d'exercice noté correct/incorrect.</p>}
        </Panel>
        <Panel title="Production" hint="brin output">
          <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
            <span style={{ fontFamily: "var(--serif)", fontSize: 38, color: "var(--accent)" }}>{agg.produced}</span>
            <span className="faint" style={{ fontSize: 13 }}>productions journalisées</span>
          </div>
          <p className="faint" style={{ fontSize: 12.5, margin: "10px 0 0" }}>{agg.reviewed} cartes revues au total · {agg.errors} erreurs.</p>
        </Panel>
      </div>

      {/* Points faibles réels (tags d'erreur) */}
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}><Eyebrow>Points faibles à réparer</Eyebrow><Btn kind="ghost" size="sm" iconR="chevronR" onClick={() => go("today")}>Séance ciblée</Btn></div>
        {agg.topErrors && agg.topErrors.length ? (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(260px,1fr))", gap: 10 }}>
            {agg.topErrors.map(w => (
              <div key={w.tag} className="card" style={{ padding: "13px 16px", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10 }}>
                <div><div style={{ fontSize: 14.5, fontWeight: 600, textTransform: "capitalize" }}>{w.tag}</div><div className="faint" style={{ fontSize: 12 }}>{w.count} occurrence{w.count > 1 ? "s" : ""}</div></div>
                <span className="chip" style={{ fontSize: 11, background: "var(--warn-tint)", color: "var(--warn)", borderColor: "transparent" }}>erreur</span>
              </div>
            ))}
          </div>
        ) : <p className="faint" style={{ fontSize: 13.5, margin: 0 }}>Aucune erreur nommée pour l'instant — les dictées et corrections en généreront.</p>}
      </div>

      <Collapsible eyebrow="Historique" title="Dernières séances" hint={`${live.recent.length} séance${live.recent.length > 1 ? "s" : ""}`} defaultOpen>
        {live.recent.length ? (
          <div style={{ display: "grid", gap: 8 }}>
            {live.recent.map((s, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 14, padding: "11px 14px", borderRadius: "var(--radius-sm)", background: "var(--card-2)", border: "1px solid var(--rule)" }}>
                <span className="faint" style={{ fontSize: 13, width: 52, fontFamily: "var(--mono)" }}>{s.date}</span>
                <span style={{ flex: 1, fontWeight: 600, fontSize: 14 }}>{s.kind}</span>
                <span className="faint" style={{ fontSize: 13 }}>{s.min} min</span>
                <span className="chip" style={{ fontSize: 11.5 }}>{s.cards} cartes</span>
              </div>
            ))}
          </div>
        ) : <p className="faint" style={{ fontSize: 13, margin: 0 }}>Aucune séance terminée encore.</p>}
      </Collapsible>

      <div>
        <Eyebrow style={{ marginBottom: 12 }}>Bibliothèque</Eyebrow>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(230px,1fr))", gap: 10 }}>
          <Tile icon="list" label="Liste des items" meta="Paginée · filtres · recherche" onClick={() => setView("liste")} />
          <Tile icon="book" label="Familles sino" meta="Racines partagées · lecture seule" onClick={() => setView("sino")} />
          <Tile icon="speaker" label="Phrases audio" meta="74 phrases · audio validé" onClick={() => setView("phrases")} />
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { PilotagePage });
