// Shared sidebar overflow behaviour.
//
// When the rail (or expanded sidebar) is too short to show every nav item, the
// items that don't fit collapse into a "…" button that opens the rest as a
// flyout menu. The Guided Tour launcher (.sb__tour / .sb__btn--tour) is a
// direct child of .sb, rendered by each page's shell OUTSIDE this component, so
// it always stays pinned to the bottom and visible regardless of screen height.
//
// Exports:
//   window.SbOverflow.useOverflow(expanded, kinds | navCount, secCount) -> { midRef, cut, popBottom }
//   window.SbOverflowNav  — React component that renders the .sb__mid region + flyout.
(function () {
  // ---- inject the shared CSS once ----
  if (!document.getElementById('sb-overflow-css')) {
    const st = document.createElement('style');
    st.id = 'sb-overflow-css';
    st.textContent = `
    .sb__mid { display:flex; flex-direction:column; gap:2px; flex:1 1 auto; min-height:0; overflow:hidden; }
    .sb__mid > * { flex-shrink:0; }
    .sb__mid-sep { height:1px; background:rgba(48,48,48,.08); margin:12px 0; }
    .sb__more { width:100%; height:36px; border:0; background:transparent; border-radius:10px; display:flex; align-items:center; gap:12px; padding:0 10px; color:rgba(48,48,48,.7); transition:all .12s ease; text-align:left; position:relative; cursor:pointer; }
    .sb:not(.is-expanded) .sb__more { padding:0; justify-content:center; }
    .sb__more:hover { background:rgba(48,48,48,.04); color:#303030; }
    .sb__more.is-open { background:linear-gradient(180deg,#FAFAFA,#fff); border:1px solid #fff; box-shadow:0 2px 8px -3px rgba(0,0,0,.15); color:#303030; }
    .sb__more.is-open .sb__btn-ic { color:#303030; }
    .sb__more-pop { position:absolute; left:calc(100% + 8px); width:232px; padding:6px; display:flex; flex-direction:column; gap:2px; background:linear-gradient(180deg,#FFFFFF,#FAFAFA); border:1px solid #E7E7E7; border-radius:14px; box-shadow:0 16px 40px -12px rgba(0,0,0,.28); z-index:60; animation:sbPop .14s ease-out; }
    .sb__more-pop .sb__btn, .sb__more-pop .sb__sbtn { padding:0 10px !important; justify-content:flex-start !important; gap:12px; height:36px; text-decoration:none; }
    .sb__more-pop .sb__btn-lab { display:inline; }
    @keyframes sbPop { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:translateY(0); } }

    /* ---- Ask Trace AI ----
       A near-white pill carrying a thin multi-colour ring that drifts sideways.
       The rail is a field of flat grey rows, so the signal is the moving
       spectrum on the edge, not a block of colour — it stays legible and calm
       while being the only thing in the sidebar that moves.

       Two backgrounds on one element: the white fill clipped to padding-box,
       the spectrum to border-box, so the 1px transparent border becomes the
       ring. The ramp is LINEAR rather than conic on purpose — a conic gradient
       on a 200x36 pill spends almost all of its sweep on the two short ends, so
       the long edges only ever show two or three hues. A horizontal ramp shows
       the whole spectrum along the top and bottom at once, like the reference.

       Seamless loop: the tile is a fixed 220px whose first and last stops are
       the same violet, and the animation shifts it by exactly one tile, so it
       repeats without a jump at any pill width (203px expanded, 43px rail).

       Height matches the 36px nav rows so the rail keeps one rhythm. */
    @keyframes wtAiFlow { to { background-position: 0 0, 220px 0; } }
    @keyframes wtAiFlowHalo { to { background-position: 220px 0; } }

    .sb__ai {
      --wt-ai-ring:linear-gradient(90deg,
        #7A4BF0 0%, #C445F0 14%, #F0426A 28%, #F2B705 42%,
        #20AB47 57%, #17C0E9 71%, #2E5FFF 86%, #7A4BF0 100%);
      width:100%; height:36px; margin:0 0 6px; padding:0 12px;
      display:flex; align-items:center; gap:10px; text-align:left; cursor:pointer; position:relative;
      border:1px solid transparent; border-radius:999px;
      background:linear-gradient(180deg,#FFFFFF 0%,#F7F8FA 100%) padding-box, var(--wt-ai-ring) border-box;
      background-size:auto, 220px 100%;
      background-position:0 0, 0 0;
      box-shadow:0 2px 8px -3px rgba(0,0,0,.15);
      animation:wtAiFlow 7s linear infinite;
      transition:box-shadow .16s ease, transform .12s ease; }
    /* halo — the same ramp, blurred behind the pill, so the colour bleeds onto
       the rail the way it does in the reference. z-index:-1 without a stacking
       context on .sb__ai puts it behind the pill's own white fill. */
    .sb__ai::before { content:''; position:absolute; inset:-2px; z-index:-1;
      border-radius:inherit; background:var(--wt-ai-ring);
      background-size:220px 100%; background-position:0 0;
      filter:blur(6px); opacity:.16;
      animation:wtAiFlowHalo 7s linear infinite;
      transition:opacity .18s ease; }
    .sb__ai:hover::before { opacity:.36; }
    .sb__ai:hover { box-shadow:0 4px 14px -4px rgba(0,0,0,.18); }
    .sb__ai:active { transform:translateY(1px); }
    .sb__ai:focus-visible { outline:2px solid #7A4BF0; outline-offset:2px; }
    /* collapsed rail: the pill becomes a ringed circle, icon only */
    .sb:not(.is-expanded) .sb__ai { padding:0; justify-content:center; }
    .sb__ai-ic { width:22px; height:22px; display:flex; align-items:center; justify-content:center; flex-shrink:0; color:#303030; }
    .sb__ai-lab { font:600 14px/1 'Inter Tight'; letter-spacing:.2px; color:#303030; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    /* in the "…" flyout it is a full-width row like the others */
    .sb__more-pop .sb__ai { margin:2px 0; padding:0 12px !important; justify-content:flex-start !important; }
    @media (prefers-reduced-motion: reduce) {
      .sb__ai { animation:none; transition:none; }
      .sb__ai::before { animation:none; transition:none; }
    }
    `;
    document.head.appendChild(st);
  }

  // ---- React-owned Lucide icon (no createIcons DOM mutation) ----
  const iconCache = {};
  function toPascal(name) {
    return String(name).split('-').map((p) => (p ? p[0].toUpperCase() + p.slice(1) : p)).join('');
  }
  function camelKey(k) { return k.indexOf('-') === -1 ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); }
  function attrsToReact(attrs) { const out = {}; for (const k in attrs) out[camelKey(k)] = attrs[k]; return out; }
  function getNode(name) {
    if (iconCache[name]) return iconCache[name];
    const icons = window.lucide && window.lucide.icons;
    const node = icons ? icons[toPascal(name)] : null;
    if (node) iconCache[name] = node;
    return node || null;
  }
  function LucideIcon(name, style) {
    const node = getNode(name);
    const svgProps = {
      xmlns: 'http://www.w3.org/2000/svg',
      width: (style && style.width) || 20, height: (style && style.height) || 20,
      viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8,
      strokeLinecap: 'round', strokeLinejoin: 'round', style,
    };
    const children = node ? node.map(([tag, attrs], i) => React.createElement(tag, { key: i, ...attrsToReact(attrs) })) : null;
    return React.createElement('svg', svgProps, children);
  }

  // ---- measurement hook ----
  // Two call shapes. Either an ordered list of entry kinds —
  // ['nav','nav','sep','nav','sep','ai','sec'] — or the older
  // (navCount, secCount) pair, which implies one rule between the two groups.
  // Rows are all 36px but a rule is not, so it is the ORDER of the kinds that
  // decides where the cut falls; a rail with a divider inside the nav group has
  // to pass the list. navCount should already include the Ask Trace AI row.
  function useOverflow(expanded, navCount, secCount) {
    const midRef = React.useRef(null);
    const [cut, setCut] = React.useState(null);
    const [popBottom, setPopBottom] = React.useState(70);
    // Arrays are a fresh reference every render, so key the effect on contents.
    const kindsKey = Array.isArray(navCount) ? navCount.join(',') : navCount + ':' + secCount;

    React.useLayoutEffect(() => {
      function measure() {
        const mid = midRef.current;
        if (!mid) return;
        const sb = mid.closest('.sb');
        if (!sb) return;
        const avail = mid.clientHeight;
        if (!avail) return;
        const gap = 2;
        const btn = mid.querySelector('.sb__btn,.sb__sbtn,.sb__more');
        const rowH = (btn ? btn.offsetHeight : 40) + gap;
        const sepEl = mid.querySelector('.sb__mid-sep');
        let sepH = 27 + gap;
        if (sepEl) {
          const cs = getComputedStyle(sepEl);
          sepH = sepEl.offsetHeight + (parseFloat(cs.marginTop) || 0) + (parseFloat(cs.marginBottom) || 0) + gap;
        }
        let kinds;
        if (Array.isArray(navCount)) kinds = navCount;
        else {
          kinds = [];
          for (let i = 0; i < navCount; i++) kinds.push('nav');
          kinds.push('sep');
          for (let i = 0; i < secCount; i++) kinds.push('sec');
        }
        const hOf = (k) => (k === 'sep' ? sepH : rowH);
        let total = 0;
        for (const k of kinds) total += hOf(k);
        let c;
        if (total <= avail + 0.5) c = kinds.length;
        else {
          let used = 0, m = 0;
          for (let i = 0; i < kinds.length; i++) {
            const hh = hOf(kinds[i]);
            if (used + hh + rowH > avail) break;
            used += hh; m++;
          }
          c = m;
        }
        setCut((prev) => (prev === c ? prev : c));
        const tour = sb.querySelector('.sb__tour,.sb__btn--tour');
        const csSb = getComputedStyle(sb);
        const padBot = parseFloat(csSb.paddingBottom) || 14;
        const sbGap = parseFloat(csSb.rowGap || csSb.gap) || 0;
        const pb = padBot + (tour ? tour.offsetHeight : 40) + sbGap + 2;
        setPopBottom((prev) => (Math.abs(prev - pb) < 0.5 ? prev : pb));
      }
      measure();
      const on = () => measure();
      window.addEventListener('resize', on);
      const t1 = setTimeout(measure, 60);
      const t2 = setTimeout(measure, 300);
      return () => { window.removeEventListener('resize', on); clearTimeout(t1); clearTimeout(t2); };
    }, [expanded, kindsKey]);

    return { midRef, cut, popBottom };
  }

  // The assistant entry is shared by every page, so it is declared here rather
  // than repeated in each shell's `secondary` list. Pass ai={false} to hide it.
  const ASK_AI = { label: 'Ask Trace AI', icon: 'sparkles' };

  // An advisor's plan list is their client roster, so the row reads "Client
  // Plans"; an individual's reads "My Plans". The name lives in the plan store
  // (shared/wt-plans.js) and the My Plans page can change it mid-session, so a
  // primary entry marked `plans` takes its label from there and re-renders on
  // 'wt-plans-change'. Pages that don't load the store keep the declared label.
  function useWtPlansLabel() {
    const [, bump] = React.useState(0);
    React.useEffect(() => {
      const on = () => bump((n) => n + 1);
      window.addEventListener('wt-plans-change', on);
      return () => window.removeEventListener('wt-plans-change', on);
    }, []);
    const API = window.WTPlans;
    return API && API.pageTitle ? API.pageTitle() : null;
  }

  // ---- the nav region component ----
  function SbOverflowNav({ expanded, primary, secondary, ai = ASK_AI, onAsk }) {
    const plansLabel = useWtPlansLabel();
    const entries = [];
    primary.forEach((p) => {
      // `sep` on a primary item draws the hairline ABOVE that row, so a shell
      // can break its nav into groups without knowing anything about this file.
      if (p.sep) entries.push({ kind: 'sep' });
      if (p.plans && plansLabel) p = { ...p, label: plansLabel };
      entries.push({ kind: 'nav', ...p });
    });
    entries.push({ kind: 'sep' });
    // Below the rule, heading the support group — it is a thing you ask, not a
    // place you navigate to, so it belongs with Book Consultation and Support.
    if (ai) entries.push({ kind: 'ai', ...ai });
    secondary.forEach((s) => entries.push({ kind: 'sec', ...s }));

    const { midRef, cut, popBottom } = useOverflow(expanded, entries.map((e) => e.kind));
    const [open, setOpen] = React.useState(false);
    const popRef = React.useRef(null);
    const moreRef = React.useRef(null);

    React.useEffect(() => { setOpen(false); }, [expanded]);
    React.useEffect(() => {
      if (!open) return;
      function onDown(e) {
        if (popRef.current && popRef.current.contains(e.target)) return;
        if (moreRef.current && moreRef.current.contains(e.target)) return;
        setOpen(false);
      }
      document.addEventListener('mousedown', onDown);
      return () => document.removeEventListener('mousedown', onDown);
    }, [open]);

    let c = cut == null ? entries.length : Math.max(0, Math.min(cut, entries.length));
    while (c > 0 && entries[c - 1] && entries[c - 1].kind === 'sep') c--;
    const visible = entries.slice(0, c);
    const overflow = entries.slice(c).filter((e) => e.kind !== 'sep');
    const hasMore = overflow.length > 0;

    function navBtn(p, key, inPop) {
      const inner = [
        React.createElement('span', { className: 'sb__btn-ic', key: 'i' }, LucideIcon(p.icon, { width: 18, height: 18 })),
      ];
      if (expanded || inPop) inner.push(React.createElement('span', { className: 'sb__btn-lab', key: 'l' }, p.label));
      const cls = 'sb__btn ' + (p.on ? 'is-on' : '');
      const title = (!expanded && !inPop) ? p.label : undefined;
      if (p.href) return React.createElement('a', { key, className: cls, href: p.href, title, style: { textDecoration: 'none' } }, inner);
      return React.createElement('button', { key, className: cls, title }, inner);
    }
    function aiBtn(a, key, inPop) {
      const inner = [React.createElement('span', { className: 'sb__ai-ic', key: 'i' },
        LucideIcon(a.icon, { width: 18, height: 18 }))];
      if (expanded || inPop) inner.push(React.createElement('span', { className: 'sb__ai-lab', key: 'l' }, a.label));
      return React.createElement('button', {
        key, className: 'sb__ai', title: (!expanded && !inPop) ? a.label : undefined,
        onClick: () => (onAsk ? onAsk() : console.info('[prototype] Ask Trace AI — assistant panel not designed yet.')),
      }, inner);
    }
    function secRow(s, key, inPop) {
      if (expanded && !inPop) {
        return React.createElement('button', { key, className: 'sb__sbtn ' + (s.accent ? 'is-accent' : '') },
          React.createElement('span', { key: 'l' }, s.label),
          LucideIcon('arrow-up-right', { width: 14, height: 14, opacity: .5 }));
      }
      const inner = [React.createElement('span', { className: 'sb__btn-ic', key: 'i' },
        LucideIcon(s.icon, { width: 18, height: 18, color: s.iconColor }))];
      if (inPop) inner.push(React.createElement('span', { className: 'sb__btn-lab', key: 'l' }, s.label));
      return React.createElement('button', { key, className: 'sb__btn ' + (s.accent && inPop ? 'is-accent' : ''), title: !inPop ? s.label : undefined }, inner);
    }

    const midChildren = visible.map((e, i) => {
      if (e.kind === 'sep') return React.createElement('div', { key: 'sep' + i, className: 'sb__mid-sep' });
      if (e.kind === 'nav') return navBtn(e, 'v' + i, false);
      if (e.kind === 'ai') return aiBtn(e, 'v' + i, false);
      return secRow(e, 'v' + i, false);
    });
    if (hasMore) {
      midChildren.push(React.createElement('button', {
        key: 'more', ref: moreRef, className: 'sb__more ' + (open ? 'is-open' : ''),
        title: 'More', 'data-sb-more': '', 'aria-label': 'Show more navigation',
        onClick: () => setOpen((o) => !o),
      },
        React.createElement('span', { className: 'sb__btn-ic', key: 'i' },
          React.createElement('svg', { width: 20, height: 20, viewBox: '0 0 24 24', fill: 'currentColor' },
            React.createElement('circle', { cx: 5, cy: 12, r: 1.9, key: 1 }),
            React.createElement('circle', { cx: 12, cy: 12, r: 1.9, key: 2 }),
            React.createElement('circle', { cx: 19, cy: 12, r: 1.9, key: 3 }))),
        expanded && React.createElement('span', { className: 'sb__btn-lab', key: 'l' }, 'More')));
    }

    return React.createElement(React.Fragment, null,
      React.createElement('div', { className: 'sb__mid', ref: midRef }, midChildren),
      open && hasMore && React.createElement('div', { className: 'sb__more-pop', ref: popRef, style: { bottom: popBottom }, 'data-sb-more': '' },
        overflow.map((e, i) => {
          if (e.kind === 'nav') return navBtn(e, 'p' + i, true);
          if (e.kind === 'ai') return aiBtn(e, 'p' + i, true);
          return secRow(e, 'p' + i, true);
        })));
  }

  window.SbOverflow = { useOverflow };
  window.SbOverflowNav = SbOverflowNav;
})();
