/* ============================================================
   wordsquest/wordsquest-screens.jsx — экраны «Английские слова»:
   обзор категорий → настройка сессии → сессия повторения → итоги.
   Регистрирует window.Screens.questsWords.
   props из шелла: { nav, tweaks } (не используются напрямую)
   ============================================================ */
const React = window.React;
const { useState, useMemo, useRef } = React;
const Icon = window.Icon;

const WQ_MODES = [
  { id: "mixed", label: "Смешанная", icon: "swords", hint: "Форматы чередуются от слова к слову: карточки, выбор, ввод, аудио, пропуск в предложении и по картинке — сессия не даёт заскучать." },
  { id: "cards", label: "Карточки", icon: "book", hint: "Вспомни перевод сам, затем оцени, насколько легко это удалось." },
  { id: "choice", label: "Выбор варианта", icon: "list", hint: "Выбери правильный перевод среди четырёх вариантов." },
  { id: "type", label: "Ввод перевода", icon: "edit", hint: "Впиши слово по-английски по русскому значению." },
  { id: "cloze", label: "Пропуск в предложении", icon: "quote", hint: "Впиши пропущенное слово в примере — учишь слово сразу в контексте." },
  { id: "audio", label: "Аудирование", icon: "play", hint: "Прослушай слово и впиши его — восприятие на слух плюс написание." },
  { id: "image", label: "По картинке", icon: "grid", hint: "Вспомни слово по картинке-ассоциации (для слов, к которым добавлена картинка)." },
];

function shuffle(arr) { return [...arr].sort(() => Math.random() - 0.5); }

function pickDistractors(word, dir, count) {
  /* берём слова из состояния, иначе правки и свои карточки не попадут в варианты */
  const cats = window.WQLookup.categories(window.WQStore.get());
  const own = cats.find((c) => c.id === word.catId);
  const pool = own && own.words.length > count + 1 ? own.words : cats.flatMap((c) => c.words);
  const field = dir === "ru" ? "en" : "ru";
  const options = pool.filter((w) => w.id !== word.id).map((w) => w[field]);
  /* у разных слов бывает одинаковый перевод — дубли среди вариантов недопустимы */
  const uniq = [...new Set(options)].filter((v) => v !== (dir === "ru" ? word.en : word.ru));
  return shuffle(uniq).slice(0, count);
}

/* пропуск в предложении: ищем слово в примере и возвращаем части вокруг него.
   Сначала точное вхождение целиком, потом — слово с суффиксом (nudge→nudged,
   glance→glances). Если ни то ни другое не нашлось — cloze не применим. */
function clozeMatch(word) {
  const ex = word && word.ex;
  if (!ex || !word.en) return null;
  const esc = word.en.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  let re = new RegExp("\\b" + esc + "\\b", "i");
  let m = re.exec(ex);
  if (!m && !/\s/.test(word.en)) { re = new RegExp("\\b" + esc + "\\w*\\b", "i"); m = re.exec(ex); }
  if (!m) return null;
  return { before: ex.slice(0, m.index), hidden: m[0], after: ex.slice(m.index + m[0].length) };
}

/* какой формат показать для слова в смешанной сессии / с запасным вариантом.
   Возвращает { fmt, dir }. Форматы, которым нужен ассет (картинка/пример),
   применяются только к подходящим словам; иначе — разумный запасной. */
function planItem(word, mode, sessionDir) {
  const hasImg = !!((window.WQStore.get().images || {})[word.id]);
  const canCloze = !!clozeMatch(word);
  if (mode === "mixed") {
    const pool = ["cards", "choice", "type", "audio"];
    if (canCloze) pool.push("cloze");
    if (hasImg) pool.push("image");
    const fmt = pool[Math.floor(Math.random() * pool.length)];
    const dir = (fmt === "cards" || fmt === "choice") ? (Math.random() < 0.5 ? "en" : "ru") : sessionDir;
    return { fmt, dir };
  }
  if (mode === "cloze" && !canCloze) return { fmt: "type", dir: "ru" };   /* нет примера с этим словом */
  if (mode === "image" && !hasImg) return { fmt: "cards", dir: sessionDir }; /* нет картинки */
  return { fmt: mode, dir: sessionDir };
}
/* формат сам «выдаёт» английское слово (ввод/аудио/пропуск) — направление ему не нужно */
const WQ_EN_OUTPUT = { type: 1, cloze: 1, audio: 1 };

