// app.jsx — root component (sidebar + main + cmd+k + tweaks)

const { useState: useStateA, useEffect: useEffectA, useRef: useRefA } = React;


/**
 * PageWrapper — сохраняет состояние страницы между переключениями вкладок.
 *
 * Раньше main-контейнер использовал `<div key={active}>` + условный рендер,
 * из-за чего React при каждом переключении полностью unmount-ил предыдущую
 * страницу. Терялись: выбранный чат, скролл, фильтры, набираемый текст,
 * in-flight запросы (без AbortController оставались висеть и писать в
 * размонтированный setState → React warnings). Это был главный root cause
 * ощущения «забывает где я был / нажал и ничего не сохранилось».
 *
 * Сейчас:
 *  - Lazy mount: страница монтируется при первом visible=true. Если юзер
 *    никогда не заходил на «Чеки» — компонент не запускается совсем.
 *  - Keep alive: после первого монтирования компонент **остаётся** в DOM,
 *    скрытый через display:none. Его state, polling-таймеры, in-flight
 *    fetch и т.п. — продолжают жить. Юзер возвращается — всё на месте.
 *
 * Trade-off: polling всех ранее открытых страниц продолжает работать в фоне.
 * Для большинства случаев это нормально (запросы лёгкие). Если станет тяжело
 * — можно подключить Page Visibility API и паузить polling когда invisible.
 */
function PageWrapper({ visible, children }) {
  const [mounted, setMounted] = useStateA(visible);
  useEffectA(() => {
    if (visible && !mounted) setMounted(true);
  }, [visible, mounted]);
  if (!mounted) return null;
  return <div style={{ display: visible ? "block" : "none" }}>{children}</div>;
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "homeVariant": "B",
  "density": "regular",
  "accent": "indigo",
  "sidebar": "full"
}/*EDITMODE-END*/;

const ACCENT_MAP = {
  indigo:  "#6366f1",
  emerald: "#10b981",
  rose:    "#f43f5e",
  amber:   "#f59e0b",
  fuchsia: "#d946ef",
};

