// Desktop app shell — left rail nav, top bar, main column
// Used by all desktop screens

// Narrow-viewport hook: phones (<= 768px) get a single column, a compact top
// bar and a bottom tab bar instead of the left rail. (Beta, 2026-09-28.)
const useNarrow = () => {
  const get = () => (typeof window !== 'undefined' ? window.innerWidth <= 768 : false);
  const [narrow, setNarrow] = React.useState(get);
  React.useEffect(() => {
    const h = () => setNarrow(get());
    window.addEventListener('resize', h);
    return () => window.removeEventListener('resize', h);
  }, []);
  return narrow;
};

const DesktopShell = ({ theme, active, onNav, children, rightRail, noPad }) => {
  const t = theme;
  const { isAuthed, signIn, me, search, setSearch, bellOpen, setBellOpen, profile } = useApp();
  const searchRef = React.useRef(null);
  const narrow = useNarrow();

  // Role-gated nav entries.
  //  • isClinician (clinician/admin) → the Clinician dashboard
  //  • isProfessional (clinician/researcher/admin) → their public profile
  const acctType = profile?.account_type;
  const isClinician = acctType === 'clinician' || acctType === 'admin';
  const isProfessional = isClinician || acctType === 'researcher';

  const navItems = [
    // OH Community beta nav (2026-09-28) — see specs/community-beta.md §3.
    // level1 / journal / professional still resolve by direct URL.
    { id: 'home', label: 'Home', icon: 'home' },
    { id: 'challenge', label: "This week's challenge", icon: 'flame' },
    { id: 'experts', label: 'Experts', icon: 'sparkle' },
    { id: 'circles', label: 'Circles', icon: 'users' },
    { id: 'assessment', label: 'My smell', icon: 'nose' },
    { id: 'profile', label: 'Profile', icon: 'profile' },
    ...(isClinician ? [{ id: 'clinician', label: 'Clinician', icon: 'shield' }] : []),
    // MVP v1 parking (2026-06-04): items below are HIDDEN from nav but their
    // routes still resolve by direct URL. See customer_flows/MVP_v1_scope.md.
    //   • { id: 'training',  label: 'Workshops', icon: 'sparkle' }
    //   • { id: 'subscribe', label: 'Membership', icon: 'plus' }
    //   • { id: 'market',    label: 'Market',     icon: 'shop' }
    //   • { id: 'cards',     label: 'Smell Cards' } — parked earlier
  ];

  // ⌘K to focus search
  React.useEffect(() => {
    const handler = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault();
        searchRef.current?.focus();
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, []);

  const onSearchSubmit = (e) => {
    e.preventDefault();
    SB.logEvent('search_submit', { query: search });
    if (active !== 'home') onNav('home');
  };

  const onAvatarClick = () => {
    if (!isAuthed) return signIn();
    onNav('profile');
  };

  return (
    <div style={{
      width: '100%', height: '100%',
      background: t.c.bg,
      color: t.c.ink,
      fontFamily: t.font.sans,
      display: 'grid',
      gridTemplateColumns: narrow ? '1fr' : '240px 1fr',
      gridTemplateRows: narrow ? '56px 1fr auto' : '56px 1fr',
      overflow: 'hidden',
      position: 'relative',
    }}>
      {/* Top bar — full width */}
      <div style={{
        gridColumn: narrow ? '1' : '1 / 3',
        borderBottom: `1px solid ${t.c.edge}`,
        background: t.c.bg3,
        display: 'grid',
        gridTemplateColumns: narrow ? '1fr auto' : '240px 1fr auto',
        alignItems: 'center',
      }}>
        {/* Brand — clickable, goes home */}
        <button onClick={() => onNav('home')} style={{
          display: 'flex', alignItems: 'center', gap: 10,
          padding: '0 20px', borderRight: narrow ? 'none' : `1px solid ${t.c.edge}`, height: '100%',
          background: 'none', border: 'none', cursor: 'pointer', textAlign: 'left',
          fontFamily: 'inherit',
        }}>
          <div style={{
            width: 26, height: 26, borderRadius: '50%',
            background: t.c.ink, color: t.c.bg3,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: t.font.serif, fontSize: 16, fontStyle: 'italic', lineHeight: 1,
          }}>o</div>
          <span style={{ fontFamily: t.font.serif, fontSize: 22, letterSpacing: '-0.01em', color: t.c.ink }}>OH Community</span>
          <span style={{ fontFamily: t.font.mono, fontSize: 9, color: t.c.muted, letterSpacing: '0.1em', marginTop: 4 }}>
            · BETA
          </span>
        </button>

        {/* Search — wired to global state, filters Home feed (hidden on phones) */}
        {!narrow && (
        <form onSubmit={onSearchSubmit} style={{ padding: '0 24px', display: 'flex', alignItems: 'center' }}>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            background: t.c.bg,
            border: `1px solid ${t.c.edge}`,
            borderRadius: t.radius.md,
            padding: '7px 14px',
            width: 420, maxWidth: '100%',
            color: t.c.muted,
          }}>
            <Icon name="search" size={14} stroke={1.5} />
            <input
              ref={searchRef}
              type="text"
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              placeholder="Search posts, scents, courses…"
              style={{
                flex: 1,
                background: 'transparent',
                border: 'none', outline: 'none',
                fontFamily: t.font.sans, fontSize: 13, color: t.c.ink,
              }}
            />
            {search && (
              <button type="button" onClick={() => setSearch('')} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 2, color: t.c.muted }}>
                <Icon name="close" size={12} />
              </button>
            )}
            <span style={{ fontFamily: t.font.mono, fontSize: 10, padding: '1px 6px', border: `1px solid ${t.c.edge}`, borderRadius: 4 }}>⌘K</span>
          </div>
        </form>
        )}

        {/* Right icons */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '0 16px', position: 'relative' }}>
          <AdminViewAsChip theme={t} />
          <BellWithBadge theme={t}
            onClick={() => { setBellOpen(!bellOpen); SB.logEvent('bell_open'); }} />
          {/* Cart icon parked for MVP v1 — comes back with the marketplace. See MVP_v1_scope.md */}
          {/* <IconButton theme={t} icon="cart" onClick={() => { onNav('cart'); SB.logEvent('cart_click'); }} /> */}
          <button onClick={onAvatarClick} style={{
            background: 'none', border: 'none', padding: 0, cursor: 'pointer',
            display: 'flex', alignItems: 'center',
          }}>
            <Avatar theme={t} name={me.name} seed={me.avatar_seed || 0} size={30} />
          </button>
          {bellOpen && <BellDropdown theme={t} onClose={() => setBellOpen(false)} onNav={onNav} />}
        </div>
      </div>

      {/* Left rail (desktop) */}
      {!narrow && <nav style={{
        borderRight: `1px solid ${t.c.edge}`,
        background: t.c.bg,
        padding: '20px 12px',
        display: 'flex', flexDirection: 'column', gap: 2,
        overflow: 'auto',
      }}>
        {navItems.map(n => (
          <NavItem key={n.id} theme={t} item={n} active={active === n.id} onClick={() => onNav(n.id)} />
        ))}

        {/* "Your circles" section removed — redundant with the Circles nav
            item above and the "Circles I'm in" strip on the profile screen. */}

        {/* Streak / XP box removed for the beta (DECISIONS 2026-08-11: streak badges off).
            The weekly effort tile on Home carries the "N of 7 days" signal instead. */}
        <div onClick={() => onNav('founding')} style={{ marginTop: 'auto', padding: 12, borderRadius: t.radius.lg, background: t.c.bg3, border: `1px solid ${t.c.edgeSoft}`, cursor: 'pointer' }}>
          <Eyebrow theme={t} style={{ color: t.c.accent }}>Beta</Eyebrow>
          <div style={{ marginTop: 6, fontFamily: t.font.serif, fontSize: 17, lineHeight: 1.15, letterSpacing: '-0.01em' }}>
            The founding wall
          </div>
          <div style={{ marginTop: 4, fontSize: 11, color: t.c.muted, lineHeight: 1.4 }}>
            Everyone who joins during the beta is named here, for good.
          </div>
        </div>
      </nav>}

      {/* Main */}
      <main style={{
        overflow: 'auto',
        background: t.c.bg,
        position: 'relative',
      }}>
        <div style={{
          display: 'grid',
          gridTemplateColumns: (rightRail && !narrow) ? 'minmax(0, 1fr) 320px' : '1fr',
          gap: narrow ? 20 : 32,
          padding: noPad ? 0 : (narrow ? '16px 16px 40px' : '28px 36px 60px'),
          maxWidth: 1280, margin: '0 auto',
        }}>
          <div style={{ minWidth: 0 }}>{children}</div>
          {rightRail && <div>{rightRail}</div>}
        </div>
      </main>

      {/* Bottom tab bar (phones) */}
      {narrow && (
        <nav style={{
          borderTop: `1px solid ${t.c.edge}`, background: t.c.bg3,
          display: 'grid', gridTemplateColumns: `repeat(${Math.min(navItems.length, 6)}, 1fr)`,
          paddingBottom: 'env(safe-area-inset-bottom)',
        }}>
          {navItems.slice(0, 6).map(n => {
            const on = active === n.id;
            const short = n.id === 'challenge' ? 'Challenge' : n.label;
            return (
              <button key={n.id} onClick={() => onNav(n.id)} style={{
                background: 'none', border: 'none', cursor: 'pointer', padding: '8px 2px 8px',
                display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
                color: on ? t.c.accent : t.c.ink2, fontFamily: t.font.sans,
              }}>
                <Icon name={n.icon} size={19} stroke={on ? 1.9 : 1.5} />
                <span style={{ fontSize: 10, fontWeight: on ? 600 : 500, whiteSpace: 'nowrap' }}>{short}</span>
              </button>
            );
          })}
        </nav>
      )}
    </div>
  );
};