/* ---- обзор: хаб категорий ------------------------------------------------ */
function WordsHub({ onStart, onOpenList }) {
  const s = window.useWQ();
  const L = window.WQLookup;
  const overall = L.overallStats(s);
  const [confirmReset, setConfirmReset] = useState(false);
  const [addingCat, setAddingCat] = useState(false);
  const [catName, setCatName] = useState("");

  const submitCat = () => {
    const id = window.WQActions.addCategory(catName);
    if (id) { setCatName(""); setAddingCat(false); onOpenList(id); }
  };

  return (
    <div>
      <div className="wq-hero">
        <div className="wq-hero__main">
          <div className="wq-hero__row">
            <div className="wq-hero__stat"><span className="wq-hero__num">{s.xp}</span><span className="e-label">XP</span></div>
            <window.WQStreakBadge streak={s.streak} />
            <div className="wq-hero__stat"><span className="wq-hero__num">{overall.learned}</span><span className="e-label">из {overall.total} выучено</span></div>
            {overall.due > 0 && <div className="wq-hero__stat wq-hero__stat--due"><span className="wq-hero__num">{overall.due}</span><span className="e-label">к повторению</span></div>}
          </div>
          <window.WQBar value={overall.learned} total={overall.total} />
        </div>
        <div className="row-2 gap-2" style={{ flexWrap: "wrap" }}>
          {/* вход в словарь: раньше это была мелкая ссылка в подвале карточки
              категории и её попросту не находили */}
          <button className="wq-btn" onClick={() => onOpenList(null)}>
            <Icon name="list" size={14} />Все карточки
          </button>
          {overall.due > 0 && (
            <button className="wq-btn wq-btn--signal" onClick={() => onStart(null)}>
              <Icon name="flame" size={14} />Повторить всё ({overall.due})
            </button>
          )}
        </div>
      </div>

      <window.SectionRule kicker={L.categories(s).length + " категорий · " + overall.total + " слов"}>Категории</window.SectionRule>

      <div className="wq-addcat">
        {addingCat ? (
          <form className="wq-addcat__form" onSubmit={(e) => { e.preventDefault(); submitCat(); }}>
            <input autoFocus className="wq-input" value={catName} onChange={(e) => setCatName(e.target.value)}
              placeholder="Название категории, например «Медицина»" />
            <button className="wq-btn wq-btn--ink" type="submit" disabled={!catName.trim()}>Создать</button>
            <button className="wq-btn" type="button" onClick={() => { setAddingCat(false); setCatName(""); }}>Отмена</button>
          </form>
        ) : (
          <button className="wq-btn wq-btn--sm" onClick={() => setAddingCat(true)}><Icon name="plus" size={13} />Своя категория</button>
        )}
      </div>

      <div className="wq-cats">
        {L.categories(s).map((cat) => {
          const stats = L.categoryStats(s, cat);
          /* у пустой своей категории заниматься нечем — ведём в список для добавления слов */
          const primary = (cat.custom && stats.total === 0) ? () => onOpenList(cat.id) : () => onStart(cat.id);
          return <window.WQCategoryCard key={cat.id} cat={cat} stats={stats}
            onClick={primary} onOpenList={() => onOpenList(cat.id)} />;
        })}
      </div>

      <div className="wq-reset">
        {confirmReset ? (
          <React.Fragment>
            <span className="e-label" style={{ color: "var(--text-muted)" }}>Точно сбросить весь прогресс?</span>
            <button className="wq-btn wq-btn--sm" onClick={() => { window.WQActions.resetAll(); setConfirmReset(false); }}>Да, сбросить</button>
            <button className="wq-btn wq-btn--sm" onClick={() => setConfirmReset(false)}>Отмена</button>
          </React.Fragment>
        ) : (
          <button className="wq-link" onClick={() => setConfirmReset(true)}>Сбросить прогресс изучения слов</button>
        )}
      </div>
    </div>
  );
}

