/* AAU CRM — App shell: TopBar + 2-level Nav + hash router */

const NAV = [
  { label: 'Tổng quan', items: [
    { label: 'Executive Dashboard', icon: 'dashboard', path: '/exec' },
    { label: 'Flow Map', icon: 'flow', path: '/flow' },
    { label: 'Lập kế hoạch', icon: 'target', path: '/planning' },
    { label: 'Theo dõi KPI', icon: 'chart', path: '/planning/tracking' },
  ]},
  { label: 'Marketing', items: [
    { label: 'Marketing Overview', icon: 'dashboard', path: '/marketing/overview' },
    { label: 'Nguồn dữ liệu', icon: 'layers', path: '/marketing/connections' },
    { label: 'Ad Tracking', icon: 'megaphone', path: '/marketing/ads' },
    { label: 'Content Analytics', icon: 'fileText', path: '/marketing/content' },
    { label: 'Lịch nội dung', icon: 'calendar', path: '/marketing/calendar' },
    { label: 'Source Performance', icon: 'chart', path: '/marketing/sources' },
    { label: 'Attribution', icon: 'flow', path: '/marketing/attribution' },
    { label: 'Cấu hình Marketing', icon: 'settings', path: '/marketing/settings' },
  ]},
  { label: 'Inbox', items: [
    { label: 'Hội thoại', icon: 'inbox', path: '/inbox', dynamicBadge: 'inbox' },
    { label: 'Báo cáo Inbox', icon: 'chart', path: '/inbox/report' },
    { label: 'Nhật ký cuộc gọi', icon: 'phone', path: '/calls' },
  ]},
  { label: 'Sales CRM', items: [
    { label: 'Hôm nay', icon: 'bolt', path: '/sales/today' },
    { label: 'Pipeline Kanban', icon: 'kanban', path: '/leads/kanban' },
    { label: 'Bảng Pipeline', icon: 'table', path: '/leads' },
    { label: 'Hồ sơ 360°', icon: 'user', path: '/leads/L1' },
    { label: 'Sales Activity', icon: 'chart', path: '/sales/activity' },
    { label: 'Báo cáo Sales', icon: 'chart', path: '/sales/report' },
    { label: 'Cấu hình & Playbook', icon: 'settings', path: '/leads/pipeline-config' },
  ]},
  { label: 'Vận hành', items: [
    { label: 'Khóa học', icon: 'graduation', path: '/courses' },
    { label: 'Lịch khai giảng', icon: 'calendar', path: '/classes' },
    { label: 'Ghi danh', icon: 'users', path: '/enrollments' },
    { label: 'Giảng viên', icon: 'user', path: '/instructors' },
  ]},
  { label: 'Tài chính', items: [
    { label: 'Tổng quan hợp nhất', icon: 'dollar', path: '/finance' },
    { label: 'P&L theo khóa', icon: 'graduation', path: '/finance/cohorts' },
    { label: 'Báo cáo doanh thu', icon: 'chart', path: '/finance/revenue' },
    { label: 'So sánh kỳ', icon: 'pieChart', path: '/finance/compare' },
    { label: 'Nguồn & Nhập liệu', icon: 'table', path: '/finance/sources' },
  ]},
  { label: 'Enablement & AI', items: [
    { label: 'Conversation Intel', icon: 'target', path: '/enablement/intelligence' },
    { label: 'Asset Library', icon: 'layers', path: '/enablement/assets' },
    { label: 'Script Flow', icon: 'flow', path: '/enablement/scripts' },
    { label: 'Knowledge Base', icon: 'book', path: '/enablement/kb' },
    { label: 'AI Chat Config', icon: 'robot', path: '/settings/ai-chat' },
  ]},
  { label: 'QC Engine', items: [
    { label: 'QC Review', icon: 'sparkles', path: '/qc/review', badge: 6 },
    { label: 'Rulesets', icon: 'checkCircle', path: '/qc/rulesets' },
    { label: 'QC Dashboard', icon: 'chart', path: '/qc/dashboard' },
    { label: 'QC Daily Report', icon: 'fileText', path: '/qc/reports' },
  ]},
  { label: 'Hệ thống', items: [
    { label: 'Quản lý người dùng', icon: 'users', path: '/admin/users' },
    { label: 'RBAC', icon: 'shield', path: '/admin/permissions' },
    { label: 'Nguồn kết nối', icon: 'message', path: '/system/channels' },
    { label: 'MCP Connections', icon: 'externalLink', path: '/system/mcp' },
    { label: 'Agents Center', icon: 'robot', path: '/system/agents' },
    { label: 'Cấu hình AI', icon: 'sparkles', path: '/system/ai-settings' },
    { label: 'Nhật ký & Chi phí', icon: 'fileText', children: [
      { label: 'Nhật ký hoạt động', path: '/system/activity' },
      { label: 'Nhật ký thông báo', path: '/system/notifications' },
      { label: 'AI Cost', path: '/system/ai-cost' },
    ]},
    { label: 'Dữ liệu mẫu', icon: 'layers', path: '/system/demo' },
    { label: 'Nhật ký cập nhật', icon: 'fileText', path: '/changelog' },
    { label: 'System Settings', icon: 'settings', path: '/settings' },
  ]},
];

