// OP Central — Shell: sidebar, topbar, role switcher, notifications drawer

// The one definition of what is in the navigation.
//
// The sidebar renders it and the keyboard command palette searches it. Two
// lists would drift, and a palette that offered a screen the sidebar hides
// would be a way around the role's permissions rather than a convenience.
function opcNavGroups(state, role) {
  const allowed = perm(role).nav;
  const pendingTransfers = state.transfer_requests?.filter(t => t.status === 'Pending').length || 0;
  const overdueCount = (state.sales_orders || []).filter(s => (s.days_overdue || 0) > 0).length;
  const pendingMatches = (state.vendor_invoices || []).filter(v => v.status === 'Pending 3-Way Match').length;
  const myTasks = window.tasksForRole ? window.tasksForRole(state, role, () => {}, () => {}, () => {}).length : 0;

  const navGroups = [
    { label: 'Overview', items: [
      { id: 'dashboard', label: 'Dashboard', icon: 'home' },
      { id: 'inbox', label: 'My Tasks', icon: 'bell', badge: myTasks || null },
    ]},
    { label: 'Sales', items: [
      { id: 'client-requests', label: 'Item Requests', icon: 'file' },
      { id: 'sourcing', label: 'Sourcing / Inquiries', icon: 'bookmark' },
      { id: 'sales-orders', label: 'Sales Orders', icon: 'receipt' },
      { id: 'customers', label: 'Customers', icon: 'user' },
    ]},
    { label: 'Inventory', items: [
      { id: 'godown', label: 'Virtual Godowns', icon: 'box' },
      { id: 'scm', label: 'SCM Tracking', icon: 'repeat' },
      { id: 'pool', label: 'Master Surplus Pool', icon: 'layers' },
      { id: 'transfers', label: 'Cross-SO Transfers', icon: 'arrowLeftRight', badge: pendingTransfers || null },
    ]},
    { label: 'Procurement', items: [
      { id: 'rfq', label: 'RFQ Comparison', icon: 'grid' },
      { id: 'vendor-pos', label: 'Vendor POs', icon: 'cart' },
      { id: 'grn', label: 'GRN', icon: 'package' },
      { id: 'three-way', label: '3-Way Match', icon: 'check', badge: pendingMatches || null },
      { id: 'vendors', label: 'Vendors', icon: 'factory' },
      { id: 'mapping', label: 'Item Name Mapping', icon: 'arrowLeftRight' },
    ]},
    { label: 'Billing', items: [
      { id: 'invoices', label: 'Invoices & e-Way Bills', icon: 'file' },
      { id: 'collections', label: 'Collections', icon: 'cash', badge: overdueCount || null },
    ]},
    { label: 'Catalogue', items: [
      { id: 'products', label: 'Products & BOM', icon: 'book' },
    ]},
    { label: 'Admin', items: [
      { id: 'settings', label: 'Customisation', icon: 'settings' },
      { id: 'audit', label: 'Audit Log', icon: 'history' },
      { id: 'onboarding', label: 'Onboarding Wizard', icon: 'sparkles' },
    ]},
  ];

  // Filter groups & items by the role's allowed nav list, then drop anything a
  // capability that this ORGANIZATION has switched off controls (a feature row
  // with enabled=false; an absent key inherits, so nothing disappears by default).
  const filteredGroups = navGroups
    .map(g => ({ ...g, items: g.items.filter(it =>
      (allowed.includes(it.id) || (window.SCM_ROUTES_SET && window.SCM_ROUTES_SET[it.id] &&
        window.canAccess && window.canAccess(role, it.id)))
      && !(window.featureBlocks && window.featureBlocks(it.id))) }))
    .filter(g => g.items.length > 0);

  // Platform console — visible only to platform (master) admins, never to tenants.
  if (window.__opcIsMaster) {
    filteredGroups.push({ label: 'Platform', items: [
      { id: 'platform', label: 'Organizations', icon: 'layers' },
    ] });
  }
  return filteredGroups;
}
window.opcNavGroups = opcNavGroups;