/* ---- форма карточки: используется и для правки, и для новой карточки ---- */
function WordForm({ initial, onSave, onCancel }) {
  const [en, setEn] = useState((initial && initial.en) || "");
  const [ru, setRu] = useState((initial && initial.ru) || "");
  const [ts, setTs] = useState((initial && initial.ts) || "");
  const [ex, setEx] = useState((initial && initial.ex) || "");
  const ok = en.trim() && ru.trim();

  return (
    <div className="wq-form">
      <label className="wq-field">
        <span className="e-label">Слово по-английски</span>
        <input autoFocus className="wq-input" value={en} onChange={(e) => setEn(e.target.value)} placeholder="word" />
      </label>
      <label className="wq-field">
        <span className="e-label">Перевод</span>
        <input className="wq-input" value={ru} onChange={(e) => setRu(e.target.value)} placeholder="перевод" />
      </label>
      <label className="wq-field">
        <span className="e-label">Транскрипция — необязательно</span>
        <input className="wq-input" value={ts} onChange={(e) => setTs(e.target.value)} placeholder="wɜːd" />
      </label>
      <label className="wq-field">
        <span className="e-label">Пример — необязательно</span>
        <textarea className="wq-input" rows={2} value={ex} onChange={(e) => setEx(e.target.value)} placeholder="This is an example sentence." />
      </label>
      <div className="row-2 gap-2" style={{ justifyContent: "center", flexWrap: "wrap" }}>
        <button className="wq-btn wq-btn--ink" disabled={!ok} onClick={() => ok && onSave({ en, ru, ts, ex })}>Сохранить</button>
        <button className="wq-btn" onClick={onCancel}>Отмена</button>
      </div>
    </div>
  );
}