/* ── Universal search: not just features. Indexes screens + live data
   (khách hàng, hội thoại, khóa học, lớp, giảng viên) so the box answers
   the user's actual intent — a matching feature surfaces first, otherwise
   it behaves like a normal flexible search over everything. ── */
function stripDia(s) {
  return (s == null ? '' : String(s)).toLowerCase()
    .normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/đ/g, 'd');
}

// Result categories — order defines how groups stack in the panel.
const SEARCH_KINDS = {
  feature:    { label: 'Màn hình & tính năng', order: 0, cap: 6, hint: 'mở' },
  lead:       { label: 'Khách hàng & Lead',    order: 1, cap: 5, hint: 'hồ sơ' },
  conv:       { label: 'Hội thoại',            order: 2, cap: 4, hint: 'mở inbox' },
  course:     { label: 'Khóa học',             order: 3, cap: 4, hint: 'xem' },
  class:      { label: 'Lớp khai giảng',       order: 4, cap: 3, hint: 'xem' },
  instructor: { label: 'Giảng viên',           order: 5, cap: 3, hint: 'xem' },
};

function buildSearchIndex() {
  const items = [];
  // 1) Screens / features from the nav tree
  for (const g of NAV) {
    for (const it of g.items) {
      if (it.path) items.push({ kind: 'feature', label: it.label, sub: g.label, path: it.path, icon: it.icon, badge: it.badge });
      if (it.children) for (const c of it.children) {
        items.push({ kind: 'feature', label: c.label, sub: g.label + ' · ' + it.label, path: c.path, icon: it.icon, badge: c.badge });
      }
    }
  }
  const A = window.AAU;
  if (A) {
    // 2) Khách hàng / Lead → Hồ sơ 360°
    (A.leads || []).forEach(l => {
      if (l.name === 'Spam Bot') return;
      const st = A.stageById ? A.stageById(l.stage) : null;
      items.push({
        kind: 'lead', label: l.name, path: '/leads/' + l.id, icon: 'user',
        sub: [l.company && l.company !== '—' ? l.company : null, l.region, st && st.name].filter(Boolean).join(' · '),
        meta: l.hot ? '🔥' : (l.grade ? l.grade : ''),
        text: [l.name, l.company, l.phone, l.email, l.region, l.industry, l.bizModel, (l.painpoints || []).join(' ')].join(' '),
      });
    });
    // 3) Hội thoại → Inbox (deep-link to the conversation)
    Object.values(A.conversations || {}).forEach(cv => {
      const l = A.leadById(cv.leadId);
      const last = cv.messages && cv.messages[cv.messages.length - 1];
      const lastTxt = last ? (last.text || (last.attachment ? '📎 ' + last.attachment : '')) : '';
      items.push({
        kind: 'conv', label: l ? l.name : cv.leadId, path: '/inbox', icon: 'inbox',
        intent: { inboxConv: cv.id },
        sub: cv.channel.toUpperCase() + (lastTxt ? ' · “' + lastTxt.slice(0, 46) + (lastTxt.length > 46 ? '…' : '') + '”' : ''),
        badge: cv.unread || undefined,
        text: [l ? l.name : '', l ? l.company : '', cv.channel, (cv.messages || []).map(m => m.text).join(' ')].join(' '),
      });
    });
    // 4) Khóa học
    (A.courses || []).forEach(c => items.push({
      kind: 'course', label: c.name, path: '/courses', icon: 'graduation',
      sub: [c.code, A.courseGroupLabels ? A.courseGroupLabels[c.group] : c.group, c.sessions ? c.sessions + ' buổi' : null].filter(Boolean).join(' · '),
      text: [c.name, c.code, c.en, c.desc].join(' '),
    }));
    // 5) Lớp khai giảng
    (A.classes || []).forEach(cl => {
      const c = A.courseById ? A.courseById(cl.course) : null;
      items.push({
        kind: 'class', label: (c ? c.name : cl.course) + ' — ' + cl.batch, path: '/classes', icon: 'calendar',
        sub: [cl.start, cl.patLabel, cl.cat].filter(Boolean).join(' · '),
        text: [cl.batch, c ? c.name : '', cl.start, cl.cat, cl.patLabel].join(' '),
      });
    });
    // 6) Giảng viên
    (A.instructors || []).forEach(i => items.push({
      kind: 'instructor', label: i.name, path: '/instructors', icon: 'user',
      sub: [i.title, i.expertise].filter(Boolean).join(' · '),
      text: [i.name, i.title, i.expertise].join(' '),
    }));
  }
  // precompute normalized search strings
  items.forEach(it => { it._l = stripDia(it.label); it._s = stripDia(it.label + ' ' + (it.sub || '') + ' ' + (it.text || '')); });
  return items;
}
let _searchIndex = null;
function getSearchIndex() { if (!_searchIndex) _searchIndex = buildSearchIndex(); return _searchIndex; }