const IconButton = ({ theme, icon, badge, onClick }) => {
  const t = theme;
  const [h, setH] = React.useState(false);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setH(true)} onMouseLeave={() => setH(false)}
      style={{
        position: 'relative',
        width: 34, height: 34, borderRadius: t.radius.md,
        background: h ? t.c.chip : 'transparent',
        border: 'none', color: t.c.ink,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: 'pointer', transition: 'background 120ms',
      }}>
      <Icon name={icon} size={16} stroke={1.5} />
      {badge && (
        <span style={{
          position: 'absolute', top: 4, right: 4,
          minWidth: 14, height: 14, padding: '0 3px',
          background: t.c.accent, color: t.c.bg3,
          borderRadius: 999,
          fontFamily: t.font.mono, fontSize: 9, fontWeight: 600,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          lineHeight: 1,
        }}>{badge}</span>
      )}
    </button>
  );
};

const NavItem = ({ theme, item, active, onClick }) => {
  const t = theme;
  const [h, setH] = React.useState(false);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setH(true)} onMouseLeave={() => setH(false)}
      style={{
        display: 'flex', alignItems: 'center', gap: 12,
        padding: '9px 12px',
        background: active ? t.c.chip : h ? t.c.bg3 : 'transparent',
        border: 'none', borderRadius: t.radius.md,
        fontFamily: t.font.sans, fontSize: 14, fontWeight: active ? 600 : 500,
        color: active ? t.c.ink : t.c.ink2,
        cursor: 'pointer', textAlign: 'left',
        transition: 'all 120ms ease',
        position: 'relative',
      }}>
      {active && <div style={{ position: 'absolute', left: -12, top: 10, bottom: 10, width: 2, background: t.c.accent, borderRadius: 2 }}/>}
      <Icon name={item.icon} size={17} stroke={1.6} />
      <span>{item.label}</span>
    </button>
  );
};