function Topbar({ onOpenTweaks }) {
  const { state, currentUser, realUserId, impersonate, stopImpersonating, getUser, navigate, route, logout } = useStore();
  const [notifOpen, setNotifOpen] = React.useState(false);
  const u = getUser(currentUser);                 // persona being acted as
  const realUser = getUser(realUserId);           // who is actually logged in
  const isAdmin = realUser && realUser.role === 'Org Admin';
  const impersonating = currentUser !== realUserId;
  // Filter notifications to the current role OR a specific assigned user.
  const myNotifs = state.notifications.filter(n => (!n.role && !n.user_id) || (n.role && n.role === (u && u.role)) || (n.user_id && n.user_id === currentUser));
  const unread = myNotifs.filter(n => !n.read).length;

  // Admin impersonation for flow testing — change persona AND jump to that
  // role's home. Does NOT change the real identity, so you can always exit back.
  const switchTo = (uid) => {
    const nu = state.users.find(x => x.id === uid);
    if (!nu) return;
    impersonate(uid);
    const dest = (perm(nu.role).primary || { route: 'dashboard' }).route;
    navigate(dest);
  };
  const exitImpersonation = () => {
    stopImpersonating();
    const dest = (perm(realUser.role).primary || { route: 'dashboard' }).route;
    navigate(dest);
  };
  // expose for sidebar select
  window.__opc_setUser = impersonate;
  window.__opc_switchTo = switchTo;

  const unreadOld = state.notifications.filter(n => !n.read).length;

  // Crumb logic
  let crumb = [{ label: 'Workspace' }];
  const labels = {
    'dashboard': 'Dashboard', 'inbox': 'My Approvals',
    'client-requests': 'Item Requests',
    'sales-orders': 'Sales Orders', 'customers': 'Customers',
    'godown': 'Virtual Godowns', 'pool': 'Master Surplus Pool', 'transfers': 'Cross-SO Transfers',
    'rfq': 'RFQ Comparison', 'vendor-pos': 'Vendor POs', 'grn': 'GRN', 'three-way': '3-Way Match', 'vendors': 'Vendors',
    'invoices': 'Invoices & e-Way Bills', 'collections': 'Collections',
    'products': 'Products & BOM',
    'settings': 'Customisation', 'audit': 'Audit Log', 'onboarding': 'Onboarding Wizard',
  };
  const parts = route.split('/');
  if (labels[parts[0]]) crumb.push({ label: labels[parts[0]] });
  if (parts.length > 1) crumb.push({ label: parts.slice(1).join('/') });

  return (
    <>
      <div className="brand" style={{ cursor: 'pointer' }} onClick={() => navigate('flow')}
           title="Back to the flow">
        <div className="brand-mark">B</div>
        <div>
          <div className="brand-name">OP Central</div>
        </div>
      </div>
      <div className="topbar">
        <div className="topbar-left">
          <div className="crumbs">
            {crumb.map((c, i) => (
              <span key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                {i > 0 && <span className="sep">›</span>}
                {i === 0
                  ? <span style={{ cursor: 'pointer' }} onClick={() => navigate('flow')}>{c.label}</span>
                  : <span className={i === crumb.length - 1 ? 'current' : ''}>{c.label}</span>}
              </span>
            ))}
          </div>
        </div>
        <div className="topbar-right">
          <div className="org-pill" title={state.org.gstin}>
            <div className="org-pill-dot">{state.org.logo_letter}</div>
            <span>{state.org.short}</span>
            <span className="mono tiny muted">· FY{String((state.org && state.org.fiscal_year) || '').replace('20','') || '—'}</span>
          </div>
          {isAdmin && state.users.length > 1 && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              {impersonating && <span className="badge warning" title="You are acting as another user">Acting as {u.role}</span>}
              <div className="role-switcher" title="Act as another user (admin) — test the role flow">
                {state.users.map(usr => (
                  <button key={usr.id} className={currentUser === usr.id ? 'active' : ''}
                    title={`${usr.name} — ${usr.role}`}
                    onClick={() => switchTo(usr.id)}>{usr.initials || usr.name.slice(0, 2)}</button>
                ))}
              </div>
              {impersonating && (
                <button className="btn btn-ghost btn-sm" title="Return to your admin account" onClick={exitImpersonation}>
                  <Icon name="x" size={12}/>Exit
                </button>
              )}
            </div>
          )}
          <button className="btn btn-ghost btn-sm" onClick={() => setNotifOpen(true)} style={{ position: 'relative' }}>
            <Icon name="bell" size={14}/>
            {unread > 0 && <span style={{
              position: 'absolute', top: 1, right: 1,
              width: 14, height: 14, borderRadius: '50%',
              background: 'var(--danger)', color: 'white',
              fontSize: 9, fontWeight: 700, display: 'grid', placeItems: 'center',
              fontFamily: 'var(--mono)',
            }}>{unread}</span>}
          </button>
          <button className="btn btn-ghost btn-sm" onClick={onOpenTweaks} title="Tweaks">
            <Icon name="sparkles" size={14}/>
          </button>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '0 2px' }} title={u.role}>
            <Avatar user={u} size={22}/>
            <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.1 }}>
              <span style={{ fontSize: 11.5, fontWeight: 500 }}>{u.name}</span>
              <span className="muted" style={{ fontSize: 10.5 }}>{u.role}</span>
            </div>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={logout} title="Sign out">
            <Icon name="arrowRight" size={14}/>
          </button>
        </div>
      </div>
      {notifOpen && <NotificationsDrawer onClose={() => setNotifOpen(false)} role={u && u.role} />}
    </>
  );
}

