/* global React, CR_UI, CR_API */
const { useState } = React;
const { Icons, Btn, EmptyState } = CR_UI;

// Render TG-style markdown (bold, italic, leave newlines)
function renderTgText(s) {
  s = s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
  s = s.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<em>$1</em>");
  return s;
}

const PostsScreen = ({ onCopy, onPostsChanged, brand, navTarget, onNavConsumed }) => {
  const [tab, setTab]           = useState("drafts");
  const [drafts, setDrafts]     = useState(null);   // null=loading, []=empty
  const [published, setPublished] = useState(null);
  const [loadError, setLoadError] = useState(null);
  const [activeId, setActiveId] = useState(null);
  // post body cache: topicId → { text, loaded }
  const [bodyCache, setBodyCache] = useState({});
  const [bodyLoading, setBodyLoading] = useState(false);
  // poll while there's an active generate_post job for this brand so a new
  // draft appears without manual reload
  const [polling, setPolling] = useState(false);
  // councilRunning persists across modal open/close — guards against duplicate re-runs
  const [councilRunning, setCouncilRunning] = useState(false);
  const [councilModal, setCouncilModal] = useState(null); // null | { topic_id, result }
  const [councilCache, setCouncilCache] = useState({});
  // Post draft versioning
  const [draftVersions, setDraftVersions] = useState({ history: [], current: 1 });
  const [selectedDraftVersion, setSelectedDraftVersion] = useState(null); // null = current
  const [historyBodyCache, setHistoryBodyCache] = useState({}); // `${topicId}_v${n}` → body
  const [refinePostRunning, setRefinePostRunning] = useState(false);
  const [bodyRefreshToken, setBodyRefreshToken] = useState(0);
  const activeIdRef = React.useRef(activeId);
  React.useEffect(() => { activeIdRef.current = activeId; }, [activeId]);

  // Poll for council result while viewing a draft that has no result yet.
  // Council runs in background after generate_post — this picks it up.
  React.useEffect(() => {
    if (!brand || !activeId || tab !== "drafts") return;
    if (councilCache[activeId]) return;
    let cancelled = false;
    let attempts = 0;
    const poll = async () => {
      if (cancelled || attempts >= 24) return; // max ~2 min
      attempts++;
      try {
        const result = await CR_API.getCouncil(brand, activeId);
        if (!cancelled && result) { setCouncilCache((c) => ({ ...c, [activeId]: result })); return; }
      } catch (_) {}
      if (!cancelled) setTimeout(poll, 5000);
    };
    const tid = setTimeout(poll, 3000);
    return () => { cancelled = true; clearTimeout(tid); };
  }, [brand, activeId, tab]); // eslint-disable-line react-hooks/exhaustive-deps

  // Keep onPostsChanged in a ref so fetchPosts doesn't change identity each
  // render — without this, the brand-change effect would call setDrafts(null)
  // on every render, leaving the list stuck on "Загрузка…" forever.
  const onPostsChangedRef = React.useRef(onPostsChanged);
  React.useEffect(() => { onPostsChangedRef.current = onPostsChanged; }, [onPostsChanged]);

  const fetchPosts = React.useCallback(() => {
    if (!brand) return;
    setLoadError(null);
    Promise.all([
      CR_API.listPostDrafts(brand).catch((err) => { throw err; }),
      CR_API.listPostsPublished(brand).catch((err) => { throw err; }),
    ]).then(([d, p]) => {
      setDrafts(d);
      setPublished(p);
      onPostsChangedRef.current && onPostsChangedRef.current();
    }).catch((err) => setLoadError(err.message));
  }, [brand]);

  // Fetch both lists when brand changes
  React.useEffect(() => {
    setActiveId(null);
    setDrafts(null);
    setPublished(null);
    fetchPosts();
  }, [fetchPosts]);

  const list = tab === "drafts" ? (drafts || []) : (published || []);

  // Apply navTarget once posts are loaded
  React.useEffect(() => {
    if (!navTarget || navTarget.kind !== "post" || drafts === null || published === null) return;
    const wantTab = navTarget.folder === "published" ? "published" : "drafts";
    setTab(wantTab);
    const targetList = wantTab === "drafts" ? drafts : published;
    if (targetList.find((p) => p.topic_id === navTarget.id)) {
      setActiveId(navTarget.id);
    }
    onNavConsumed && onNavConsumed();
  }, [navTarget, drafts, published, onNavConsumed]);

  const postMeta = list.find((p) => p.topic_id === activeId) || list[0];

  // Auto-select first post when drafts/published arrive or tab changes.
  // Without this, the body fetch effect (which keys on activeId) never fires
  // and the right pane stays on "Загрузка…" forever.
  React.useEffect(() => {
    if (navTarget) return; // wait for navTarget effect to drive selection
    if (drafts === null || published === null) return; // still loading
    if (activeId && list.find((p) => p.topic_id === activeId)) return;
    const first = list[0];
    setActiveId(first?.topic_id || null);
  }, [tab, drafts, published, navTarget, list, activeId]);

  // Poll for new drafts while a generate_post job is running
  React.useEffect(() => {
    if (!brand || !polling) return;
    const t = setInterval(fetchPosts, 4000);
    return () => clearInterval(t);
  }, [brand, polling, fetchPosts]);
  React.useEffect(() => {
    if (!brand) return;
    let cancelled = false;
    async function check() {
      try {
        const jobs = await CR_API.listJobs(brand, true);
        if (cancelled) return;
        setPolling(jobs.some((j) => j.kind === "generate_post" && (j.status === "running" || j.status === "queued")));
      } catch (_) {}
    }
    check();
    const t = setInterval(check, 4000);
    return () => { cancelled = true; clearInterval(t); };
  }, [brand]);

  // Fetch full post body lazily
  React.useEffect(() => {
    if (!brand || !activeId) return;
    const cacheKey = `${tab}:${activeId}`;
    if (bodyCache[cacheKey]) return;
    setBodyLoading(true);
    CR_API.getPost(brand, activeId, tab)
      .then((doc) => {
        setBodyCache((c) => ({ ...c, [cacheKey]: doc.body }));
        setBodyLoading(false);
      })
      .catch(() => {
        setBodyCache((c) => ({ ...c, [cacheKey]: "" }));
        setBodyLoading(false);
      });
  }, [brand, activeId, tab, bodyRefreshToken]); // eslint-disable-line react-hooks/exhaustive-deps

  // Load draft version list when switching topics or after a refine job finishes.
  React.useEffect(() => {
    if (!brand || !activeId || tab !== "drafts") return;
    setSelectedDraftVersion(null);
    CR_API.postDraftVersions(brand, activeId)
      .then(setDraftVersions)
      .catch(() => setDraftVersions({ history: [], current: 1 }));
  }, [brand, activeId, tab, bodyRefreshToken]); // eslint-disable-line react-hooks/exhaustive-deps

  // Fetch history body lazily when an old version is selected
  React.useEffect(() => {
    if (!brand || !activeId || !selectedDraftVersion) return;
    const key = `${activeId}_v${selectedDraftVersion}`;
    if (historyBodyCache[key] !== undefined) return;
    CR_API.getPostHistory(brand, activeId, selectedDraftVersion)
      .then((data) => setHistoryBodyCache((c) => ({ ...c, [key]: data.body })))
      .catch(() => setHistoryBodyCache((c) => ({ ...c, [key]: "" })));
  }, [brand, activeId, selectedDraftVersion]); // eslint-disable-line react-hooks/exhaustive-deps

  // Watch refine_post jobs; refresh post + versions when job finishes
  const wasRefinePostRunning = React.useRef(false);
  React.useEffect(() => {
    if (!brand) return;
    let cancelled = false;
    async function check() {
      try {
        const activeJobs = await CR_API.listJobs(brand, true);
        if (cancelled) return;
        const running = activeJobs.some(
          (j) => j.kind === "refine_post" && (j.status === "running" || j.status === "queued"),
        );
        if (wasRefinePostRunning.current && !running) {
          const tid = activeIdRef.current;
          if (tid) {
            setBodyCache((c) => { const n = { ...c }; delete n[`drafts:${tid}`]; return n; });
            setBodyRefreshToken((t) => t + 1);
            CR_API.postDraftVersions(brand, tid)
              .then((data) => { setDraftVersions(data); setSelectedDraftVersion(null); })
              .catch(() => {});
          }
          fetchPosts();
        }
        wasRefinePostRunning.current = running;
        setRefinePostRunning(running);
      } catch (_) {}
    }
    check();
    const t = setInterval(check, 4000);
    return () => { cancelled = true; clearInterval(t); };
  }, [brand, fetchPosts]); // eslint-disable-line react-hooks/exhaustive-deps

  const cacheKey = `${tab}:${activeId}`;
  const historyKey = selectedDraftVersion ? `${activeId}_v${selectedDraftVersion}` : null;
  const postText = historyKey
    ? (historyBodyCache[historyKey] ?? null)
    : (bodyCache[cacheKey] ?? null);
  const loading = drafts === null || published === null;
  const isEmpty = !loading && list.length === 0;

  const renderStage = () => {
    if (loadError) {
      return (
        <div className="post-stage__inner">
          <div className="error-banner" style={{ maxWidth: "none", margin: 0 }}>
            <Icons.AlertTriangle size={18} />
            <div style={{ flex: 1 }}>
              <div className="error-banner__title">Не удалось загрузить посты</div>
              <div className="error-banner__body">{loadError}</div>
            </div>
          </div>
        </div>
      );
    }
    if (loading) {
      return (
        <div className="post-stage__inner" style={{ textAlign: "center", paddingTop: 60 }}>
          <div style={{ color: "var(--status-info-fg)", display: "inline-block" }}><Icons.Spinner size={24} /></div>
          <div className="screen__title" style={{ fontSize: 20, marginTop: 14 }}>Загрузка…</div>
        </div>
      );
    }
    if (isEmpty || !postMeta) {
      return (
        <EmptyState
          icon={<Icons.Send size={26} />}
          title={tab === "drafts" ? "Нет черновиков" : "Нет опубликованных постов"}
          body="Перейди в Articles, выбери одобренную тему и нажми «Сгенерировать пост»."
        />
      );
    }

    return (
      <div className="post-stage__inner">
        {bodyLoading || postText === null ? (
          <div style={{ textAlign: "center", paddingTop: 60 }}>
            <div style={{ color: "var(--status-info-fg)", display: "inline-block" }}><Icons.Spinner size={24} /></div>
          </div>
        ) : (
          <div className="tg-frame">
            <div className="tg-bubble">
              <div className="tg-bubble__channel">
                <span style={{ width: 28, height: 28, borderRadius: 999, background: "#DA435D", color: "#fff", display: "inline-flex", alignItems: "center", justifyContent: "center", fontSize: 12, fontWeight: 700, fontFamily: "TT Lakes" }}>{(brand || "").slice(0, 2).toUpperCase()}</span>
                {brand}
              </div>
              <div className="tg-bubble__text" dangerouslySetInnerHTML={{ __html: renderTgText(postText) }} />
              <div className="tg-bubble__time">
                {tab === "drafts"
                  ? `черновик · ${new Date(postMeta.created_at).toLocaleDateString("ru-RU")}`
                  : `опубликован · ${new Date(postMeta.created_at).toLocaleDateString("ru-RU")}`}
              </div>
            </div>
          </div>
        )}
        {!bodyLoading && postText !== null && (
          <div style={{ marginTop: 14, color: "var(--fg-muted)", fontSize: 12.5, textAlign: "center" }}>
            Превью того, как пост будет выглядеть в Telegram Desktop
          </div>
        )}
        {tab === "drafts" && !bodyLoading && postText !== null && (() => {
          const cr = councilCache[postMeta?.topic_id];
          if (!cr) return null;
          const decision = cr.verdict?.decision;
          const approved = decision === "approved";
          const failed = decision === "failed";
          return (
            <div
              style={{
                marginTop: 10, cursor: "pointer", borderRadius: 6, padding: "8px 14px",
                background: approved ? "var(--status-approved-bg)" : failed ? "var(--status-rejected-bg)" : "var(--status-warning-bg)",
                display: "flex", alignItems: "center", gap: 10,
              }}
              onClick={() => setCouncilModal({ topic_id: postMeta.topic_id, result: cr })}
            >
              <span style={{ fontWeight: 700, fontSize: 12, color: approved ? "var(--status-approved-fg)" : failed ? "var(--status-rejected-fg)" : "var(--status-warning-fg)" }}>
                {approved ? `✓ ${cr.verdict?.model || "Судья"} одобрил` : failed ? "⚠ Ошибка консилиума" : "✎ Нужны правки"}
              </span>
              <span style={{ fontSize: 11, color: "var(--fg-muted)" }}>
                GPT {cr.reviews?.gpt?.score ?? "—"} · Gemini {cr.reviews?.gemini?.score ?? "—"}
              </span>
              <span style={{ marginLeft: "auto", fontSize: 11, color: "var(--fg-muted)" }}>подробнее →</span>
            </div>
          );
        })()}
      </div>
    );
  };

  return (
    <div className="screen">
      <div className="screen__header" style={{ paddingBottom: 0 }}>
        <div className="screen__header-row">
          <div style={{ flex: 1 }}>
            <div className="section-label" style={{ marginBottom: 6 }}>Telegram · {brand}</div>
            <h1 className="screen__title">Посты</h1>
            <div className="screen__subtitle">
              Финальные посты для копирования в Telegram. Кнопка <span className="kbd">⌘C</span> копирует текст в буфер.
            </div>
          </div>
        </div>
        <div className="tabs">
          <button className={"tab " + (tab === "drafts" ? "is-active" : "")} onClick={() => setTab("drafts")}>
            Черновики <span className="tab__count">{drafts ? drafts.length : "…"}</span>
          </button>
          <button className={"tab " + (tab === "published" ? "is-active" : "")} onClick={() => setTab("published")}>
            Опубликованные <span className="tab__count">{published ? published.length : "…"}</span>
          </button>
        </div>
      </div>

      <div className="split" style={{ flex: 1 }}>
        <aside className="split__aside" style={{ width: 320 }}>
          <div className="split__aside-list" style={{ padding: 8 }}>
            {list.map((p) => (
              <div key={p.topic_id}
                className={"post-list-item " + (activeId === p.topic_id ? "is-active" : "")}
                onClick={() => setActiveId(p.topic_id)}>
                <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
                  <div className="post-list-item__title" style={{ flex: 1, minWidth: 0 }}>{p.topic_id}</div>
                  {p.council_decision && (
                    <span style={{
                      fontSize: 10, fontWeight: 700, padding: "1px 5px", borderRadius: 3, flexShrink: 0,
                      background: p.council_decision === "approved" ? "var(--status-approved-bg)" : p.council_decision === "failed" ? "var(--status-rejected-bg)" : "var(--status-warning-bg)",
                      color: p.council_decision === "approved" ? "var(--status-approved-fg)" : p.council_decision === "failed" ? "var(--status-rejected-fg)" : "var(--status-warning-fg)",
                    }}>
                      {p.council_decision === "approved" ? "✓" : p.council_decision === "failed" ? "⚠" : "✎"}
                    </span>
                  )}
                </div>
                <div className="post-list-item__meta">{new Date(p.created_at).toLocaleDateString("ru-RU")} · v{p.research_version}</div>
              </div>
            ))}
          </div>
        </aside>

        <main className="split__main">
          {postMeta && !isEmpty && (
            <div className="post-toolbar">
              <h2 className="post-toolbar__title">{postMeta.topic_id}</h2>
              {tab === "drafts" && draftVersions.history.length > 0 && (
                <select
                  className="version-select"
                  value={selectedDraftVersion ?? draftVersions.current}
                  onChange={(e) => {
                    const v = Number(e.target.value);
                    setSelectedDraftVersion(v === draftVersions.current ? null : v);
                  }}
                >
                  <option value={draftVersions.current}>v{draftVersions.current} · текущая</option>
                  {[...draftVersions.history].reverse().map((v) => (
                    <option key={v} value={v}>v{v}</option>
                  ))}
                </select>
              )}
              <Btn kind="primary" size="sm" icon={<Icons.Clipboard size={13} />} onClick={() => onCopy && onCopy({ text: postText })}>Скопировать в буфер</Btn>
              {tab === "drafts" && (
                <Btn kind="approve" size="sm" icon={<Icons.Check size={13} />}
                  onClick={async () => {
                    try { await CR_API.publishPost(brand, postMeta.topic_id); fetchPosts(); }
                    catch (e) { alert("Ошибка публикации: " + e.message); }
                  }}>Опубликовать</Btn>
              )}
              <Btn kind="reject" size="sm" icon={<Icons.X size={13} />}
                onClick={async () => {
                  const folderLabel = tab === "drafts" ? "черновик" : "опубликованный пост";
                  if (!confirm(`Удалить ${folderLabel} «${postMeta.topic_id}»?\n\nФайл будет удалён из workspace, в git-истории остаётся коммит для отката.`)) return;
                  try {
                    await CR_API.deletePost(brand, postMeta.topic_id, tab);
                    setActiveId(null);
                    fetchPosts();
                  } catch (e) {
                    alert("Ошибка удаления: " + e.message);
                  }
                }}>Удалить</Btn>
            </div>
          )}
          <div className="post-stage">{renderStage()}</div>
        </main>
      </div>

      {councilModal && (
        <div className="modal-overlay" onClick={() => setCouncilModal(null)}>
          <div className="modal" style={{ maxWidth: 640, width: "90vw" }} onClick={(e) => e.stopPropagation()}>
            <div className="modal__header">
              <div className="modal__title">Консилиум · {councilModal.topic_id}</div>
              <button className="modal__close" onClick={() => setCouncilModal(null)} aria-label="Close"><Icons.X /></button>
            </div>
            <div className="modal__body" style={{ maxHeight: "70vh", overflowY: "auto" }}>
              {councilRunning ? (
                <div style={{ textAlign: "center", padding: "40px 0" }}>
                  <div style={{ color: "var(--status-info-fg)" }}><Icons.Spinner size={28} /></div>
                  <div style={{ marginTop: 14, fontWeight: 600 }}>GPT и Gemini читают пост…</div>
                  <div style={{ fontSize: 13, color: "var(--fg-muted)", marginTop: 6 }}>
                    Параллельный анализ двух моделей, затем судья принимает решение. ~30–60 секунд.
                  </div>
                </div>
              ) : councilModal.result ? (() => {
                const r = councilModal.result;
                const gpt = r.reviews?.gpt || {};
                const gemini = r.reviews?.gemini || {};
                const verdict = r.verdict || {};
                const approved = verdict.decision === "approved";
                const verdictFailed = verdict.decision === "failed";
                return (
                  <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
                    <div style={{
                      padding: "12px 16px", borderRadius: 6, fontWeight: 700, fontSize: 15,
                      background: approved ? "var(--status-approved-bg)" : verdictFailed ? "var(--status-rejected-bg)" : "var(--status-warning-bg)",
                      color: approved ? "var(--status-approved-fg)" : verdictFailed ? "var(--status-rejected-fg)" : "var(--status-warning-fg)",
                    }}>
                      {approved ? `✓ ${verdict.model || "Судья"} одобрил пост` : verdictFailed ? `⚠ Ошибка консилиума` : `✎ ${verdict.model || "Судья"} рекомендует доработку`}
                      {verdict.reasoning && <div style={{ fontWeight: 400, fontSize: 13, marginTop: 4 }}>{verdict.reasoning}</div>}
                    </div>
                    {verdict.final_suggestions?.length > 0 && (
                      <div>
                        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.09em", textTransform: "uppercase", color: "var(--fg-muted)", marginBottom: 6 }}>Рекомендации {verdict.model || "судьи"}</div>
                        <ul style={{ margin: 0, paddingLeft: 18, fontSize: 13 }}>
                          {verdict.final_suggestions.map((s, i) => <li key={i}>{s}</li>)}
                        </ul>
                      </div>
                    )}
                    {[["GPT", gpt, gpt.model], ["Gemini", gemini, gemini.model]].map(([name, rv, model]) => (
                      <div key={name} style={{ border: "1px solid var(--border)", borderRadius: 6, padding: "12px 16px" }}>
                        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
                          <span style={{ fontWeight: 700, fontSize: 13 }}>{name}</span>
                          <span style={{ fontSize: 11, color: "var(--fg-muted)" }}>{model}</span>
                          <span style={{ marginLeft: "auto", fontWeight: 700, fontSize: 16 }}>{rv.score ?? "—"}<span style={{ fontSize: 11, color: "var(--fg-muted)", fontWeight: 400 }}>/10</span></span>
                        </div>
                        {rv.error && <div style={{ color: "var(--status-rejected-fg)", fontSize: 12, marginBottom: 6 }}>Ошибка: {rv.error}</div>}
                        {rv.summary && <div style={{ fontSize: 13, marginBottom: 8, color: "var(--fg-primary)" }}>{rv.summary}</div>}
                        {rv.issues?.length > 0 && (
                          <div style={{ marginBottom: 6 }}>
                            <div style={{ fontSize: 11, color: "var(--fg-muted)", marginBottom: 3 }}>Проблемы:</div>
                            <ul style={{ margin: 0, paddingLeft: 16, fontSize: 12, color: "var(--fg-muted)" }}>
                              {rv.issues.map((s, i) => <li key={i}>{s}</li>)}
                            </ul>
                          </div>
                        )}
                        {rv.suggestions?.length > 0 && (
                          <div>
                            <div style={{ fontSize: 11, color: "var(--fg-muted)", marginBottom: 3 }}>Предложения:</div>
                            <ul style={{ margin: 0, paddingLeft: 16, fontSize: 12 }}>
                              {rv.suggestions.map((s, i) => <li key={i}>{s}</li>)}
                            </ul>
                          </div>
                        )}
                      </div>
                    ))}
                    {r.errors?.length > 0 && (
                      <div style={{ background: "var(--status-rejected-bg)", borderRadius: 6, padding: "10px 14px", fontSize: 12, color: "var(--status-rejected-fg)" }}>
                        {r.errors.join("; ")}
                      </div>
                    )}
                    <div style={{ fontSize: 11, color: "var(--fg-muted)" }}>
                      Запущено: {new Date(r.created_at).toLocaleString("ru-RU")}
                    </div>
                  </div>
                );
              })() : null}
            </div>
            <div className="modal__footer">
              <Btn kind="ghost" onClick={() => setCouncilModal(null)}>Закрыть</Btn>
              {!councilRunning && councilModal.result?.verdict?.decision !== "failed" && (
                <Btn kind="primary" size="sm" icon={<Icons.Sparkles size={13} />}
                  disabled={refinePostRunning}
                  onClick={async () => {
                    const topicId = councilModal.topic_id;
                    try {
                      await CR_API.refinePost(brand, topicId);
                      setCouncilModal(null);
                    } catch (e) {
                      alert("Ошибка: " + e.message);
                    }
                  }}>
                  {refinePostRunning ? "Дорабатывается…" : "Доработать пост"}
                </Btn>
              )}
              {!councilRunning && (
                <Btn kind="secondary" size="sm" icon={<Icons.Refresh size={13} />}
                  onClick={async () => {
                    const topicId = councilModal.topic_id;
                    setCouncilRunning(true);
                    try {
                      const result = await CR_API.runCouncil(brand, topicId);
                      setCouncilCache((c) => ({ ...c, [topicId]: result }));
                      setCouncilModal((m) => m ? { ...m, result } : null);
                    } catch (e) {
                      alert("Ошибка: " + e.message);
                      setCouncilModal(null);
                    } finally {
                      setCouncilRunning(false);
                    }
                  }}>Перезапустить</Btn>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

// =========================================================================
// Settings Screen
// =========================================================================
const SETTINGS_TABS = [
  { id: "brand", label: "Канал" },
  { id: "settings", label: "Что искать" },
  { id: "style", label: "Как писать" },
  { id: "sources", label: "Где искать" },
];

const HelperBlock = ({ items }) => (
  <div className="settings-card settings-helper-card">
    <h4>Подсказки</h4>
    <ul>{items.map((i, k) => <li key={k} dangerouslySetInnerHTML={{ __html: i }} />)}</ul>
  </div>
);

// ---------------------------------------------------------------------------
// HistoryDrawer — shows bootstrap snapshots, supports rollback with confirm.
// ---------------------------------------------------------------------------
const HistoryDrawer = ({ open, onClose, onRestored, brand }) => {
  const [entries, setEntries] = React.useState(null); // null=loading, []=empty
  const [restoring, setRestoring] = React.useState(null); // snapshot_id being restored
  const [confirmId, setConfirmId] = React.useState(null); // snapshot_id awaiting confirm

  const reload = React.useCallback(() => {
    setEntries(null);
    CR_API.getBootstrapHistory(brand)
      .then(setEntries)
      .catch(() => setEntries([]));
  }, [brand]);

  React.useEffect(() => {
    if (open) reload();
  }, [open, reload]);

  // ESC closes drawer
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  const handleRestore = async (id) => {
    setRestoring(id);
    try {
      const result = await CR_API.restoreBootstrapSnapshot(brand, id);
      onRestored && onRestored(id, result.pre_restore_id);
      reload();
    } catch (e) {
      alert(e.status === 409 ? "Бренд занят активным запуском" : "Ошибка отката: " + e.message);
    } finally {
      setRestoring(null);
      setConfirmId(null);
    }
  };

  if (!open) return null;

  return (
    <>
      <div className="history-overlay" onClick={onClose} />
      <aside className="history-drawer">
        <div className="history-drawer__header">
          <h3 style={{ margin: 0, fontSize: 16, fontWeight: 700 }}>История правок</h3>
          <Btn kind="ghost" size="sm" icon={<Icons.X size={14} />} onClick={onClose}>Закрыть</Btn>
        </div>
        <div className="history-drawer__body">
          {entries === null && (
            <div className="history-drawer__empty">Загрузка…</div>
          )}
          {entries && entries.length === 0 && (
            <div className="history-drawer__empty">
              История пуста. Snapshot создаётся автоматически перед каждым запуском «Прочитать 100 постов и заполнить».
            </div>
          )}
          {entries && entries.map((e) => (
            <div
              key={e.id}
              className={"history-entry" + (e.is_pre_restore ? " history-entry--pre-restore" : "")}
            >
              <div className="history-entry__time">
                {new Date(e.created_at).toLocaleString("ru-RU", {
                  day: "numeric", month: "short", hour: "2-digit", minute: "2-digit",
                })}
                {e.is_pre_restore && (
                  <span className="history-entry__badge">перед откатом</span>
                )}
              </div>
              <div className="history-entry__channel">{e.channel_at_time || "—"}</div>
              <div className="history-entry__files">{e.files.join(" · ")}</div>
              <div className="history-entry__actions">
                {confirmId === e.id ? (
                  <>
                    <Btn kind="danger" size="sm" disabled={!!restoring} onClick={() => handleRestore(e.id)}>
                      {restoring === e.id ? "Откат…" : "Подтвердить откат"}
                    </Btn>
                    <Btn kind="ghost" size="sm" onClick={() => setConfirmId(null)}>Отмена</Btn>
                  </>
                ) : (
                  <Btn
                    kind="ghost"
                    size="sm"
                    disabled={!!restoring}
                    icon={<Icons.Refresh size={12} />}
                    onClick={() => setConfirmId(e.id)}
                  >
                    Откатить
                  </Btn>
                )}
              </div>
            </div>
          ))}
        </div>
      </aside>
    </>
  );
};

const SettingsScreen = ({ onSave, onBootstrap, brand }) => {
  const [tab, setTab] = useState("brand");
  // Markdown buffers — start empty, fetch from API on mount.
  const [settingsBuf, setSettingsBuf] = React.useState("");
  const [styleBuf, setStyleBuf] = React.useState("");
  const [sourcesBuf, setSourcesBuf] = React.useState("");
  // Brand metadata (brand.yaml) — controlled inputs.
  const [brandMeta, setBrandMeta] = React.useState({
    slug: brand,
    channel: "",
    timezone: "Europe/Moscow",
    auto_discover_at: "09:00",
    website: "",
    theme_description: "",
    last_bootstrap_at: null,
  });
  const [stats, setStats] = React.useState(null);
  const [bootstrapStats, setBootstrapStats] = React.useState(null);
  const [historyOpen, setHistoryOpen] = React.useState(false);

  React.useEffect(() => {
    if (!brand) return;
    CR_API.getBrandBootstrapStats(brand).then(setBootstrapStats).catch(() => setBootstrapStats(null));
  }, [brand]);

  const tgChannel = brandMeta.channel || "@" + brand;

  // Load real data on first mount + when switching tabs or brand.
  const reload = React.useCallback(async () => {
    if (!brand) return;
    try {
      const [b, s, st, src, statsResp] = await Promise.all([
        CR_API.getBrandYaml(brand).catch(() => null),
        CR_API.getDoc(brand, "settings").catch(() => null),
        CR_API.getDoc(brand, "style").catch(() => null),
        CR_API.getDoc(brand, "sources").catch(() => null),
        CR_API.getBrandStats(brand).catch(() => null),
      ]);
      if (b) setBrandMeta({ ...b, channel: b.channel || "", website: b.website || "", theme_description: b.theme_description || "" });
      if (s) setSettingsBuf(s.text || "");
      if (st) setStyleBuf(st.text || "");
      if (src) setSourcesBuf(src.text || "");
      if (statsResp) setStats(statsResp);
    } catch (e) {
      console.warn("Settings reload failed:", e.message);
    }
  }, [brand]);

  React.useEffect(() => { reload(); }, [reload, tab]);

  const saveDoc = async (name, text, label) => {
    try {
      await CR_API.putDoc(brand, name, text);
      onSave && onSave(label);
    } catch (e) {
      onSave && onSave(`Ошибка: ${e.message}`);
    }
  };

  const saveBrandYaml = async () => {
    try {
      const updated = await CR_API.putBrandYaml(brand, {
        channel: brandMeta.channel,
        timezone: brandMeta.timezone,
        auto_discover_at: brandMeta.auto_discover_at,
        website: brandMeta.website || null,
        theme_description: brandMeta.theme_description || null,
      });
      setBrandMeta({ ...updated, channel: updated.channel || "", website: updated.website || "", theme_description: updated.theme_description || "" });
      onSave && onSave("brand.yaml");
    } catch (e) {
      onSave && onSave(`Ошибка: ${e.message}`);
    }
  };

  return (
    <div className="screen">
      <div className="screen__header">
        <div className="screen__header-row">
          <div style={{ flex: 1 }}>
            <div className="section-label" style={{ marginBottom: 6 }}>Конфиг бренда · {brand}</div>
            <h1 className="screen__title">Настройки</h1>
            <div className="screen__subtitle">Все правки сохраняются в историю — любую можно откатить.</div>
          </div>
        </div>
        <div className="tabs">
          {SETTINGS_TABS.map((t) => (
            <button key={t.id} className={"tab " + (tab === t.id ? "is-active" : "")} onClick={() => setTab(t.id)}>{t.label}</button>
          ))}
        </div>
      </div>

      <div className="settings-body">
        {tab === "brand" && (
          <div className="settings-grid">
            <div>
              <div className="settings-card">
                <div style={{ display: "flex", alignItems: "center" }}>
                  <div className="section-label">О КАНАЛЕ</div>
                  <div style={{ flex: 1 }} />
                  <Btn kind="primary" size="sm" icon={<Icons.Check size={13} />} onClick={saveBrandYaml}>Сохранить</Btn>
                </div>
                <h3 style={{ marginTop: 6 }}>{brandMeta.slug}</h3>
                {brandMeta.theme_description ? (
                  <p>{brandMeta.theme_description}</p>
                ) : (
                  <p style={{ color: "var(--fg-muted)", fontStyle: "italic" }}>
                    Описание тематики появится после первого «Прочитать 100 постов и заполнить» — Claude сгенерирует его на основе постов.
                  </p>
                )}
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginTop: 18 }}>
                  <div>
                    <label className="label">Имя в системе</label>
                    <input className="input" value={brandMeta.slug} disabled />
                    <div style={{ fontSize: 11, color: "var(--fg-muted)", marginTop: 4 }}>read-only — нельзя менять после создания</div>
                  </div>
                  <div>
                    <label className="label">Канал в Telegram</label>
                    <input className="input" value={brandMeta.channel} onChange={(e) => setBrandMeta({ ...brandMeta, channel: e.target.value })} placeholder="@username" />
                  </div>
                  <div>
                    <label className="label">Часовой пояс</label>
                    <input className="input" value={brandMeta.timezone} onChange={(e) => setBrandMeta({ ...brandMeta, timezone: e.target.value })} />
                  </div>
                  <div>
                    <label className="label">Время автопоиска</label>
                    <input className="input" value={brandMeta.auto_discover_at} onChange={(e) => setBrandMeta({ ...brandMeta, auto_discover_at: e.target.value })} placeholder="HH:MM" />
                  </div>
                  <div style={{ gridColumn: "1 / 3" }}>
                    <label className="label">Сайт бренда</label>
                    <input className="input" value={brandMeta.website} onChange={(e) => setBrandMeta({ ...brandMeta, website: e.target.value })} placeholder="https://example.com" />
                    <div style={{ fontSize: 11, color: "var(--fg-muted)", marginTop: 4 }}>
                      Bootstrap прочитает страницу через Firecrawl и достанет описание/услуги/ЦА. Можно оставить пустым.
                    </div>
                  </div>
                </div>
              </div>

              <div className="settings-card" style={{ marginTop: 20 }}>
                <div className="section-label" style={{ color: "var(--status-warn-fg)" }}>АВТОЗАПОЛНЕНИЕ</div>
                <h3 style={{ marginTop: 6 }}>Заполнить настройки автоматически</h3>
                <p>Claude прочитает последние 100 опубликованных постов и сам заполнит вкладки <strong>«Что искать»</strong> и <strong>«Как писать»</strong> на основе вашего канала.</p>
                <div className="settings-warning">
                  <strong>Внимание:</strong> перезапишет существующие настройки и правила стиля. Старые версии остаются в истории — любую можно откатить.
                </div>
                <div style={{ marginTop: 12, display: "flex", gap: 8 }}>
                  <Btn kind="primary" icon={<Icons.Sparkles size={14} />} onClick={() => onBootstrap && onBootstrap(tgChannel, 100)}>Прочитать 100 постов и заполнить</Btn>
                  <Btn kind="ghost" onClick={() => setHistoryOpen(true)}>История правок</Btn>
                </div>
              </div>
            </div>

            <div>
              <div className="settings-card">
                <h4 style={{ fontFamily: "TT Lakes,sans-serif", fontWeight: 600, fontSize: 10, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--fg-muted)", margin: "0 0 10px" }}>Статистика</h4>
                <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
                  <div>
                    <div style={{ fontSize: 32, fontFamily: "Geometria", fontWeight: 700, color: "var(--brand-red)", lineHeight: 1 }}>
                      {stats?.subscribers != null ? stats.subscribers.toLocaleString("ru-RU") : (stats?.subscribers_error ? "—" : "…")}
                    </div>
                    <div style={{ color: "var(--fg-muted)", fontSize: 12 }}>подписчиков</div>
                    {stats?.subscribers_error && (
                      <div style={{ color: "var(--status-error-fg)", fontSize: 10, marginTop: 2 }}>{stats.subscribers_error.slice(0, 60)}</div>
                    )}
                  </div>
                  <div>
                    {bootstrapStats === null ? (
                      <div style={{ color: "var(--fg-muted)", fontSize: 13 }}>…</div>
                    ) : bootstrapStats.has_bootstrap ? (
                      <>
                        <div style={{ fontSize: 11, fontFamily: "TT Lakes,sans-serif", fontWeight: 600, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--fg-muted)", marginBottom: 8 }}>
                          Паспорт канала
                        </div>
                        <div style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 13, lineHeight: 1.4 }}>
                          <div>
                            <span style={{ fontWeight: 700, color: "var(--brand-red)" }}>{bootstrapStats.main_topics_count}</span> тем · <span style={{ fontWeight: 700, color: "var(--brand-red)" }}>{bootstrapStats.keywords_count}</span> ключей · <span style={{ fontWeight: 700, color: "var(--brand-red)" }}>{bootstrapStats.content_formats_count}</span> форматов
                          </div>
                          <div>
                            <span style={{ fontWeight: 700, color: "var(--brand-red)" }}>{bootstrapStats.style_templates_count}</span> шаблона · <span style={{ fontWeight: 700, color: "var(--brand-red)" }}>{bootstrapStats.tg_channels_count + bootstrapStats.web_whitelist_count}</span> источников
                          </div>
                          <div style={{ color: "var(--fg-muted)", fontSize: 11, marginTop: 4 }}>
                            {bootstrapStats.posts_analyzed} постов проанализировано
                          </div>
                        </div>
                      </>
                    ) : (
                      <>
                        <div style={{ fontSize: 11, fontFamily: "TT Lakes,sans-serif", fontWeight: 600, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--fg-muted)", marginBottom: 8 }}>
                          Паспорт канала
                        </div>
                        <div style={{ color: "var(--fg-muted)", fontSize: 13, fontStyle: "italic" }}>
                          Канал ещё не проанализирован
                        </div>
                      </>
                    )}
                  </div>
                  <div>
                    <div style={{ fontSize: 32, fontFamily: "Geometria", fontWeight: 700, color: "var(--brand-red)", lineHeight: 1 }}>
                      {stats?.tokens_this_month != null ? stats.tokens_this_month.toLocaleString("ru-RU") : "…"}
                    </div>
                    <div style={{ color: "var(--fg-muted)", fontSize: 12 }}>
                      токенов · {new Date().toLocaleString("ru-RU", { month: "long" })}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        )}

        {tab === "settings" && (
          <div className="settings-grid">
            <div className="settings-card" style={{ padding: 0, overflow: "hidden" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "12px 16px", borderBottom: "1px solid var(--border)" }}>
                <div className="section-label">settings.md</div>
                <div style={{ flex: 1 }} />
                <Btn kind="ghost" size="sm" onClick={reload}>Отменить правки</Btn>
                <Btn kind="primary" size="sm" icon={<Icons.Check size={13} />} onClick={() => saveDoc("settings", settingsBuf, "settings.md")}>Сохранить</Btn>
              </div>
              <textarea className="editor-area" style={{ minHeight: 600, border: 0, borderRadius: 0 }} value={settingsBuf} onChange={(e) => setSettingsBuf(e.target.value)} spellCheck={false} />
            </div>
            <HelperBlock items={[
              "<code>main_topics</code> — широкие темы для поиска",
              "<code>keywords</code> — точечные ключевики, которые Claude использует в поисковых запросах",
              "<code>content_formats</code> — какие типы материалов нужны",
              "<code>audience</code> — для tone-check на этапе поста",
              "<code>exclude</code> — анти-фильтр (что НЕ берём)",
            ]} />
          </div>
        )}

        {tab === "style" && (
          <div className="settings-grid">
            <div className="settings-card" style={{ padding: 0, overflow: "hidden" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "12px 16px", borderBottom: "1px solid var(--border)" }}>
                <div className="section-label">style_guide.md</div>
                <div style={{ flex: 1 }} />
                <Btn kind="ghost" size="sm" onClick={reload}>Отменить правки</Btn>
                <Btn kind="primary" size="sm" icon={<Icons.Check size={13} />} onClick={() => saveDoc("style", styleBuf, "style_guide.md")}>Сохранить</Btn>
              </div>
              <textarea className="editor-area" style={{ minHeight: 600, border: 0, borderRadius: 0 }} value={styleBuf} onChange={(e) => setStyleBuf(e.target.value)} spellCheck={false} />
            </div>
            <HelperBlock items={[
              "<code>tone</code> — голос канала: 2-3 строки",
              "<code>structure</code> — каркас типового поста",
              "<code>templates</code> — шаблоны для авто-генерации",
              "<code>vocabulary</code> — список «использовать / избегать»",
            ]} />
          </div>
        )}

        {tab === "sources" && (
          <div className="settings-grid">
            <div className="settings-card" style={{ padding: 0, overflow: "hidden" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "12px 16px", borderBottom: "1px solid var(--border)" }}>
                <div className="section-label">sources.md</div>
                <div style={{ flex: 1 }} />
                <Btn kind="ghost" size="sm" onClick={reload}>Отменить правки</Btn>
                <Btn kind="primary" size="sm" icon={<Icons.Check size={13} />} onClick={() => saveDoc("sources", sourcesBuf, "sources.md")}>Сохранить</Btn>
              </div>
              <textarea className="editor-area" style={{ minHeight: 600, border: 0, borderRadius: 0 }} value={sourcesBuf} onChange={(e) => setSourcesBuf(e.target.value)} spellCheck={false} />
            </div>
            <HelperBlock items={[
              "<code>tg_channels</code> — каналы для Read-only мониторинга",
              "<code>web_whitelist</code> — домены, с которых разрешено брать материалы",
              "<code>rss_feeds</code> — фиды, проверяются раз в час",
            ]} />
          </div>
        )}
      </div>

      <HistoryDrawer
        open={historyOpen}
        onClose={() => setHistoryOpen(false)}
        brand={brand}
        onRestored={(id, preId) => {
          console.log("Bootstrap restored:", id, "pre_restore_id:", preId);
          alert("Откат выполнен");
          // Re-fetch bootstrap stats card
          setBootstrapStats(null);
          CR_API.getBrandBootstrapStats(brand).then(setBootstrapStats).catch(() => {});
          // Re-fetch all doc contents so editors reflect restored files
          reload();
        }}
      />
    </div>
  );
};

window.CR_POSTS = { PostsScreen };
window.CR_SETTINGS = { SettingsScreen };