// Notifications dropdown — honest empty state with the roadmap
// Admin-only "View as" chip — lets admins preview the experience for a
// specific path without changing their own profile. Shows nothing for
// non-admin users. The selected path is read by useApp().effectivePath
// and consumed by HomeFeed, FeaturedExpertsStrip, SuggestedFollowsCard, etc.
const AdminViewAsChip = ({ theme }) => {
  const t = theme;
  const { isAdmin, viewAsPath, setViewAsPath } = useApp();
  const [open, setOpen] = React.useState(false);

  // Important: this useEffect MUST be called unconditionally on every render,
  // before any early returns, or the hook count changes when `isAdmin` flips
  // (Rules of Hooks). The body can short-circuit; the call itself cannot.
  React.useEffect(() => {
    if (!open) return;
    const onDocClick = (e) => {
      if (!e.target.closest?.('[data-view-as-chip]')) setOpen(false);
    };
    setTimeout(() => document.addEventListener('click', onDocClick), 0);
    return () => document.removeEventListener('click', onDocClick);
  }, [open]);

  if (!isAdmin) return null;

  // 'both' is the wildcard that shows everyone via fetchFeaturedExperts;
  // null means "no override, use my profile path".
  const options = [
    { id: 'both',         label: 'All experts' },
    { id: 'recovery',     label: 'Recovery only' },
    { id: 'performance',  label: 'Performance only' },
    { id: null,           label: 'My own path' },
  ];
  const current = options.find(o => o.id === viewAsPath) || options[3];
  const isOverriding = viewAsPath !== null;

  return (
    <div data-view-as-chip style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: 'flex', alignItems: 'center', gap: 6,
        padding: '5px 10px',
        background: isOverriding ? t.c.accentSoft : 'transparent',
        border: `1px solid ${isOverriding ? t.c.accent : t.c.edge}`,
        borderRadius: t.radius.xl,
        fontFamily: t.font.mono, fontSize: 10, letterSpacing: '0.06em',
        color: isOverriding ? t.c.accent : t.c.muted, cursor: 'pointer',
        textTransform: 'uppercase', fontWeight: 600,
      }}>
        VIEW · {current.label.toUpperCase()}
        <span style={{ fontSize: 8 }}>▾</span>
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 32, right: 0, minWidth: 160, zIndex: 9100,
          background: t.c.surface, border: `1px solid ${t.c.edge}`,
          borderRadius: t.radius.md, padding: 4,
          boxShadow: '0 12px 36px rgba(0,0,0,0.18)',
        }}>
          {options.map(o => (
            <button key={String(o.id)} onClick={() => { setViewAsPath(o.id); setOpen(false); }} style={{
              display: 'block', width: '100%', textAlign: 'left',
              padding: '8px 12px',
              background: o.id === viewAsPath ? t.c.bg3 : 'transparent',
              border: 'none', borderRadius: t.radius.sm,
              fontFamily: t.font.sans, fontSize: 13, color: t.c.ink, cursor: 'pointer',
            }}>{o.label}</button>
          ))}
        </div>
      )}
    </div>
  );
};