function scoreItem(it, qs) {
  const l = it._l, s = it._s;
  if (l.startsWith(qs)) return 100;
  if ((' ' + l).includes(' ' + qs)) return 86;   // matches a word start in the label
  if (l.includes(qs)) return 72;
  if (s.includes(qs)) return 46;
  // subsequence match over the label (typo / abbrev tolerant)
  let i = 0; for (const ch of l) { if (ch === qs[i]) i++; if (i === qs.length) break; }
  if (i === qs.length) return 18;
  return -1;
}

function CommandSearch() {
  const [q, setQ] = useState('');
  const [open, setOpen] = useState(false);
  const [active, setActive] = useState(0);
  const inputRef = useRef(null);
  const listRef = useRef(null);

  const results = useMemo(() => {
    const idx = getSearchIndex();
    const query = q.trim();
    if (!query) {
      // Quick access — just the top screens, but the box searches far more.
      return idx.filter(i => i.kind === 'feature').slice(0, 7);
    }
    const qs = stripDia(query);
    const scored = idx
      .map(it => ({ it, sc: scoreItem(it, qs) }))
      .filter(x => x.sc >= 0)
      .sort((a, b) => b.sc - a.sc);
    // Bucket by kind, cap each, then stack groups in defined order.
    const byKind = {};
    scored.forEach(({ it, sc }) => { (byKind[it.kind] = byKind[it.kind] || []).push({ ...it, score: sc }); });
    const out = [];
    Object.keys(SEARCH_KINDS)
      .sort((a, b) => SEARCH_KINDS[a].order - SEARCH_KINDS[b].order)
      .forEach(k => { if (byKind[k]) out.push(...byKind[k].slice(0, SEARCH_KINDS[k].cap)); });
    return out.slice(0, 18);
  }, [q]);

  useEffect(() => { setActive(0); }, [q]);

  // ⌘K / Ctrl-K to focus
  useEffect(() => {
    const on = e => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault(); inputRef.current && inputRef.current.focus(); setOpen(true);
      }
    };
    window.addEventListener('keydown', on);
    return () => window.removeEventListener('keydown', on);
  }, []);

  const go = (item) => {
    if (!item) return;
    if (item.intent) window.NavIntent = item.intent;   // deep-link payload (e.g. which conversation)
    navigate(item.path);
    setOpen(false); setQ('');
    inputRef.current && inputRef.current.blur();
  };

  const onKey = e => {
    if (e.key === 'ArrowDown') { e.preventDefault(); setActive(a => Math.min(a + 1, results.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(a => Math.max(a - 1, 0)); }
    else if (e.key === 'Enter') { e.preventDefault(); go(results[active]); }
    else if (e.key === 'Escape') { setOpen(false); inputRef.current && inputRef.current.blur(); }
  };

  // keep active item scrolled into view inside the list (skip group headers)
  useEffect(() => {
    if (!open || !listRef.current) return;
    const el = listRef.current.querySelectorAll('.cmd-item')[active];
    if (el) { const lr = listRef.current, t = el.offsetTop, b = t + el.offsetHeight;
      if (t < lr.scrollTop) lr.scrollTop = t;
      else if (b > lr.scrollTop + lr.clientHeight) lr.scrollTop = b - lr.clientHeight; }
  }, [active, open]);

  return (
    <div className={'search cmd' + (open ? ' open' : '')}>
      <div className="search-box" onClick={() => inputRef.current && inputRef.current.focus()}>
        <Icon name="search" size={16} />
        <input
          ref={inputRef}
          placeholder="Tìm màn hình, khách hàng, hội thoại, khóa học…"
          value={q}
          onChange={e => { setQ(e.target.value); setOpen(true); }}
          onFocus={() => setOpen(true)}
          onBlur={() => setTimeout(() => setOpen(false), 120)}
          onKeyDown={onKey}
        />
        <span className="t11" style={{ opacity: 0.5 }}>⌘K</span>
      </div>
      {open && (
        <div className="cmd-panel">
          <div className="cmd-cap">{q.trim() ? (results.length + ' kết quả') : 'Truy cập nhanh'}</div>
          {results.length === 0 ? (
            <div className="cmd-empty">
              <div style={{ fontWeight: 600, color: 'var(--p-text)' }}>Không có kết quả cho “{q.trim()}”.</div>
              <div style={{ marginTop: 5 }}>Thử tên khách, số điện thoại, kênh chat, tên khóa học hoặc giảng viên.</div>
            </div>
          ) : (
            <div className="cmd-list" ref={listRef}>
              {results.map((r, i) => {
                const showHead = i === 0 || results[i - 1].kind !== r.kind;
                return (
                  <React.Fragment key={r.kind + ':' + r.path + ':' + (r.intent ? r.intent.inboxConv : '') + ':' + i}>
                    {showHead && <div className="cmd-sec">{SEARCH_KINDS[r.kind].label}</div>}
                    <a
                      href={'#' + r.path}
                      className={'cmd-item' + (i === active ? ' active' : '') + ' k-' + r.kind}
                      onMouseEnter={() => setActive(i)}
                      onMouseDown={e => { e.preventDefault(); go(r); }}
                    >
                      <span className="cmd-ic"><Icon name={r.icon} size={16} /></span>
                      <span className="cmd-txt">
                        <span className="cmd-label">
                          {r.label}
                          {r.meta ? <span className="cmd-meta">{r.meta}</span> : null}
                          {r.badge ? <span className="cmd-badge">{r.badge}</span> : null}
                        </span>
                        {r.sub ? <span className="cmd-group">{r.sub}</span> : null}
                      </span>
                      <span className="cmd-enter">{i === active ? SEARCH_KINDS[r.kind].hint : <Icon name="chevronRight" size={14} />}</span>
                    </a>
                  </React.Fragment>
                );
              })}
            </div>
          )}
          <div className="cmd-foot">
            <span><kbd>↑</kbd><kbd>↓</kbd> di chuyển</span>
            <span><kbd>↵</kbd> mở</span>
            <span><kbd>esc</kbd> đóng</span>
          </div>
        </div>
      )}
    </div>
  );
}

function useHashRoute() {
  const [path, setPath] = useState(() => (location.hash || '#/exec').slice(1));
  useEffect(() => {
    const on = () => setPath((location.hash || '#/exec').slice(1));
    window.addEventListener('hashchange', on);
    return () => window.removeEventListener('hashchange', on);
  }, []);
  return path;
}
function navigate(p) { location.hash = '#' + p; const m = document.querySelector('.main'); if (m) m.scrollTop = 0; }
window.navigate = navigate;

function matchRoute(routes, path) {
  for (const r of routes) {
    if (r.path === path) return { comp: r.comp, params: {}, route: r };
    if (r.path.includes(':')) {
      const rp = r.path.split('/'), pp = path.split('/');
      if (rp.length !== pp.length) continue;
      const params = {}; let ok = true;
      for (let i = 0; i < rp.length; i++) { if (rp[i].startsWith(':')) params[rp[i].slice(1)] = decodeURIComponent(pp[i]); else if (rp[i] !== pp[i]) { ok = false; break; } }
      if (ok) return { comp: r.comp, params, route: r };
    }
  }
  return null;
}

// Tổng tin chưa đọc thật từ AAU.conversations (cho badge sidebar Hội thoại).
function inboxUnread() {
  try { return Object.values(AAU.conversations || {}).reduce((s, c) => s + (Number(c.unread) || 0), 0); }
  catch (e) { return 0; }
}

function NavItem({ item, path }) {
  const hasKids = item.children && item.children.length;
  const childActive = hasKids && item.children.some(c => c.path === path);
  const [open, setOpen] = useState(childActive);
  useEffect(() => { if (childActive) setOpen(true); }, [childActive]);
  const active = item.path === path;
  const badge = item.dynamicBadge === 'inbox' ? inboxUnread() : item.badge;
  if (!hasKids) {
    return (
      <div className={'nav-item' + (active ? ' active' : '')} onClick={() => navigate(item.path)}>
        <span className="ni-ic"><Icon name={item.icon} size={17} /></span>
        <span>{item.label}</span>
        {badge ? <span className="ni-badge">{badge}</span> : null}
      </div>
    );
  }
  return (
    <div>
      <div className={'nav-item' + (open ? ' open' : '') + (childActive ? ' active' : '')} onClick={() => setOpen(!open)}>
        <span className="ni-ic"><Icon name={item.icon} size={17} /></span>
        <span>{item.label}</span>
        <span className="chev"><Icon name="chevronRight" size={14} /></span>
      </div>
      {open && <div className="nav-children">{item.children.map(c => (
        <div key={c.path} className={'nav-child' + (c.path === path ? ' active' : '')} onClick={() => navigate(c.path)}>
          <span>{c.label}</span>{c.badge && <span className="ni-badge">{c.badge}</span>}
        </div>
      ))}</div>}
    </div>
  );
}

function NavGroup({ group, path }) {
  const groupHasActive = group.items.some(it =>
    it.path === path || (it.children && it.children.some(c => c.path === path))
  );
  const key = 'navgroup:' + group.label;
  const [open, setOpen] = useState(() => {
    const saved = localStorage.getItem(key);
    if (saved !== null) return saved === '1';
    return true;
  });
  // Always reveal a group that contains the active route
  useEffect(() => { if (groupHasActive && !open) setOpen(true); }, [groupHasActive]);
  const toggle = () => { const n = !open; setOpen(n); localStorage.setItem(key, n ? '1' : '0'); };
  return (
    <div className={'nav-group' + (open ? '' : ' collapsed')}>
      <button className="gl" onClick={toggle} aria-expanded={open}>
        <span>{group.label}</span>
        <span className="gl-chev"><Icon name="chevronRight" size={13} /></span>
      </button>
      {open && <div className="nav-group-items">
        {group.items.map(it => <NavItem key={it.label} item={it} path={path} />)}
      </div>}
    </div>
  );
}

function AppShell({ routes }) {
  const path = useHashRoute();
  const matched = matchRoute(routes, path) || { comp: () => <NotFound path={path} />, params: {} };
  const Comp = matched.comp;
  const [tenant, setTenant] = useState(AAU.tenants[0]);
  const viewer = useViewer();
  // Re-render sidebar khi dữ liệu hội thoại đổi (hydrate / wipe / đọc tin) → badge real-time.
  const [, bump] = React.useReducer(x => x + 1, 0);
  useEffect(() => {
    const on = () => bump();
    window.addEventListener('aau:conversations-changed', on);
    return () => window.removeEventListener('aau:conversations-changed', on);
  }, []);
  return (
    <div className="app">
      <div className="topbar">
        <div className="brand" onClick={() => navigate('/exec')} style={{ cursor: 'pointer' }}>
          <div className="mark">A</div>
          <div><div className="bt">AAU CRM Center</div><div className="bs">Học viện F&B</div></div>
        </div>
        <CommandSearch />
        <div className="spacer" />
        <div className="viewas" title="Xem hệ thống với vai trò khác (RBAC preview)">
          <Icon name="eye" size={14} />
          <span className="t11 viewas-lbl">Xem với</span>
          <select className="viewas-sel" value={viewer.id} onChange={e => AccessStore.set(e.target.value)}>
            {AAU.users.filter(u => u.salesRole && u.salesRole !== 'admin').map(u => <option key={u.id} value={u.id}>{u.name} · {u.title}</option>)}
          </select>
        </div>
        <div className="tenant">
          <Icon name="building" size={15} /><span>{tenant.short}</span><Icon name="chevronDown" size={14} />
        </div>
        <div className="tb-actions">
          <button className="tb-btn" title="Tự động hóa"><Icon name="bolt" size={18} /></button>
          <button className="tb-btn" title="Thông báo"><Icon name="bell" size={18} /><span className="ndot" /></button>
        </div>
        <div className="user" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <Avatar name={viewer.name} color={viewer.color} size={30} />
          <button className="tb-btn" title="Đăng xuất" onClick={() => authLogout()} style={{ width: 'auto', padding: '4px 9px', fontSize: 12, fontWeight: 600 }}>Thoát</button>
        </div>
      </div>
      <nav className="nav">
        {NAV.map(g => <NavGroup key={g.label} group={g} path={path} />)}
      </nav>
      <main className={'main' + (matched.route && matched.route.flush ? ' flush' : '')}>
        <Comp {...matched.params} />
      </main>
    </div>
  );
}

function NotFound({ path }) {
  return <div className="page"><EmptyState icon="search" title="Không tìm thấy màn hình" desc={'Route ' + path + ' chưa được đăng ký.'} action={<Button variant="primary" onClick={() => navigate('/exec')}>Về Dashboard</Button>} /></div>;
}

// ============================================================
//  Authentication gate — a real login screen in front of the app.
//  Enforced only when served from a real host (the VPS); local dev
//  (localhost / 127.0.0.1 / file://) opens straight through so the
//  build workflow stays frictionless. ?api / token still work as before.
// ============================================================
const LOGIN_INP = { width: '100%', boxSizing: 'border-box', padding: '9px 11px', borderRadius: 8, border: '1px solid #d1d5db', fontSize: 14, outline: 'none' };

function applyAuthUser(u) {
  if (!u || !u.id) return;
  try { AAU.currentUser = u; } catch (e) { }
  try { localStorage.setItem('aau_user', JSON.stringify(u)); } catch (e) { }
  try { if (AAU.users.find(x => x.id === u.id)) AccessStore.set(u.id); } catch (e) { }
}

function authLogout() {
  try { window.API && window.API.logout && window.API.logout(); } catch (e) { }
  try { localStorage.removeItem('aau_token'); localStorage.removeItem('aau_user'); } catch (e) { }
  location.reload();
}

function LoginScreen({ onSuccess }) {
  const [email, setEmail] = useState('');
  const [pw, setPw] = useState('');
  const [err, setErr] = useState('');
  const [busy, setBusy] = useState(false);
  async function submit(e) {
    if (e) e.preventDefault();
    if (!email.trim() || !pw) { setErr('Nhập email và mật khẩu.'); return; }
    setBusy(true); setErr('');
    try {
      const res = await window.API.login(email.trim().toLowerCase(), pw);
      applyAuthUser(res.user);
      onSuccess(res.user);
    } catch (ex) {
      setErr('Email hoặc mật khẩu không đúng.');
      setBusy(false);
    }
  }
  return (
    <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f4f6f8' }}>
      <form onSubmit={submit} style={{ width: 360, background: '#fff', borderRadius: 14, padding: '30px 28px', boxShadow: '0 8px 30px rgba(0,0,0,.10)', border: '1px solid #e3e6ea' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 18 }}>
          <div style={{ width: 38, height: 38, borderRadius: 9, background: '#0a7d4f', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 800, fontSize: 20 }}>A</div>
          <div><div style={{ fontWeight: 800, fontSize: 16 }}>AAU CRM Center</div><div style={{ fontSize: 12, color: '#6b7280' }}>Học viện F&B</div></div>
        </div>
        <div style={{ fontSize: 13, color: '#374151', marginBottom: 14 }}>Đăng nhập để tiếp tục</div>
        <label style={{ display: 'block', fontSize: 12, fontWeight: 600, color: '#374151', marginBottom: 4 }}>Email</label>
        <input value={email} onChange={e => setEmail(e.target.value)} autoFocus type="email" placeholder="ten@aau.vn" style={LOGIN_INP} />
        <label style={{ display: 'block', fontSize: 12, fontWeight: 600, color: '#374151', margin: '12px 0 4px' }}>Mật khẩu</label>
        <input value={pw} onChange={e => setPw(e.target.value)} type="password" placeholder="••••••••" style={LOGIN_INP} />
        {err && <div style={{ color: '#b91c1c', fontSize: 12.5, marginTop: 10 }}>{err}</div>}
        <button type="submit" disabled={busy} style={{ width: '100%', marginTop: 18, padding: '10px', borderRadius: 8, border: 'none', background: '#0a7d4f', color: '#fff', fontWeight: 700, fontSize: 14, opacity: busy ? .6 : 1, cursor: busy ? 'default' : 'pointer' }}>{busy ? 'Đang đăng nhập…' : 'Đăng nhập'}</button>
      </form>
    </div>
  );
}

// ---- account activation (invited user sets their own password) ----
function hashRoute() { return (location.hash || '').replace(/^#/, '').split('?')[0]; }
function hashQuery(name) {
  const h = location.hash || ''; const i = h.indexOf('?');
  if (i < 0) return '';
  try { return new URLSearchParams(h.slice(i + 1)).get(name) || ''; } catch (e) { return ''; }
}

function ActivationScreen() {
  const token = hashQuery('token');
  const [info, setInfo] = useState(null);
  const [pw, setPw] = useState(''); const [pw2, setPw2] = useState('');
  const [err, setErr] = useState(''); const [busy, setBusy] = useState(false); const [done, setDone] = useState(false);
  useEffect(() => {
    let alive = true;
    (async () => {
      try { await window.AAU_BOOT; } catch (e) { }
      if (!token) { if (alive) setErr('Link không hợp lệ (thiếu mã kích hoạt).'); return; }
      try { const r = await window.API.activateInfo(token); if (alive) setInfo(r); }
      catch (ex) { if (alive) setErr(ex.message || 'Link không hợp lệ hoặc đã hết hạn.'); }
    })();
    return () => { alive = false; };
  }, []);
  async function submit(e) {
    if (e) e.preventDefault();
    if (pw.length < 6) { setErr('Mật khẩu tối thiểu 6 ký tự.'); return; }
    if (pw !== pw2) { setErr('Mật khẩu nhập lại không khớp.'); return; }
    setBusy(true); setErr('');
    try {
      await window.API.activate(token, pw);
      setDone(true);
      setTimeout(() => { location.hash = '#/exec'; location.reload(); }, 1200);
    } catch (ex) { setErr(ex.message || 'Kích hoạt thất bại.'); setBusy(false); }
  }
  const card = { width: 380, background: '#fff', borderRadius: 14, padding: '30px 28px', boxShadow: '0 8px 30px rgba(0,0,0,.10)', border: '1px solid #e3e6ea' };
  const wrap = { minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f4f6f8' };
  const head = (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 18 }}>
      <div style={{ width: 38, height: 38, borderRadius: 9, background: '#0a7d4f', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 800, fontSize: 20 }}>A</div>
      <div><div style={{ fontWeight: 800, fontSize: 16 }}>AAU CRM Center</div><div style={{ fontSize: 12, color: '#6b7280' }}>Kích hoạt tài khoản</div></div>
    </div>
  );
  if (done) return <div style={wrap}><div style={card}>{head}<div style={{ color: '#0a7d4f', fontSize: 14, fontWeight: 600 }}>✓ Kích hoạt thành công! Đang đưa bạn vào hệ thống…</div></div></div>;
  if (err && !info) return <div style={wrap}><div style={card}>{head}<div style={{ color: '#b91c1c', fontSize: 13.5 }}>{err}</div><div style={{ fontSize: 12.5, color: '#6b7280', marginTop: 12 }}>Hãy đề nghị quản trị viên gửi lại link mời mới.</div></div></div>;
  return (
    <div style={wrap}>
      <form onSubmit={submit} style={card}>
        {head}
        {info && <div style={{ fontSize: 13, color: '#374151', marginBottom: 14 }}>Xin chào <b>{info.name || info.email}</b>, đặt mật khẩu để kích hoạt tài khoản <b>{info.email}</b>.</div>}
        <label style={{ display: 'block', fontSize: 12, fontWeight: 600, color: '#374151', marginBottom: 4 }}>Mật khẩu mới</label>
        <input value={pw} onChange={e => setPw(e.target.value)} autoFocus type="password" placeholder="Tối thiểu 6 ký tự" style={LOGIN_INP} />
        <label style={{ display: 'block', fontSize: 12, fontWeight: 600, color: '#374151', margin: '12px 0 4px' }}>Nhập lại mật khẩu</label>
        <input value={pw2} onChange={e => setPw2(e.target.value)} type="password" placeholder="••••••••" style={LOGIN_INP} />
        {err && <div style={{ color: '#b91c1c', fontSize: 12.5, marginTop: 10 }}>{err}</div>}
        <button type="submit" disabled={busy || !info} style={{ width: '100%', marginTop: 18, padding: '10px', borderRadius: 8, border: 'none', background: '#0a7d4f', color: '#fff', fontWeight: 700, fontSize: 14, opacity: (busy || !info) ? .6 : 1, cursor: (busy || !info) ? 'default' : 'pointer' }}>{busy ? 'Đang kích hoạt…' : 'Đặt mật khẩu & vào hệ thống'}</button>
      </form>
    </div>
  );
}

function AuthGate({ children }) {
  const isLocalDev = location.hostname === 'localhost' || location.hostname === '127.0.0.1' || location.protocol === 'file:';
  const [route, setRoute] = useState(hashRoute());
  useEffect(() => {
    const on = () => setRoute(hashRoute());
    window.addEventListener('hashchange', on);
    return () => window.removeEventListener('hashchange', on);
  }, []);
  const [state, setState] = useState(isLocalDev ? 'authed' : 'checking');
  useEffect(() => {
    if (isLocalDev) return;
    if (hashRoute() === '/activate') return; // activation page is public
    let alive = true;
    (async () => {
      try { await window.AAU_BOOT; } catch (e) { }
      if (!localStorage.getItem('aau_token')) { if (alive) setState('login'); return; }
      try {
        const me = await window.API.get('/auth/me');
        applyAuthUser(me);
        if (alive) setState('authed');
      } catch (e) {
        try { localStorage.removeItem('aau_token'); localStorage.removeItem('aau_user'); } catch (_) { }
        if (alive) setState('login');
      }
    })();
    return () => { alive = false; };
  }, []);
  if (route === '/activate') return <ActivationScreen />; // public, even on the VPS
  if (state === 'checking') return <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#6b7280', fontSize: 14 }}>Đang tải…</div>;
  if (state === 'login') return <LoginScreen onSuccess={() => setState('authed')} />;
  return children;
}

Object.assign(window, { AppShell, NAV, AuthGate, authLogout });
