// Main app shell — auth gate + role-aware sidebar + topbar + page router + tweaks

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#4f9bd1",
  "headingFont": "Source Serif 4",
  "bodyFont": "DM Sans",
  "sidebar": "navy",
  "density": "regular"
}/*EDITMODE-END*/;

const ACCENTS = {
  "#4f9bd1": { soft: "#e6f1f9", strong: "#2f7ab0" },
  "#3aa7a0": { soft: "#dff2f0", strong: "#1f807a" },
  "#5468d6": { soft: "#e6e8f7", strong: "#3845b0" },
  "#c98851": { soft: "#f7ead9", strong: "#a96731" },
};

const SIDEBAR_THEMES = {
  navy:    { bg: "#1a2332", text: "#e8ecf3" },
  charcoal:{ bg: "#1d1f24", text: "#e8ecf3" },
  ivory:   { bg: "#f6f5f1", text: "#22231e" },
};

// Which pages each role may open.
const ROLE_PAGES = {
  admin:      ["dashboard", "orders", "docs", "employees", "chat", "settings"],
  controller: ["dashboard", "orders", "docs", "employees", "chat"],
  executor:   ["orders", "docs", "chat"],
};
const ROLE_LABEL = { admin: "Администратор", controller: "Отдел контроля", executor: "Исполнитель" };