// Where clicking a notification should land. A notification's role/user_id
// TARGET and the route its KIND points at have always been two separate
// lists — nothing guaranteed the recipient's own nav also carries that
// route (e.g. a 'grn'-kind notification targets Billing, which has never
// carried 'grn' in its own nav). Dashboard is the one route every role
// keeps, so a kind the viewer cannot actually open lands there instead of
// bouncing silently off the app-level route guard.
function notificationRoute(n, role) {
  const dest = n.kind === 'transfer' ? 'transfers'
    : n.kind === 'grn' ? 'grn'
    : n.kind === 'overdue' ? 'collections'
    : n.kind === 'match' ? 'three-way'
    : n.kind === 'sourcing' ? 'sourcing'
    : n.kind === 'client-request' ? 'client-requests'
    : 'inbox';
  return canAccess(role, dest) ? dest : 'dashboard';
}
window.notificationRoute = notificationRoute;

function NotificationsDrawer({ onClose, role }) {
  const { state, mutate, navigate, currentUser } = useStore();
  const items = state.notifications.filter(n => (!n.role && !n.user_id) || (n.role && n.role === role) || (n.user_id && n.user_id === currentUser));
  const ref = React.useRef(null);
  const openerRef = React.useRef(null);
  const idRef = React.useRef('drawer-' + Math.random().toString(36).slice(2));
  const onCloseRef = React.useRef(onClose);
  onCloseRef.current = onClose;

  // The same four things every dialog in this app does. This one is not a
  // Modal, so it has to say so itself: Escape closes it and only it, focus
  // moves in when it opens and back to the bell when it closes.
  React.useEffect(() => {
    openerRef.current = document.activeElement;
    const pop = window.opcOverlayPush ? window.opcOverlayPush(idRef.current) : null;
    const onEsc = (e) => {
      if (e.key !== 'Escape') return;
      if (window.opcOverlayTop && window.opcOverlayTop() !== idRef.current) return;
      onCloseRef.current && onCloseRef.current();
    };
    window.addEventListener('keydown', onEsc);
    const t = setTimeout(() => {
      const box = ref.current;
      if (!box || box.contains(document.activeElement)) return;
      const first = box.querySelector('button, [data-kbd-click], [tabindex]:not([tabindex="-1"])');
      if (first && first.focus) { try { first.focus(); } catch (e) {} }
    }, 0);
    return () => {
      clearTimeout(t);
      window.removeEventListener('keydown', onEsc);
      if (pop) pop(); else if (window.opcOverlayPop) window.opcOverlayPop(idRef.current);
      const back = openerRef.current;
      if (back && typeof back.focus === 'function' && document.contains(back)) {
        try { back.focus(); } catch (e) {}
      }
    };
  }, []);

  // Tab stays inside it, the same way it stays inside a dialog.
  const onKeyDown = (e) => {
    if (e.key !== 'Tab' || !ref.current) return;
    const focusable = Array.from(ref.current.querySelectorAll(
      'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]),'
      + ' textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'))
      .filter(el => el === document.activeElement
                 || (window.kbdVisible ? window.kbdVisible(el) : el.offsetParent !== null));
    if (!focusable.length) return;
    const first = focusable[0], last = focusable[focusable.length - 1];
    if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
    else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
  };

  return (
    <>
      <div style={{ position: 'fixed', inset: 0, zIndex: 80 }} onClick={onClose}/>
      <div className="drawer" ref={ref} role="dialog" aria-modal="true"
           aria-label="Notifications" onKeyDown={onKeyDown}>
        <div className="drawer-header">
          <div>
            <strong style={{ fontSize: 13 }}>Notifications</strong>
            <div className="muted tiny">For {role} · {items.length} total · {items.filter(n=>!n.read).length} unread</div>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="x" size={14}/></button>
        </div>
        <div className="drawer-body">
          {items.length === 0 && <div className="empty"><div className="empty-title">All clear</div>No notifications for your role.</div>}
          {items.map(n => (
            <div key={n.id} className="queue-item" style={{ background: n.read ? 'transparent' : 'var(--accent-bg)' }}
                 onClick={() => {
                   mutate(s => ({ ...s, notifications: s.notifications.map(x => x.id === n.id ? {...x, read: true} : x) }));
                   navigate(notificationRoute(n, role));
                   onClose();
                 }}>
              <div style={{ width: 6, height: 6, borderRadius: '50%', background: n.read ? 'var(--border-strong)' : 'var(--accent)' }}/>
              <div className="grow">
                <div style={{ fontSize: 12.5, marginBottom: 2 }}>{n.text}</div>
                <div className="muted tiny">{n.kind} · {fmtDate(n.date)}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

window.Topbar = Topbar;