/* ---- карточка слова: разбор + правка + картинка + прогресс ------------- */
function WordDetail({ word, onClose, startInEdit }) {
  const s = window.useWQ();
  const L = window.WQLookup;
  /* берём слово из состояния, чтобы правки были видны сразу после сохранения */
  const w = L.word(s, word.id) || word;
  const st = L.wordStatus(s, w.id);
  const img = (s.images || {})[w.id];
  const fileRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const [editing, setEditing] = useState(!!startInEdit);
  const p = st.prog;
  const dm = (iso) => (iso ? iso.slice(8, 10) + "." + iso.slice(5, 7) + "." + iso.slice(0, 4) : "—");
  const cat = window.WQCategories.find((c) => c.id === w.catId);
  const isCustom = L.isCustom(w.id);
  const isEdited = !isCustom && !!(s.edits || {})[w.id];

  const pick = async (file) => {
    if (!file) return;
    setBusy(true);
    try { window.WQActions.setWordImage(w.id, await window.WQFileToDataURL(file)); }
    finally { setBusy(false); }
  };

  return (
    <div className="wq-overlay" onClick={onClose}>
      <div className="wq-modal" onClick={(e) => e.stopPropagation()}>
        <div className="wq-modal__bar">
          <span className="e-label">{cat ? cat.title : "Слово"}{isCustom ? " · своя карточка" : isEdited ? " · изменена" : ""}</span>
          <button className="wq-modal__x" onClick={onClose} title="Закрыть"><Icon name="close" size={14} /></button>
        </div>
        <div className="wq-modal__body">
          {editing ? (
            <WordForm initial={w} onCancel={() => setEditing(false)}
              onSave={(fields) => { window.WQActions.editWord(w.id, fields); setEditing(false); }} />
          ) : (
            <React.Fragment>
              <div className="wq-card__en">{w.en} <window.WQSpeakBtn text={w.en} /></div>
              {w.ts && <div className="wq-card__ts mono">/{w.ts}/</div>}
              <div className="wq-card__ru">{w.ru}</div>
              {w.ex && <div className="wq-card__ex">«{w.ex}»</div>}
              <div className="row-2 gap-2" style={{ justifyContent: "center", flexWrap: "wrap" }}>
                <button className="wq-btn wq-btn--sm" onClick={() => setEditing(true)}>Редактировать текст</button>
                {isEdited && <button className="wq-link" onClick={() => window.WQActions.resetWord(w.id)}>Вернуть исходное</button>}
              </div>
            </React.Fragment>
          )}

          <div className="wq-imgzone"
            onClick={() => fileRef.current && fileRef.current.click()}
            onDragOver={(e) => { e.preventDefault(); e.currentTarget.classList.add("wq-imgzone--over"); }}
            onDragLeave={(e) => e.currentTarget.classList.remove("wq-imgzone--over")}
            onDrop={(e) => { e.preventDefault(); e.currentTarget.classList.remove("wq-imgzone--over"); pick(e.dataTransfer.files[0]); }}>
            {img ? <img className="wq-imgzone__img" src={img} alt="" /> : (
              <span className="wq-imgzone__empty e-label">{busy ? "Загрузка…" : "Картинка-ассоциация — нажмите или перетащите файл"}</span>
            )}
            <input ref={fileRef} type="file" accept="image/*" hidden
              onChange={(e) => { pick(e.target.files[0]); e.target.value = ""; }} />
          </div>
          {img && (
            <div className="row-2 gap-2" style={{ justifyContent: "center", flexWrap: "wrap" }}>
              <button className="wq-btn wq-btn--sm" onClick={() => fileRef.current && fileRef.current.click()}>Заменить картинку</button>
              <button className="wq-link" onClick={() => window.WQActions.removeWordImage(w.id)}>Удалить картинку</button>
            </div>
          )}

          <div className="wq-progtable">
            <div><span className="e-label">Статус</span><span className="mono">{st.label}{st.due ? " · пора повторить" : ""}</span></div>
            {p ? (
              <React.Fragment>
                <div><span className="e-label">Повторений</span><span className="mono">{p.reps}</span></div>
                <div><span className="e-label">Интервал</span><span className="mono">{p.interval} дн.</span></div>
                <div><span className="e-label">Следующее</span><span className="mono">{dm(p.due)}</span></div>
                <div><span className="e-label">Лёгкость</span><span className="mono">{p.ef}</span></div>
              </React.Fragment>
            ) : (
              <div><span className="e-label">Прогресс</span><span className="mono">слово ещё не изучалось</span></div>
            )}
          </div>

          {isCustom && (
            <button className="wq-link" style={{ color: "#B23A2E" }}
              onClick={() => { window.WQActions.deleteWord(w.id); onClose(); }}>Удалить карточку</button>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---- список слов категории ---------------------------------------------- */
function WordsList({ catId, onBack, onStart }) {
  const s = window.useWQ();
  const L = window.WQLookup;
  const cats = L.categories(s);
  /* catId === null — словарь по всем категориям */
  const cat = catId ? cats.find((c) => c.id === catId) : null;
  const [open, setOpen] = useState(null);
  const [adding, setAdding] = useState(false);
  const [addCat, setAddCat] = useState(catId || (cats[0] && cats[0].id));
  const [q, setQ] = useState("");
  const [filterCat, setFilterCat] = useState(catId || "all");
  const [renaming, setRenaming] = useState(false);
  const [rename, setRename] = useState("");
  const [confirmDel, setConfirmDel] = useState(false);
  if (catId && !cat) return <div className="wq-empty">Категория не найдена.</div>;

  const ownCat = !!(cat && cat.custom);
  const scope = catId ? [cat] : (filterCat === "all" ? cats : cats.filter((c) => c.id === filterCat));
  const needle = q.trim().toLowerCase();
  const words = scope.flatMap((c) => c.words).filter((w) =>
    !needle || w.en.toLowerCase().includes(needle) || w.ru.toLowerCase().includes(needle));

  return (
    <div>
      <button className="wq-link wq-setup__back" onClick={onBack}><Icon name="back" size={14} />К категориям</button>
      <div className="row-3" style={{ justifyContent: "space-between", alignItems: "flex-end", flexWrap: "wrap", gap: "var(--s-4)" }}>
        <div>
          {renaming ? (
            <form className="wq-addcat__form" style={{ marginBottom: 4 }} onSubmit={(e) => { e.preventDefault(); const t = rename.trim(); if (t) { window.WQActions.renameCategory(catId, t); setRenaming(false); } }}>
              <input autoFocus className="wq-input" value={rename} onChange={(e) => setRename(e.target.value)} />
              <button className="wq-btn wq-btn--ink wq-btn--sm" type="submit">ОК</button>
              <button className="wq-btn wq-btn--sm" type="button" onClick={() => setRenaming(false)}>Отмена</button>
            </form>
          ) : (
            <h2 className="wq-setup__title">{cat ? cat.title : "Все карточки"}</h2>
          )}
          <div className="wq-setup__stats mono">{words.length} слов · нажмите на слово, чтобы открыть и отредактировать</div>
          {ownCat && !renaming && (
            <div className="row-2 gap-3" style={{ marginTop: 4 }}>
              <span className="wq-link" onClick={() => { setRename(cat.title); setRenaming(true); }}>Переименовать</span>
              {confirmDel ? (
                <React.Fragment>
                  <span className="e-label" style={{ color: "var(--text-muted)" }}>Удалить категорию со всеми словами?</span>
                  <span className="wq-link" style={{ color: "#B23A2E" }} onClick={() => { window.WQActions.deleteCategory(catId); onBack(); }}>Да</span>
                  <span className="wq-link" onClick={() => setConfirmDel(false)}>Нет</span>
                </React.Fragment>
              ) : (
                <span className="wq-link" style={{ color: "#B23A2E" }} onClick={() => setConfirmDel(true)}>Удалить категорию</span>
              )}
            </div>
          )}
        </div>
        <div className="row-2 gap-2" style={{ flexWrap: "wrap" }}>
          <button className="wq-btn wq-btn--sm" onClick={() => {
            const preset = catId || (filterCat !== "all" ? filterCat : (cats[0] && cats[0].id));
            setAddCat(preset);
            setAdding(true);
          }}>+ Своё слово</button>
          <button className="wq-btn wq-btn--ink" onClick={() => onStart(catId)}>Заниматься</button>
        </div>
      </div>

      <div className="wq-listtools">
        <input className="wq-input wq-listtools__search" value={q} onChange={(e) => setQ(e.target.value)}
          placeholder="Поиск по слову или переводу…" />
        {!catId && (
          <select className="wq-select" value={filterCat} onChange={(e) => setFilterCat(e.target.value)}>
            <option value="all">Все категории</option>
            {cats.map((c) => <option key={c.id} value={c.id}>{c.title}</option>)}
          </select>
        )}
      </div>

      {words.length === 0 && <div className="wq-empty">Ничего не найдено.</div>}

      <div className="wq-wordlist">
        {words.map((w) => {
          const st = L.wordStatus(s, w.id);
          const img = (s.images || {})[w.id];
          const mark = L.isCustom(w.id) ? "своё" : (s.edits || {})[w.id] ? "изменено" : null;
          return (
            <button key={w.id} className="wq-wordrow" onClick={() => setOpen(w)}>
              <span className="wq-wordrow__thumb">{img ? <img src={img} alt="" /> : null}</span>
              <span className="wq-wordrow__en">
                {w.en}
                {mark && <span className="wq-wordrow__mark">{mark}</span>}
              </span>
              <span className="wq-wordrow__ru">{w.ru}</span>
              <span className={"wq-wordrow__st wq-wordrow__st--" + st.key}>{st.due ? "к повторению" : st.label}</span>
            </button>
          );
        })}
      </div>

      {open && <WordDetail word={open} onClose={() => setOpen(null)} />}

      {adding && (
        <div className="wq-overlay" onClick={() => setAdding(false)}>
          <div className="wq-modal" onClick={(e) => e.stopPropagation()}>
            <div className="wq-modal__bar">
              <span className="e-label">Новая карточка</span>
              <button className="wq-modal__x" onClick={() => setAdding(false)} title="Закрыть"><Icon name="close" size={14} /></button>
            </div>
            <div className="wq-modal__body">
              {/* в общем словаре категорию нужно выбрать явно */}
              <label className="wq-field" style={{ width: "100%" }}>
                <span className="e-label">Категория</span>
                <select className="wq-select" value={addCat} onChange={(e) => setAddCat(e.target.value)}>
                  {cats.map((c) => <option key={c.id} value={c.id}>{c.title}</option>)}
                </select>
              </label>
              <WordForm onCancel={() => setAdding(false)}
                onSave={(fields) => {
                  const id = window.WQActions.addWord(addCat, fields);
                  if (id) {
                    setAdding(false);
                    /* сразу открываем новую карточку — обычно следом хочется добавить картинку */
                    setOpen({ id, catId: addCat, ...fields });
                  }
                }} />
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ---- настройка сессии: категория уже выбрана, выбираем режим/направление */
function SessionSetup({ catId, onBegin, onBack }) {
  const s = window.useWQ();
  const L = window.WQLookup;
  const cat = catId ? L.categories(s).find((c) => c.id === catId) : null;
  const stats = cat ? L.categoryStats(s, cat) : L.overallStats(s);
  const [mode, setMode] = useState("mixed");
  const [dir, setDir] = useState("en"); /* "en" = EN→RU, "ru" = RU→EN */

  const queue = useMemo(() => window.WQLookup.sessionQueue(s, cat, { newLimit: 8 }), [catId]);
  /* направление имеет смысл только для карточек и выбора; в остальном скрыто */
  const showDir = mode === "cards" || mode === "choice";

  return (
    <div className="wq-setup">
      <button className="wq-link wq-setup__back" onClick={onBack}><Icon name="back" size={14} />К категориям</button>
      <h2 className="wq-setup__title">{cat ? cat.title : "Все категории"}</h2>
      <div className="wq-setup__stats mono">
        {stats.due} к повторению · {stats.fresh} новых · {stats.learned}/{stats.total} выучено
      </div>

      <div className="wq-setup__block">
        <div className="e-label wq-setup__label">Вид проверки</div>
        <div className="wq-modes">
          {WQ_MODES.map((m) => (
            <button key={m.id} className={"wq-mode" + (mode === m.id ? " wq-mode--on" : "")} onClick={() => setMode(m.id)}>
              <Icon name={m.icon} size={18} />
              <span className="wq-mode__label">{m.label}</span>
            </button>
          ))}
        </div>
        <div className="wq-setup__hint">{WQ_MODES.find((m) => m.id === mode).hint}</div>
      </div>

      {showDir && (
        <div className="wq-setup__block">
          <div className="e-label wq-setup__label">Направление</div>
          <div className="seg">
            <button className={"seg__btn" + (dir === "en" ? " seg__btn--on" : "")} onClick={() => setDir("en")}>Английский → русский</button>
            <button className={"seg__btn" + (dir === "ru" ? " seg__btn--on" : "")} onClick={() => setDir("ru")}>Русский → английский</button>
          </div>
        </div>
      )}

      {queue.length === 0 ? (
        <div className="wq-empty">Все слова этой категории уже повторены на сегодня. Загляните завтра — или выберите другую категорию.</div>
      ) : (
        <button className="wq-btn wq-btn--ink wq-setup__go" onClick={() => onBegin({ queue, mode, dir })}>
          Начать ({queue.length} слов)
        </button>
      )}
    </div>
  );
}

/* ---- активная сессия: 6 форматов + смешанный режим -------------------- */
const norm = (v) => (v || "").trim().toLowerCase().replace(/[.,!?;:]/g, "");

function SessionRunner({ session, onFinish }) {
  const { queue, mode, dir: sessionDir } = session;
  const [idx, setIdx] = useState(0);
  const [stats, setStats] = useState({ correct: 0, wrong: 0, xp: 0 });
  const [feedback, setFeedback] = useState(null);
  const [typed, setTyped] = useState("");
  const { useEffect } = React;

  /* план сессии: у каждого слова свой формат (для «смешанной») и направление.
     Считается один раз на старте — чтобы форматы не переставлялись на ре-рендере. */
  const plan = useMemo(() => queue.map((w) => planItem(w, mode, sessionDir)), []);
  const word = queue[idx];
  const cur = plan[idx] || { fmt: "cards", dir: sessionDir };
  const fmt = cur.fmt;
  const dir = cur.dir;
  const images = window.WQStore.get().images || {};
  const cloze = fmt === "cloze" ? clozeMatch(word) : null;

  /* варианты для «выбора» и «по картинке» (в image ответ — английское слово) */
  const optDir = fmt === "image" ? "ru" : dir;
  const options = useMemo(() => {
    if (!word || (fmt !== "choice" && fmt !== "image")) return null;
    const correct = optDir === "ru" ? word.en : word.ru;
    return shuffle([correct, ...pickDistractors(word, optDir, 3)]);
  }, [word && word.id, fmt]);
  const optCorrect = optDir === "ru" ? word.en : word.ru;

  /* аудио-формат: проговариваем слово при показе */
  useEffect(() => {
    if (word && fmt === "audio") { try { window.WQSpeak(word.en); } catch (e) {} }
  }, [word && word.id, fmt]);

  const finishOne = (quality) => {
    if (!word) return;
    window.WQActions.reviewWord(word.id, quality);
    const xpMap = { 0: 1, 1: 1, 2: 2, 3: 5, 4: 8, 5: 12 };
    const next = {
      correct: stats.correct + (quality >= 3 ? 1 : 0),
      wrong: stats.wrong + (quality < 3 ? 1 : 0),
      xp: stats.xp + (xpMap[quality] || 0),
    };
    setStats(next);
    setFeedback(null);
    setTyped("");
    if (idx + 1 >= queue.length) onFinish(next);
    else setIdx((i) => i + 1);
  };

  if (!word) return null;

  /* общий «подвал» после ответа: транскрипция, картинка, кнопка «Далее» */
  const FeedbackFoot = ({ ok, children }) => (
    <div className="wq-feedback">
      <span className={ok ? "wq-feedback__ok" : "wq-feedback__bad"}>{children}</span>
      {word.ts && <span className="mono wq-feedback__ts">/{word.ts}/</span>}
      <window.WQWordImage wordId={word.id} size="sm" />
      <button className="wq-btn wq-btn--ink wq-btn--sm" onClick={() => finishOne(ok ? 4 : 1)}>Далее</button>
    </div>
  );

  return (
    <div className="wq-session">
      <div className="wq-session__top">
        <button className="wq-link" onClick={() => onFinish(stats)}><Icon name="close" size={14} />Прервать</button>
        <window.WQBar value={idx} total={queue.length} />
        <span className="mono wq-session__count">{idx + 1} / {queue.length}</span>
      </div>

      {fmt === "cards" ? (
        <window.WQFlashcard word={word} dir={dir} onRate={finishOne} />

      ) : fmt === "choice" ? (
        <div className="wq-card">
          <div className="wq-card__kicker">{dir === "ru" ? "Как будет по-английски" : "Что означает"}</div>
          <div className="wq-card__front">
            {dir === "ru" ? word.ru : word.en}
            {dir !== "ru" && <window.WQSpeakBtn text={word.en} />}
          </div>
          <div className="wq-choices">
            {options.map((opt) => {
              const isCorrect = opt === optCorrect;
              const chosen = feedback && feedback.opt === opt;
              const cls = "wq-choice" + (feedback ? (isCorrect ? " wq-choice--correct" : chosen ? " wq-choice--wrong" : " wq-choice--dim") : "");
              return (
                <button key={opt} className={cls} disabled={!!feedback} onClick={() => setFeedback({ ok: isCorrect, opt })}>{opt}</button>
              );
            })}
          </div>
          {feedback && <FeedbackFoot ok={feedback.ok}>{feedback.ok ? "Верно!" : "Правильный ответ: " + optCorrect}</FeedbackFoot>}
        </div>

      ) : fmt === "image" ? (
        <div className="wq-card">
          <div className="wq-card__kicker">Что это за слово?</div>
          <img className="wq-qimg" src={images[word.id]} alt="" />
          <div className="wq-choices">
            {options.map((opt) => {
              const isCorrect = opt === optCorrect;
              const chosen = feedback && feedback.opt === opt;
              const cls = "wq-choice" + (feedback ? (isCorrect ? " wq-choice--correct" : chosen ? " wq-choice--wrong" : " wq-choice--dim") : "");
              return (
                <button key={opt} className={cls} disabled={!!feedback} onClick={() => setFeedback({ ok: isCorrect, opt })}>{opt}</button>
              );
            })}
          </div>
          {feedback && <FeedbackFoot ok={feedback.ok}>{feedback.ok ? "Верно!" : optCorrect + " — " + word.ru}</FeedbackFoot>}
        </div>

      ) : fmt === "cloze" && cloze ? (
        <div className="wq-card">
          <div className="wq-card__kicker">Впиши пропущенное слово</div>
          <div className="wq-cloze">{cloze.before}<span className="wq-cloze__gap" />{cloze.after}</div>
          <div className="wq-card__ru" style={{ fontSize: "var(--t-body)" }}>{word.ru}</div>
          {!feedback ? (
            <form className="wq-typeform" onSubmit={(e) => {
              e.preventDefault();
              const ok = norm(typed) === norm(cloze.hidden) || norm(typed) === norm(word.en);
              setFeedback({ ok });
            }}>
              <input autoFocus className="wq-input" value={typed} onChange={(e) => setTyped(e.target.value)} placeholder="пропущенное слово…" />
              <button className="wq-btn wq-btn--ink" type="submit">Проверить</button>
            </form>
          ) : (
            <React.Fragment>
              <div className="wq-cloze wq-cloze--full">{cloze.before}<b>{cloze.hidden}</b>{cloze.after}</div>
              <FeedbackFoot ok={feedback.ok}>{feedback.ok ? "Верно!" : <React.Fragment>Правильно: <b>{cloze.hidden}</b>{typed ? " (вы ввели «" + typed + "»)" : ""}</React.Fragment>}</FeedbackFoot>
            </React.Fragment>
          )}
        </div>

      ) : fmt === "audio" ? (
        <div className="wq-card">
          <div className="wq-card__kicker">Прослушай и впиши слово</div>
          <button type="button" className="wq-audio" onClick={() => window.WQSpeak(word.en)} title="Прослушать ещё раз">🔊</button>
          {!feedback ? (
            <form className="wq-typeform" onSubmit={(e) => {
              e.preventDefault();
              const ok = norm(typed) === norm(word.en);
              setFeedback({ ok });
            }}>
              <input autoFocus className="wq-input" value={typed} onChange={(e) => setTyped(e.target.value)} placeholder="что услышал(а)…" />
              <button className="wq-btn wq-btn--ink" type="submit">Проверить</button>
            </form>
          ) : (
            <React.Fragment>
              <div className="wq-card__front" style={{ fontSize: "var(--t-h2)" }}>{word.en}</div>
              <div className="wq-card__ru" style={{ fontSize: "var(--t-body)" }}>{word.ru}</div>
              <FeedbackFoot ok={feedback.ok}>{feedback.ok ? "Верно!" : <React.Fragment>Правильно: <b>{word.en}</b>{typed ? " (вы ввели «" + typed + "»)" : ""}</React.Fragment>}</FeedbackFoot>
            </React.Fragment>
          )}
        </div>

      ) : (
        <div className="wq-card">
          <div className="wq-card__kicker">Впиши слово по-английски</div>
          <div className="wq-card__front">{word.ru}</div>
          {!feedback ? (
            <form className="wq-typeform" onSubmit={(e) => {
              e.preventDefault();
              setFeedback({ ok: norm(typed) === norm(word.en) });
            }}>
              <input autoFocus className="wq-input" value={typed} onChange={(e) => setTyped(e.target.value)} placeholder="type in English…" />
              <button className="wq-btn wq-btn--ink" type="submit">Проверить</button>
            </form>
          ) : (
            <FeedbackFoot ok={feedback.ok}>{feedback.ok ? "Верно!" : <React.Fragment>Правильно: <b>{word.en}</b> (вы ввели «{typed}»)</React.Fragment>}</FeedbackFoot>
          )}
        </div>
      )}
    </div>
  );
}

/* ---- итоги сессии --------------------------------------------------------- */
function SessionSummary({ stats, onDone }) {
  const total = stats.correct + stats.wrong;
  const pct = total > 0 ? Math.round((stats.correct / total) * 100) : 0;
  return (
    <div className="wq-summary">
      <Icon name="check" size={36} style={{ color: "var(--signal)" }} />
      <h2 className="wq-summary__title">Сессия завершена</h2>
      <div className="wq-summary__row">
        <div className="wq-summary__stat"><span className="wq-hero__num">{total}</span><span className="e-label">слов повторено</span></div>
        <div className="wq-summary__stat"><span className="wq-hero__num">{pct}%</span><span className="e-label">верно</span></div>
        <div className="wq-summary__stat"><span className="wq-hero__num">+{stats.xp}</span><span className="e-label">XP</span></div>
      </div>
      <button className="wq-btn wq-btn--ink" onClick={onDone}>К категориям</button>
    </div>
  );
}

/* ---- корневой компонент --------------------------------------------------- */
function WordsQuestScreen() {
  const [view, setView] = useState({ mode: "hub" });

  return (
    <div className="page page--wide screen-enter wordsquest" data-screen-label="Английские слова">
      <div className="page-head">
        <div className="page-head__titles">
          <span className="eyebrow-mono">Квесты · Изучение языка</span>
          <h1 className="page-title">Английские слова</h1>
        </div>
      </div>

      {view.mode === "hub" && (
        <WordsHub onStart={(catId) => setView({ mode: "setup", catId })}
          onOpenList={(catId) => setView({ mode: "words", catId })} />
      )}
      {view.mode === "words" && (
        <WordsList catId={view.catId} onBack={() => setView({ mode: "hub" })}
          onStart={(catId) => setView({ mode: "setup", catId })} />
      )}
      {view.mode === "setup" && (
        <SessionSetup catId={view.catId} onBack={() => setView({ mode: "hub" })}
          onBegin={(session) => setView({ mode: "session", session })} />
      )}
      {view.mode === "session" && (
        <SessionRunner session={view.session} onFinish={(stats) => setView({ mode: "summary", stats })} />
      )}
      {view.mode === "summary" && (
        <SessionSummary stats={view.stats} onDone={() => setView({ mode: "hub" })} />
      )}
    </div>
  );
}

window.Screens = Object.assign(window.Screens || {}, { questsWords: WordsQuestScreen });