// Hash routing: the page lives in location.hash (#/employees) so switching tabs
// changes the URL and a refresh keeps you on the same page. The hash never hits
// the Go server, so no backend/SPA-fallback changes are needed.
const PAGE_KEYS = ["dashboard", "orders", "docs", "employees", "chat", "settings"];
function pageFromHash() {
  const h = (window.location.hash || "").replace(/^#\/?/, "");
  return PAGE_KEYS.includes(h) ? h : "";
}

function App() {
  const [me, setMe] = React.useState(undefined); // undefined=loading, null=guest, object=user
  const [page, setPage] = React.useState(() => pageFromHash() || "orders");
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [intent, setIntent] = React.useState(null);
  const [overdueCount, setOverdueCount] = React.useState(0);
  const [pwOpen, setPwOpen] = React.useState(false);

  // Who am I?
  React.useEffect(() => {
    if (!window.API) { setMe(null); return; }
    window.API.me().then(u => { window.ME = u; setMe(u); }).catch(() => { window.ME = null; setMe(null); });
  }, []);

  // Browser back/forward (or a manually edited hash) → switch the page.
  React.useEffect(() => {
    const onHash = () => { const p = pageFromHash(); if (p) setPage(p); };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);

  // Keep the URL hash in sync with the active page once authenticated, clamping
  // to pages the role may open (so a disallowed hash redirects to a valid one).
  React.useEffect(() => {
    if (!me) return;
    const allowed = ROLE_PAGES[me.role] || ["orders"];
    const eff = allowed.includes(page) ? page : allowed[0];
    if (eff !== page) { setPage(eff); return; }
    const target = "#/" + eff;
    if (window.location.hash !== target) window.location.hash = target;
  }, [page, me]);

  // Apply tweaks via CSS variables on :root (also on the login screen).
  React.useEffect(() => {
    const root = document.documentElement;
    const a = ACCENTS[t.accent] || ACCENTS["#4f9bd1"];
    root.style.setProperty("--accent", t.accent);
    root.style.setProperty("--accent-soft", a.soft);
    root.style.setProperty("--accent-strong", a.strong);
    root.style.setProperty("--sans", `"${t.bodyFont}", system-ui, sans-serif`);
    root.style.setProperty("--serif", `"${t.headingFont}", Georgia, serif`);
    const sb = SIDEBAR_THEMES[t.sidebar] || SIDEBAR_THEMES.navy;
    root.style.setProperty("--ink-700", sb.bg);
    document.body.style.fontSize = t.density === "compact" ? "13px" : t.density === "comfy" ? "15px" : "14px";
  }, [t.accent, t.headingFont, t.bodyFont, t.sidebar, t.density]);

  React.useEffect(() => {
    const fonts = new Set([t.headingFont, t.bodyFont]);
    fonts.forEach(f => {
      const id = `gf-${f.replace(/\s+/g, "-")}`;
      if (document.getElementById(id)) return;
      const link = document.createElement("link");
      link.id = id;
      link.rel = "stylesheet";
      link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(f)}:wght@400;500;600;700&display=swap`;
      document.head.appendChild(link);
    });
  }, [t.headingFont, t.bodyFont]);

  // Overdue badge — only when authenticated.
  React.useEffect(() => {
    if (!me || !window.API) return;
    if (window.refreshDirectory) window.refreshDirectory(); // live people/departments from the DB
    window.API.listTasks().then(ts => {
      if (!Array.isArray(ts)) return;
      setOverdueCount(ts.filter(x => (typeof effStatus === "function" ? effStatus(x) : x.status) === "overdue").length);
    }).catch(() => {});
  }, [me]);

  const goOrders = (patch) => { setIntent({ ...patch, _ts: Date.now() }); setPage("orders"); };

  if (me === undefined) {
    return <div style={{ display: "grid", placeItems: "center", height: "100vh", color: "var(--ink-400)" }}>Загрузка…</div>;
  }
  if (me === null) {
    return <LoginPage onLogin={u => { window.ME = u; setMe(u); }} />;
  }

  const allowed = ROLE_PAGES[me.role] || ["orders"];
  const PAGES = {
    dashboard: { label: "Дашборд",      icon: I.Dashboard, comp: () => <Dashboard onNavigate={setPage} /> },
    orders:    { label: "Поручения",    icon: I.Clipboard, comp: () => <PorucheniyaPage intent={intent} /> },
    docs:      { label: "Документы",    icon: I.Docs,      comp: () => <Documents /> },
    employees: { label: "Сотрудники",   icon: I.Users,     comp: () => <EmployeesPage onOpenOrders={goOrders} /> },
    chat:      { label: "ИИ-ассистент", icon: I.Sparkle,   comp: () => <ChatPage />, badge: "AI" },
    settings:  { label: "Настройки",    icon: I.Settings,  comp: () => <SettingsPage /> },
  };
  const effPage = allowed.includes(page) ? page : allowed[0];
  const CurrentPage = PAGES[effPage].comp;
  const currentLabel = PAGES[effPage].label;
  const mainNav = allowed.filter(k => k !== "settings");
  const sb = SIDEBAR_THEMES[t.sidebar];
  const sidebarStyle = { background: sb.bg, color: sb.text };
  const logout = () => { window.API.logout().finally(() => { window.ME = null; setMe(null); }); };

  return (
    <div className="app" data-screen-label={`crm-${effPage}`}>
      <aside className="sidebar" style={sidebarStyle}>
        <div className="brand">
          <div className="brand-mark">П</div>
          <div className="brand-name">Паблисити <em>CRM</em></div>
        </div>

        <div className="org-switch" style={{ cursor: "default" }}>
          <div className="logo">ЮЦ</div>
          <div className="meta">
            <div className="name">Юридический центр</div>
            <div className="sub">Контроль поручений</div>
          </div>
        </div>

        <div className="nav-section-label">Главное</div>
        <nav className="nav">
          {mainNav.map(k => {
            const v = PAGES[k];
            const Ic = v.icon;
            return (
              <button key={k} className={`nav-item ${effPage === k ? "active" : ""}`} onClick={() => setPage(k)}>
                <Ic size={16} />
                <span>{v.label}</span>
                {v.badge && <span className="badge">{v.badge}</span>}
              </button>
            );
          })}
        </nav>

        {allowed.includes("settings") && (
          <>
            <div className="nav-section-label">Система</div>
            <nav className="nav">
              <button className={`nav-item ${effPage === "settings" ? "active" : ""}`} onClick={() => setPage("settings")}>
                <I.Settings size={16} />
                <span>Настройки</span>
              </button>
            </nav>
          </>
        )}

        <div className="sidebar-spacer"></div>

        <div className="sidebar-footer">
          <div className="avatar" title="Сменить пароль" style={{ cursor: "pointer" }} onClick={() => setPwOpen(true)}>{initials(me.name || me.login)}</div>
          <div className="user-meta" title="Сменить пароль" style={{ cursor: "pointer" }} onClick={() => setPwOpen(true)}>
            <div className="name">{me.name || me.login}</div>
            <div className="role">{ROLE_LABEL[me.role] || me.role}</div>
          </div>
          <button className="icon-btn" title="Выйти" onClick={logout}><I.Logout size={14} /></button>
        </div>
      </aside>

      <main className="main">
        <div className="topbar">
          <div className="crumbs">
            <I.Building size={13} />
            <span>Юр. центр</span>
            <span className="sep">/</span>
            <span className="current">{currentLabel}</span>
          </div>

          <TopbarSearch onQuery={(query) => goOrders({ query })} onPerson={(assignee) => goOrders({ assignee })} />

          <div className="top-actions">
            <button className="icon-btn" style={{ position: "relative" }} title={`Просрочено: ${overdueCount}`} onClick={() => goOrders({ status: "overdue", allYear: true })}>
              <I.Bell size={16} />
              {overdueCount > 0 && (
                <span style={{ position: "absolute", top: -3, right: -3, background: "var(--err)", color: "#fff", fontSize: 9.5, fontWeight: 600, padding: "1px 5px", borderRadius: 9, lineHeight: 1.4 }}>
                  {overdueCount > 99 ? "99+" : overdueCount}
                </span>
              )}
            </button>
            <button className="icon-btn" title="Помощь" onClick={() => window.alert("Справочный центр пока в разработке.")}>
              <I.Help size={16} />
            </button>
          </div>
        </div>

        <CurrentPage />
      </main>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Тема" />
        <TweakColor label="Акцент" value={t.accent} options={Object.keys(ACCENTS)} onChange={(v) => setTweak("accent", v)} />
        <TweakRadio label="Сайдбар" value={t.sidebar} options={["navy", "charcoal", "ivory"]} onChange={(v) => setTweak("sidebar", v)} />
        <TweakSection label="Шрифты" />
        <TweakSelect label="Заголовки" value={t.headingFont} options={["Source Serif 4", "Fraunces", "DM Sans", "Manrope", "Bricolage Grotesque"]} onChange={(v) => setTweak("headingFont", v)} />
        <TweakSelect label="Текст" value={t.bodyFont} options={["DM Sans", "Source Sans 3", "Manrope", "IBM Plex Sans"]} onChange={(v) => setTweak("bodyFont", v)} />
        <TweakSection label="Плотность" />
        <TweakRadio label="Размер" value={t.density} options={["compact", "regular", "comfy"]} onChange={(v) => setTweak("density", v)} />
      </TweaksPanel>

      {pwOpen && <ChangePasswordModal onClose={() => setPwOpen(false)} />}
    </div>
  );
}

// Topbar search with live suggestions: matching employees (→ open their поручения)
// and a free-text option (→ search task text). Backed by the live directory.
function TopbarSearch({ onQuery, onPerson }) {
  const { people } = useDirectory();
  const [q, setQ] = React.useState("");
  const [open, setOpen] = React.useState(false);
  const [hi, setHi] = React.useState(0);
  const ref = React.useRef(null);
  const ql = q.trim().toLowerCase();
  const matches = ql ? Object.keys(people).filter(n => n.toLowerCase().includes(ql)).slice(0, 6) : [];

  React.useEffect(() => {
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);

  const submitQuery = () => { if (q.trim()) { onQuery(q.trim()); setOpen(false); } };
  const pickPerson = (name) => { onPerson(name); setQ(""); setOpen(false); };
  const onKey = (e) => {
    if (e.key === "Enter") { e.preventDefault(); if (open && matches[hi]) pickPerson(matches[hi]); else submitQuery(); }
    else if (e.key === "ArrowDown") { e.preventDefault(); setOpen(true); setHi(h => Math.min(matches.length - 1, h + 1)); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setHi(h => Math.max(0, h - 1)); }
    else if (e.key === "Escape") setOpen(false);
  };

  return (
    <div className="search" ref={ref} style={{ position: "relative" }}>
      <I.Search size={14} />
      <input
        placeholder="Поиск по поручениям и сотрудникам… (Enter)"
        value={q}
        onChange={e => { setQ(e.target.value); setOpen(true); setHi(0); }}
        onFocus={() => setOpen(true)}
        onKeyDown={onKey}
      />
      <span className="kbd">⌘K</span>
      {open && (matches.length > 0 || q.trim()) && (
        <div style={{ position: "absolute", top: "calc(100% + 6px)", left: 0, right: 0, background: "var(--paper)", border: "1px solid var(--ink-100)", borderRadius: "var(--r-md)", boxShadow: "var(--sh-2)", overflow: "hidden", zIndex: 300 }}>
          {matches.length > 0 && <div style={{ padding: "6px 11px", fontSize: 10.5, textTransform: "uppercase", letterSpacing: ".05em", color: "var(--ink-300)" }}>Сотрудники</div>}
          {matches.map((n, i) => (
            <div key={n} onMouseEnter={() => setHi(i)} onMouseDown={(e) => { e.preventDefault(); pickPerson(n); }}
              style={{ display: "flex", justifyContent: "space-between", gap: 10, padding: "7px 11px", fontSize: 13, cursor: "pointer", background: i === hi ? "var(--ink-50)" : "transparent" }}>
              <span>{n}</span>
              <span style={{ color: "var(--ink-400)", fontSize: 11.5 }}>{people[n] && people[n].dept}</span>
            </div>
          ))}
          {q.trim() && (
            <div onMouseDown={(e) => { e.preventDefault(); submitQuery(); }}
              style={{ display: "flex", alignItems: "center", gap: 6, padding: "8px 11px", fontSize: 12.5, cursor: "pointer", borderTop: matches.length > 0 ? "1px solid var(--ink-100)" : "none", color: "var(--accent-strong)" }}>
              <I.Search size={11} /> Искать «{q.trim()}» по тексту поручений
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function LoginPage({ onLogin }) {
  const [login, setLogin] = React.useState("");
  const [password, setPassword] = React.useState("");
  const [err, setErr] = React.useState("");
  const [busy, setBusy] = React.useState(false);

  const submit = async (e) => {
    if (e) e.preventDefault();
    if (!login.trim() || !password) return;
    setBusy(true); setErr("");
    try {
      const u = await window.API.login(login.trim(), password);
      onLogin(u);
    } catch (e2) {
      setErr(String((e2 && e2.message) || e2));
      setBusy(false);
    }
  };

  return (
    <div style={{ minHeight: "100vh", display: "grid", placeItems: "center", background: "var(--bg)" }}>
      <form className="card" style={{ width: 360, maxWidth: "92vw", boxShadow: "var(--sh-3)" }} onSubmit={submit}>
        <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 14, padding: 28 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 2 }}>
            <div className="brand-mark" style={{ width: 34, height: 34 }}>П</div>
            <div className="brand-name" style={{ fontSize: 20, color: "var(--ink-700)" }}>Паблисити <em>CRM</em></div>
          </div>
          <div style={{ fontSize: 13, color: "var(--ink-400)" }}>Вход в систему контроля поручений</div>
          <div>
            <label className="fld-label">Логин</label>
            <input className="fld" style={{ width: "100%" }} value={login} onChange={e => setLogin(e.target.value)} autoFocus />
          </div>
          <div>
            <label className="fld-label">Пароль</label>
            <input className="fld" style={{ width: "100%" }} type="password" value={password} onChange={e => setPassword(e.target.value)} />
          </div>
          {err && <div style={{ color: "var(--err)", fontSize: 12.5 }}>{err}</div>}
          <button className="btn btn-accent" type="submit" disabled={busy || !login.trim() || !password} style={{ justifyContent: "center" }}>
            {busy ? "Вход…" : "Войти"}
          </button>
        </div>
      </form>
    </div>
  );
}

function ChangePasswordModal({ onClose }) {
  const [oldP, setOldP] = React.useState("");
  const [newP, setNewP] = React.useState("");
  const [msg, setMsg] = React.useState("");
  const [busy, setBusy] = React.useState(false);

  const submit = async () => {
    if (!oldP || newP.length < 6) { setMsg("Новый пароль — минимум 6 символов"); return; }
    setBusy(true); setMsg("");
    try {
      await window.API.changePassword(oldP, newP);
      setMsg("Пароль изменён ✓");
      setOldP(""); setNewP("");
      setTimeout(onClose, 900);
    } catch (e) {
      setMsg(String((e && e.message) || e));
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="tg-overlay open" style={{ background: "rgba(13,22,32,.5)", display: "grid", placeItems: "center", zIndex: 260 }} onClick={onClose}>
      <div className="card" style={{ width: 380, maxWidth: "92vw", boxShadow: "var(--sh-3)" }} onClick={e => e.stopPropagation()}>
        <div className="card-head">
          <div style={{ flex: 1 }}><h3 className="card-title">Смена пароля</h3></div>
          <button className="icon-btn" style={{ color: "var(--ink-400)" }} onClick={onClose}><I.X size={18} /></button>
        </div>
        <div className="card-body" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <div><label className="fld-label">Текущий пароль</label><input className="fld" style={{ width: "100%" }} type="password" value={oldP} onChange={e => setOldP(e.target.value)} /></div>
          <div><label className="fld-label">Новый пароль</label><input className="fld" style={{ width: "100%" }} type="password" value={newP} onChange={e => setNewP(e.target.value)} /></div>
          {msg && <div style={{ fontSize: 12.5, color: msg.includes("✓") ? "var(--ok)" : "var(--err)" }}>{msg}</div>}
        </div>
        <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: "0 20px 18px" }}>
          <button className="btn btn-ghost" onClick={onClose}>Отмена</button>
          <button className="btn btn-accent" onClick={submit} disabled={busy}>Сохранить</button>
        </div>
      </div>
    </div>
  );
}

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