// Bell icon with unread-count badge. Polls every 60s when the user is signed in.
const BellWithBadge = ({ theme, onClick }) => {
  const t = theme;
  const { isAuthed, session } = useApp();
  const userId = session?.user?.id;
  const [unread, setUnread] = React.useState(0);

  React.useEffect(() => {
    if (!isAuthed || !userId) { setUnread(0); return; }
    let cancelled = false;
    const refresh = () => SB.fetchUnreadNotificationCount(userId)
      .then(n => { if (!cancelled) setUnread(n); });
    refresh();
    const iv = setInterval(refresh, 60000);
    return () => { cancelled = true; clearInterval(iv); };
  }, [isAuthed, userId]);

  return (
    <div style={{ position: 'relative' }}>
      <IconButton theme={t} icon="bell" onClick={onClick} />
      {unread > 0 && (
        <span style={{
          position: 'absolute', top: 4, right: 4,
          minWidth: 16, height: 16, padding: '0 4px',
          borderRadius: 8, background: t.c.accent, color: '#fff',
          fontFamily: t.font.mono, fontSize: 10, fontWeight: 700,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          pointerEvents: 'none',
        }}>{unread > 9 ? '9+' : unread}</span>
      )}
    </div>
  );
};

const BellDropdown = ({ theme, onClose, onNav }) => {
  const t = theme;
  const { isAuthed, signIn, session } = useApp();
  const userId = session?.user?.id;
  const [items, setItems] = React.useState(null); // null = loading

  React.useEffect(() => {
    const onDocClick = (e) => {
      if (!e.target.closest?.('[data-bell-dropdown]')) onClose();
    };
    setTimeout(() => document.addEventListener('click', onDocClick), 0);
    return () => document.removeEventListener('click', onDocClick);
  }, [onClose]);

  // Load notifications for signed-in users; mark all read on dropdown open.
  React.useEffect(() => {
    if (!isAuthed || !userId) { setItems([]); return; }
    SB.fetchNotifications(userId).then(rows => {
      setItems(rows);
      const hasUnread = rows.some(r => !r.read_at);
      if (hasUnread) SB.markAllNotificationsRead(userId);
    });
  }, [isAuthed, userId]);

  const handleNotifClick = (n) => {
    if (n.kind === 'circle_open' && n.payload?.slug) {
      onClose();
      onNav('circle:' + n.payload.slug);
    }
  };

  return (
    <div data-bell-dropdown style={{
      position: 'absolute', top: 50, right: 16,
      width: 360, maxHeight: 480, zIndex: 9000,
      background: t.c.surface,
      border: `1px solid ${t.c.edge}`,
      borderRadius: t.radius.lg,
      boxShadow: '0 20px 60px rgba(0,0,0,0.18)',
      fontFamily: t.font.sans, color: t.c.ink,
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
    }}>
      <div style={{ padding: '14px 18px', borderBottom: `1px solid ${t.c.edgeSoft}` }}>
        <div style={{ fontFamily: t.font.serif, fontSize: 18 }}>Notifications</div>
      </div>

      <div style={{ overflowY: 'auto', flex: 1 }}>
        {!isAuthed ? (
          <div style={{ padding: 18 }}>
            <div style={{ fontSize: 13, color: t.c.ink2, lineHeight: 1.6 }}>
              Sign in to see notifications about new comments, replies in your circles, and communities you're waitlisted for.
            </div>
            <Btn theme={t} variant="primary" full
              onClick={() => { onClose(); signIn(); }}
              style={{ marginTop: 14 }}>
              Sign in
            </Btn>
          </div>
        ) : items === null ? (
          <div style={{ padding: 24, textAlign: 'center', color: t.c.muted, fontSize: 13 }}>Loading…</div>
        ) : items.length === 0 ? (
          <div style={{ padding: 24, fontSize: 13, color: t.c.ink2, lineHeight: 1.6 }}>
            Nothing here yet. We'll let you know when a community you're waitlisted for opens, or when someone replies in your circles.
          </div>
        ) : (
          <div>
            {items.map(n => (
              <button key={n.id} onClick={() => handleNotifClick(n)} style={{
                display: 'block', width: '100%', textAlign: 'left',
                padding: '12px 18px',
                background: n.read_at ? 'transparent' : t.c.bg3,
                border: 'none', borderBottom: `1px solid ${t.c.edgeSoft}`,
                cursor: n.kind === 'circle_open' ? 'pointer' : 'default',
                fontFamily: t.font.sans,
              }}>
                {n.kind === 'circle_open' ? (
                  <>
                    <div style={{ fontSize: 13, color: t.c.ink, lineHeight: 1.45 }}>
                      <strong>{n.payload?.name || 'A community'}</strong> is now open. You were on the waitlist; you're now a member.
                    </div>
                    <div style={{ fontFamily: t.font.mono, fontSize: 10, color: t.c.muted, marginTop: 4, letterSpacing: '0.06em' }}>
                      {new Date(n.created_at).toLocaleDateString()} · TAP TO OPEN
                    </div>
                  </>
                ) : (
                  <div style={{ fontSize: 13, color: t.c.ink2 }}>{n.kind}</div>
                )}
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

Object.assign(window, { DesktopShell, IconButton, NavItem, BellDropdown, BellWithBadge, AdminViewAsChip, useNarrow });