// Логотип сайдбара: реальный Telegram-аватар БОТА (/api/owners/bot/avatar).
// Пока фото грузится или если его нет (404/ошибка) — прежний зелёный градиент
// (тот же контейнер w-7 h-7 rounded-lg, размер/скругление не меняются).
function BrandMark() {
  const [imgFailed, setImgFailed] = useStateA(false);
  return (
    <div className="w-7 h-7 rounded-lg shadow-lg shrink-0 overflow-hidden"
         style={{ background: "linear-gradient(160deg, #b9dc77, #567f22)", boxShadow: "0 8px 18px -8px rgba(86,127,34,.5)" }}>
      {!imgFailed && (
        <img src="/api/owners/bot/avatar" alt="" onError={() => setImgFailed(true)}
             className="w-full h-full object-cover" />
      )}
    </div>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [active, setActive] = useStateA("home");
  // Мобильное меню: на узких экранах (телефон) сайдбар прячется и выезжает
  // по кнопке-бургеру, чтобы контент занимал всю ширину, а не уезжал за край.
  const [mobileNavOpen, setMobileNavOpen] = useStateA(false);
  const [cmdkOpen, setCmdkOpen] = useStateA(false);
  const [hint, setHint] = useStateA(null);
  const [statusPills, setStatusPills] = useStateA(STATUS_PILLS);
  const gTimer = useRefA(null);

  const allItems = NAV.flatMap((g) => g.items);
  const current = allItems.find((n) => n.id === active);

  /* Polling статуса бота: первый запрос сразу + потом каждые 10 сек.
     Берёт реальные данные из /api/status. */
  useEffectA(() => {
    let stopped = false;
    const refresh = async () => {
      const pills = await fetchStatus();
      if (!stopped) setStatusPills(pills);
    };
    refresh();
    const id = setInterval(refresh, 10_000);
    return () => { stopped = true; clearInterval(id); };
  }, []);

  // Density CSS var (could be expanded — for now just applied to spacing inline)
  const densityScale = t.density === "compact" ? 0.85 : t.density === "spacious" ? 1.15 : 1;

  // [бежевый редизайн] Хоткеи (⌘K + g-leader переключение разделов) убраны по просьбе.

  // Sidebar collapse
  const sidebarCollapsed = t.sidebar === "icons";
  const accent = ACCENT_MAP[t.accent] || ACCENT_MAP.indigo;

  return (
    <div
      className="min-h-screen bg-transparent text-zinc-100 flex gap-3 p-2 sm:p-4 font-sans selection:bg-[rgba(122,93,68,.18)] max-w-[1620px] mx-auto"
      style={{ "--accent": accent }}
    >
      {/* Затемнение под выехавшим меню — только на узких экранах, клик закрывает. */}
      {mobileNavOpen && (
        <div className="lg:hidden fixed inset-0 bg-black/50 z-40" onClick={() => setMobileNavOpen(false)} />
      )}
      <aside
        className={
          "glass-soft rounded-3xl backdrop-blur-md flex flex-col overflow-hidden transition-[width,transform] duration-300 "
          // Десктоп: обычный столбец слева, ширина зависит от collapse.
          + "lg:shrink-0 lg:static lg:translate-x-0 "
          + (sidebarCollapsed ? "lg:w-[68px] " : "lg:w-[268px] ")
          // Телефон/планшет: выезжающая панель поверх контента, всегда полной ширины.
          + "max-lg:fixed max-lg:inset-y-2 max-lg:left-2 max-lg:z-50 max-lg:w-[280px] max-lg:shadow-2xl "
          + (mobileNavOpen ? "max-lg:translate-x-0" : "max-lg:-translate-x-[115%]")
        }
      >
        <div className={"px-5 pt-5 pb-4 " + (sidebarCollapsed ? "px-3" : "")}>
          {!sidebarCollapsed && (
            <div className="flex gap-1.5 mb-4">
              <span className="w-3 h-3 rounded-full" style={{ background: "#e9866b", boxShadow: "inset 0 0 0 .5px rgba(0,0,0,.18)" }} />
              <span className="w-3 h-3 rounded-full" style={{ background: "#f0bd55", boxShadow: "inset 0 0 0 .5px rgba(0,0,0,.18)" }} />
              <span className="w-3 h-3 rounded-full" style={{ background: "#79c47f", boxShadow: "inset 0 0 0 .5px rgba(0,0,0,.18)" }} />
            </div>
          )}
          <div className="flex items-center gap-2.5">
            <BrandMark />
            {!sidebarCollapsed && <div className="text-base font-semibold tracking-tight">Zxckotik</div>}
          </div>
        </div>

        <nav className="flex-1 px-3 py-2 space-y-6 overflow-y-auto hide-scrollbar">
          {NAV.map((g) => (
            <div key={g.group}>
              {!sidebarCollapsed && (
                <div className="px-3 mb-2 text-[10px] uppercase tracking-[0.12em] text-zinc-600 font-semibold">
                  {g.group}
                </div>
              )}
              <div className="space-y-0.5">
                {g.items.map((item) => {
                  const isActive = active === item.id;
                  return (
                    <button
                      key={item.id}
                      onClick={() => { setActive(item.id); setMobileNavOpen(false); }}
                      title={sidebarCollapsed ? item.label + " (g " + item.hotkey.split(" ")[1] + ")" : ""}
                      className={"w-full flex items-center gap-[11px] px-3 py-[9px] rounded-xl text-[13.5px] transition-all relative group " + (isActive ? "bg-[rgba(27,25,21,.07)] text-zinc-50 font-semibold shadow-[inset_0_1px_0_rgba(255,255,255,.5)]" : "text-zinc-400 hover:text-zinc-100 hover:bg-zinc-900/50") + (sidebarCollapsed ? " justify-center px-0" : "")}
                    >
                      <Icon name={item.icon} className="w-[18px] h-[18px]" />
                      {!sidebarCollapsed && (
                        <>
                          <span className="font-medium">{item.label}</span>
                          <span className="ml-auto flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
                            {item.hotkey.split(" ").map((k, i) => (
                              <Kbd key={i}>{k.toUpperCase()}</Kbd>
                            ))}
                          </span>
                        </>
                      )}
                    </button>
                  );
                })}
              </div>
            </div>
          ))}
        </nav>

        <div className="p-3 border-t border-zinc-900">
          {(() => {
            // Source of truth — statusPills из /api/status (тот же что в header).
            const botPill = statusPills.find((p) => p.label === "Бот") || { value: "…", color: "zinc" };
            const tgPill = statusPills.find((p) => p.label === "Telegram") || { value: "…", color: "zinc" };
            const llmPill = statusPills.find((p) => p.label === "LLM") || { value: "…", color: "zinc" };
            const errPill = statusPills.find((p) => p.label === "Ошибки") || { value: "0", color: "zinc" };
            const dotColor = {
              emerald: "bg-emerald-400",
              rose: "bg-rose-400",
              amber: "bg-amber-400",
              zinc: "bg-zinc-500",
            }[botPill.color] || "bg-zinc-500";
            const subOk = tgPill.color === "emerald" && llmPill.color === "emerald";
            return !sidebarCollapsed ? (
              <div className="px-3 py-2.5 rounded-lg bg-zinc-900/60 border border-zinc-800/60">
                <div className="flex items-center justify-between mb-1.5">
                  <div className="flex items-center gap-2">
                    <div className={"w-1.5 h-1.5 rounded-full " + dotColor + (botPill.color === "emerald" ? " live-pulse" : "")} />
                    <span className="text-xs font-medium">Бот {botPill.value}</span>
                  </div>
                </div>
                <div className="flex items-center justify-between text-[10px] text-zinc-500">
                  <span>TG · LLM · {subOk ? "OK" : "сбой"}</span>
                  <span>{errPill.value} ошибок</span>
                </div>
              </div>
            ) : (
              <div className="flex justify-center">
                <div className={"w-2 h-2 rounded-full " + dotColor + (botPill.color === "emerald" ? " live-pulse" : "")} title={"Бот " + botPill.value} />
              </div>
            );
          })()}
        </div>
      </aside>

      <main className="flex-1 flex flex-col min-w-0 glass rounded-3xl overflow-hidden">
        <header className="h-14 border-b border-zinc-900 px-4 sm:px-6 flex items-center gap-3 shrink-0">
          {/* Кнопка-бургер — только на узких экранах, открывает боковое меню. */}
          <button
            className="lg:hidden -ml-1 p-2 rounded-lg text-zinc-300 hover:bg-zinc-900/50"
            onClick={() => setMobileNavOpen(true)}
            aria-label="Меню"
          >
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" />
            </svg>
          </button>
          <div className="lg:hidden text-sm font-semibold tracking-tight">Zxckotik</div>
          <div className="ml-auto flex items-center gap-2">
            {statusPills.map((s) => (
              <div
                key={s.label}
                className={"hidden lg:flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg border text-[11px] " + ({
                  emerald: "bg-emerald-500/10 border-emerald-500/20 text-emerald-400",
                  rose:    "bg-rose-500/10 border-rose-500/20 text-rose-400",
                  amber:   "bg-amber-500/10 border-amber-500/20 text-amber-400",
                  zinc:    "bg-zinc-900/60 border-zinc-800 text-zinc-400",
                }[s.color] || "bg-zinc-900/60 border-zinc-800 text-zinc-400")}
              >
                <div className={"w-1.5 h-1.5 rounded-full " + ({
                  emerald: "bg-emerald-400",
                  rose:    "bg-rose-400",
                  amber:   "bg-amber-400",
                  zinc:    "bg-zinc-500",
                }[s.color] || "bg-zinc-500")} />
                <span className="font-medium">{s.label}:</span>
                <span>{s.value}</span>
              </div>
            ))}
          </div>
        </header>

        <div
          className="flex-1 px-4 sm:px-8 py-5 sm:py-7 overflow-y-auto hide-scrollbar"
          style={{ fontSize: densityScale === 1 ? undefined : `${densityScale}rem` }}
        >
          {/* Каждая страница в PageWrapper'е → keep-alive между переключениями.
              При первом visit ленится монтироваться, дальше живёт скрытая.
              Это убирает «забывает где я был» — главный source ощущения,
              что админка ненадёжная. */}
          <PageWrapper visible={active === "home"}>
            <HomePage variant={t.homeVariant} />
          </PageWrapper>
          <PageWrapper visible={active === "stats"}><StatsPage /></PageWrapper>
          <PageWrapper visible={active === "chats"}><ChatsPage /></PageWrapper>
          <PageWrapper visible={active === "accounts"}><AccountsPage /></PageWrapper>
          <PageWrapper visible={active === "prices"}><PricesPage /></PageWrapper>
          <PageWrapper visible={active === "requisites"}><RequisitesPage /></PageWrapper>
          <PageWrapper visible={active === "content"}><ContentPage /></PageWrapper>
          <PageWrapper visible={active === "checks"}><ChecksPage /></PageWrapper>
          <PageWrapper visible={active === "payments"}><PaymentsPage /></PageWrapper>
          <PageWrapper visible={active === "settings"}><SettingsPage /></PageWrapper>
        </div>
      </main>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Главная" />
        <TweakRadio
          label="Вариант"
          value={t.homeVariant}
          options={[
            { value: "A", label: "Классика" },
            { value: "B", label: "Live-pulse" },
          ]}
          onChange={(v) => setTweak("homeVariant", v)}
        />
        <TweakSection label="Внешний вид" />
        <TweakRadio
          label="Плотность"
          value={t.density}
          options={[
            { value: "compact", label: "Плотно" },
            { value: "regular", label: "Норма" },
            { value: "spacious", label: "Просторно" },
          ]}
          onChange={(v) => setTweak("density", v)}
        />
        <TweakColor
          label="Акцент"
          value={ACCENT_MAP[t.accent] || ACCENT_MAP.indigo}
          options={Object.values(ACCENT_MAP)}
          onChange={(hex) => {
            const k = Object.entries(ACCENT_MAP).find(([_, v]) => v.toLowerCase() === String(hex).toLowerCase());
            setTweak("accent", k ? k[0] : "indigo");
          }}
        />
        <TweakRadio
          label="Сайдбар"
          value={t.sidebar}
          options={[
            { value: "full", label: "Полный" },
            { value: "icons", label: "Иконки" },
          ]}
          onChange={(v) => setTweak("sidebar", v)}
        />
      </TweaksPanel>
    </div>
  );
}

function Root() {
  // null = проверка ещё не завершена (показываем пустой экран чтобы не
  // мелькала login-форма до того как cookie проверится).
  const [authed, setAuthed] = useStateA(null);

  useEffectA(() => {
    let stopped = false;
    checkAuth().then((ok) => { if (!stopped) setAuthed(ok); });
    // Если mutating endpoint вернёт 401 — слушаем глобальное событие и
    // показываем login-форму (cookie мог истечь).
    const onAuthRequired = () => setAuthed(false);
    window.addEventListener("auth-required", onAuthRequired);
    return () => { stopped = true; window.removeEventListener("auth-required", onAuthRequired); };
  }, []);

  // <Toaster/> монтируем рядом с любым экраном (login или App) чтобы
  // ошибки login/network тоже могли пушиться в toast. Toaster сам себя
  // прячет когда списка нет — нулевой overhead.
  const ToasterEl = typeof Toaster !== "undefined" ? <Toaster /> : null;

  // Sprint β — Debug HUD overlay (passive). Активация: ?debug=1 в URL
  // или localStorage.debugHud=1. Без активации компонент возвращает null.
  // Можем монтировать безусловно — он сам решает показываться или нет.
  const DebugHudEl = typeof DebugHud !== "undefined" ? <DebugHud /> : null;

  if (authed === null) {
    return <><div className="min-h-screen bg-zinc-950" />{ToasterEl}{DebugHudEl}</>;
  }
  if (!authed) {
    return <><LoginScreen onSuccess={() => setAuthed(true)} />{ToasterEl}{DebugHudEl}</>;
  }
  return <><App />{ToasterEl}{DebugHudEl}</>;
}

ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
