// Shared UI bits — icons, sidebar, topbar, mock data
// Uses window-scoped exports so other JSX files can reference.

const Ico = {
  search: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>,
  dashboard: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg>,
  orders: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>,
  products: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m7.5 4.27 9 5.15"/><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="M3.3 7 12 12l8.7-5"/><path d="M12 22V12"/></svg>,
  users: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
  ai: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>,
  b2b: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 21h18"/><path d="M5 21V7l7-4 7 4v14"/><path d="M9 9v.01"/><path d="M9 13v.01"/><path d="M9 17v.01"/><path d="M15 9v.01"/><path d="M15 13v.01"/><path d="M15 17v.01"/></svg>,
  datasheet: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M9 13h6"/><path d="M9 17h6"/></svg>,
  gallery: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>,
  pricing: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20.59 13.41 13.42 20.58a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/><line x1="7" x2="7.01" y1="7" y2="7"/></svg>,
  modules: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/></svg>,
  settings: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>,
  bell: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>,
  grid: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/></svg>,
  plus: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>,
  download: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>,
  upload: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg>,
  refresh: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg>,
  filter: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>,
  chev: (p={}) => <svg {...p} width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>,
  chevRight: (p={}) => <svg {...p} width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m9 18 6-6-6-6"/></svg>,
  dots: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>,
  shield: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg>,
  check: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>,
  x: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>,
  arrowUp: (p={}) => <svg {...p} width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" x2="12" y1="19" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>,
  arrowDown: (p={}) => <svg {...p} width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="12" x2="12" y1="5" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>,
  mail: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>,
  lock: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>,
  logout: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>,
  construction: (p={}) => <svg {...p} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="6" width="20" height="8" rx="1"/><path d="M17 14v7"/><path d="M7 14v7"/><path d="M17 3v3"/><path d="M7 3v3"/><path d="M10 14 2.3 6.3"/><path d="m14 6 7.7 7.7"/><path d="m8 6 8 8"/></svg>,
  sparkles: (p={}) => <svg {...p} width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/></svg>,
};

// ───── Logo ─────
// Picks an image from the configured branding (window.currentBranding).
// `kind` = 'auto' | 'icon' | 'logo'  — 'icon' forces the square favicon (good for small sizes / badges).
// `variant` = 'light' | 'dark'       — which logo variant to prefer when falling back to logo_*_url.
function Logo({ size = 32, variant = 'light', kind = 'auto' }) {
  const b = window.currentBranding || {};
  let url = null;
  if (kind === 'icon') {
    url = b.favicon_url || (variant === 'dark' ? b.logo_dark_url : b.logo_light_url);
  } else if (kind === 'logo') {
    url = variant === 'dark' ? (b.logo_dark_url || b.logo_light_url) : (b.logo_light_url || b.logo_dark_url);
  } else {
    // auto — small sizes prefer the compact favicon; bigger sizes prefer the full logo
    if (size <= 40) url = b.favicon_url || (variant === 'dark' ? (b.logo_dark_url || b.logo_light_url) : (b.logo_light_url || b.logo_dark_url));
    else            url = (variant === 'dark' ? (b.logo_dark_url || b.logo_light_url) : (b.logo_light_url || b.logo_dark_url)) || b.favicon_url;
  }
  if (url) {
    return <img src={url} alt="" style={{ width: size, height: size, objectFit: 'contain' }}/>;
  }
  return <div className="sb-logo-mark" style={{ width: size, height: size }} />;
}

// ───── BrandBadge — prominent circular logo for the login / onboarding screens ─────
function BrandBadge({ size = 64 }) {
  const b = window.currentBranding || {};
  const url = b.favicon_url || b.logo_dark_url || b.logo_light_url;
  const pad = Math.round(size * 0.18);
  return (
    <div
      className="brand-badge"
      style={{
        width: size, height: size, borderRadius: '50%',
        background: 'linear-gradient(135deg, #ffffff 0%, #f4f7fb 100%)',
        display: 'grid', placeItems: 'center',
        boxShadow: '0 8px 24px rgba(0,0,0,0.25), 0 1px 0 rgba(255,255,255,0.2) inset',
        padding: pad, boxSizing: 'border-box',
      }}
    >
      {url ? <img src={url} alt="" style={{ width:'100%', height:'100%', objectFit:'contain' }}/>
           : <div className="sb-logo-mark" style={{ width:'100%', height:'100%' }}/>}
    </div>
  );
}

// ───── Sparkline ─────
function Sparkline({ data, color = '#3a6da0', w = 80, h = 26 }) {
  const max = Math.max(...data), min = Math.min(...data);
  const range = max - min || 1;
  const pts = data.map((d, i) => {
    const x = (i / (data.length - 1)) * w;
    const y = h - ((d - min) / range) * (h - 4) - 2;
    return `${x},${y}`;
  }).join(' ');
  return (
    <svg width={w} height={h}>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// ───── Topbar ─────
function Topbar({ crumbs = [], showSearch = true, showLauncher = true }) {
  function openMenu() {
    document.body.classList.add('sidebar-open');
  }
  return (
    <div className="topbar">
      <button
        className="topbar-menu-btn"
        onClick={openMenu}
        aria-label="Otvoriť menu"
        type="button"
      >
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <line x1="3" x2="21" y1="6" y2="6"/>
          <line x1="3" x2="21" y1="12" y2="12"/>
          <line x1="3" x2="21" y1="18" y2="18"/>
        </svg>
      </button>
      <div className="crumbs">
        {crumbs.map((c, i) => (
          <React.Fragment key={i}>
            {i > 0 && <span className="sep">/</span>}
            {i === crumbs.length - 1 ? <b>{c}</b> : <span>{c}</span>}
          </React.Fragment>
        ))}
      </div>
      {showSearch && (
        <div className="topbar-search">
          <Ico.search className="ico"/>
          <input placeholder="Hľadať objednávky, produkty, zákazníkov…" />
          <kbd>⌘K</kbd>
        </div>
      )}
      {showLauncher && (
        <button className="launcher-btn">
          <Ico.grid/> Aplikácie <Ico.chev/>
        </button>
      )}
      <NotificationBell/>
    </div>
  );
}

// ───── Notifications bell ─────
// Plays a 2-tone ding on new notifications + shows a top-right toast + optional
// desktop browser notification. Respects user setting `dd_notif_sound` (off/on).
function playNotifDing() {
  try {
    if (localStorage.getItem('dd_notif_sound') === 'off') return;
    const Ctx = window.AudioContext || window.webkitAudioContext;
    if (!Ctx) return;
    const ctx = window._ddAudioCtx || (window._ddAudioCtx = new Ctx());
    const tone = (freq, start, dur) => {
      const o = ctx.createOscillator();
      const g = ctx.createGain();
      o.type = 'sine'; o.frequency.value = freq;
      o.connect(g); g.connect(ctx.destination);
      const t = ctx.currentTime + start;
      g.gain.setValueAtTime(0, t);
      g.gain.linearRampToValueAtTime(0.18, t + 0.015);
      g.gain.exponentialRampToValueAtTime(0.001, t + dur);
      o.start(t); o.stop(t + dur + 0.02);
    };
    tone(880, 0, 0.22);
    tone(1318.5, 0.12, 0.28); // E6 — brightens the dint
  } catch {}
}

function showNotifToast(item) {
  try {
    const el = document.createElement('div');
    el.className = 'notif-toast';
    const chipText = item.kind === 'new_order' ? 'Objednávka'
      : item.kind === 'b2b_pending'  ? 'B2B'
      : item.kind === 'leave_pending' ? 'Žiadosť o voľno'
      : item.kind === 'ai_approval'   ? 'AI schválenie'
      : 'Nové';
    el.innerHTML = `
      <div class="notif-toast-top">
        <span class="notif-toast-chip">${chipText}</span>
        <button class="notif-toast-close" aria-label="zavrieť">✕</button>
      </div>
      <div class="notif-toast-title">${(item.title || '').replace(/</g,'&lt;')}</div>
      ${item.subtitle ? `<div class="notif-toast-sub">${item.subtitle.replace(/</g,'&lt;')}</div>` : ''}
      <a class="notif-toast-action" href="${item.link || '#'}">${item.action || 'Otvoriť'} →</a>
    `;
    document.body.appendChild(el);
    const close = () => { el.classList.add('fade'); setTimeout(() => el.remove(), 300); };
    el.querySelector('.notif-toast-close').addEventListener('click', close);
    el.querySelector('.notif-toast-action').addEventListener('click', close);
    setTimeout(close, 7000);
  } catch {}
}

function showBrowserNotification(item) {
  try {
    if (!('Notification' in window)) return;
    if (Notification.permission !== 'granted') return;
    const n = new Notification(item.title || 'ERP', {
      body: item.subtitle || '',
      icon: (window.currentBranding?.favicon_url) || '/favicon.ico',
      tag: item.id,
    });
    n.onclick = () => { window.focus(); if (item.link) window.location.hash = item.link.replace(/^#/, ''); n.close(); };
  } catch {}
}

function NotificationBell() {
  const [open, setOpen] = React.useState(false);
  const [items, setItems] = React.useState([]);
  const [lastSeen, setLastSeen] = React.useState(() => {
    try { return localStorage.getItem('dd_notif_seen'); } catch { return null; }
  });
  const prevIdsRef = React.useRef(null); // null = first fetch (don't ding)
  const ref = React.useRef(null);

  async function fetchNow() {
    try {
      const r = await fetch('/api/notifications', { credentials: 'same-origin' });
      if (!r.ok) return;
      const j = await r.json();
      const next = j.items || [];
      // Detect newly arrived items (present now but not in the previous snapshot).
      if (prevIdsRef.current) {
        const prev = prevIdsRef.current;
        const arrived = next.filter(i => !prev.has(i.id));
        if (arrived.length > 0) {
          playNotifDing();
          // Show toast for the most recent (first in sorted list)
          showNotifToast(arrived[0]);
          showBrowserNotification(arrived[0]);
        }
      }
      prevIdsRef.current = new Set(next.map(i => i.id));
      setItems(next);
    } catch {}
  }

  React.useEffect(() => {
    fetchNow();
    const id = setInterval(fetchNow, 30_000);  // faster polling (30s instead of 60s)
    // Also refresh when tab regains focus — admin sees incoming requests asap
    const onVis = () => { if (document.visibilityState === 'visible') fetchNow(); };
    document.addEventListener('visibilitychange', onVis);
    return () => { clearInterval(id); document.removeEventListener('visibilitychange', onVis); };
    // eslint-disable-next-line
  }, []);

  // Ask for browser notification permission on first bell open (one time).
  function maybeAskPermission() {
    try {
      if ('Notification' in window && Notification.permission === 'default') {
        Notification.requestPermission();
      }
    } catch {}
  }

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

  // Mark all as "seen" when dropdown opens — badge clears.
  function openDropdown() {
    setOpen(v => {
      const next = !v;
      if (next) {
        const now = new Date().toISOString();
        setLastSeen(now);
        try { localStorage.setItem('dd_notif_seen', now); } catch {}
        maybeAskPermission();
      }
      return next;
    });
  }

  const unreadCount = lastSeen
    ? items.filter(i => i.ts && new Date(i.ts) > new Date(lastSeen)).length
    : items.length;

  return (
    <div ref={ref} style={{position:'relative'}}>
      <button className="topbar-btn" onClick={openDropdown} title="Notifikácie">
        <Ico.bell/>
        {unreadCount > 0 && <span className="dot" style={{background:'var(--red-500)'}}/>}
      </button>
      {open && (
        <div style={{
          position:'absolute', top:38, right:0, width:380, maxHeight:480, overflow:'auto',
          background:'#fff', border:'1px solid var(--border)', borderRadius:10,
          boxShadow:'var(--shadow-lg)', zIndex:500,
        }}>
          <div style={{padding:'10px 14px', borderBottom:'1px solid var(--border)', display:'flex', alignItems:'center'}}>
            <b style={{fontSize:13}}>Notifikácie</b>
            <span className="small muted" style={{marginLeft:'auto'}}>{items.length} {items.length === 1 ? 'položka' : 'položiek'}</span>
          </div>
          {items.length === 0 ? (
            <div style={{padding:30, textAlign:'center', color:'var(--ink-500)', fontSize:13}}>
              🎉 Žiadne nové udalosti.
            </div>
          ) : (
            <div>
              {items.slice(0, 15).map(n => {
                const chip =
                    n.kind === 'new_order'    ? <span className="chip navy"  style={{padding:'1px 6px', fontSize:10}}>Objednávka</span>
                  : n.kind === 'b2b_pending'  ? <span className="chip amber" style={{padding:'1px 6px', fontSize:10}}>B2B</span>
                  : n.kind === 'leave_pending'? <span className="chip amber" style={{padding:'1px 6px', fontSize:10}}>Žiadosť o voľno</span>
                  : n.kind === 'ai_approval'  ? <span className="chip blue"  style={{padding:'1px 6px', fontSize:10}}>AI</span>
                  :                              <span className="chip gray"  style={{padding:'1px 6px', fontSize:10}}>{n.kind}</span>;
                return (
                  <a
                    key={n.id}
                    href={n.link}
                    onClick={() => setOpen(false)}
                    style={{
                      display:'block', padding:'10px 14px', borderBottom:'1px solid var(--border)',
                      textDecoration:'none', color:'var(--ink-900)',
                    }}
                    onMouseEnter={e => e.currentTarget.style.background = 'var(--blue-50)'}
                    onMouseLeave={e => e.currentTarget.style.background = '#fff'}
                  >
                    <div style={{display:'flex', alignItems:'center', gap:6, marginBottom:2}}>
                      {chip}
                      <span className="small muted" style={{marginLeft:'auto'}}>{relTime(n.ts)}</span>
                    </div>
                    <div style={{fontWeight:500, fontSize:13}}>{n.title}</div>
                    {n.subtitle && <div className="small muted">{n.subtitle}</div>}
                    <div className="small" style={{color:'var(--navy-700)', fontWeight:500, marginTop:2}}>→ {n.action}</div>
                  </a>
                );
              })}
            </div>
          )}
          <NotifSoundToggle/>
        </div>
      )}
    </div>
  );
}

function NotifSoundToggle() {
  const [on, setOn] = React.useState(() => localStorage.getItem('dd_notif_sound') !== 'off');
  function toggle() {
    const next = !on;
    setOn(next);
    try { localStorage.setItem('dd_notif_sound', next ? 'on' : 'off'); } catch {}
    // Preview the ding when turning on
    if (next) playNotifDing();
  }
  return (
    <div style={{padding:'8px 14px', borderTop:'1px solid var(--border)', display:'flex', alignItems:'center', gap:10, fontSize:11.5, color:'var(--ink-500)'}}>
      <span>{on ? '🔔 Zvuk zapnutý' : '🔕 Zvuk vypnutý'}</span>
      <button type="button"
        onClick={toggle}
        className={`notif-switch ${on ? 'on' : ''}`}
        style={{marginLeft:'auto'}}
        aria-pressed={on}
        title={on ? 'Vypnúť zvukové upozornenie' : 'Zapnúť zvukové upozornenie'}>
        <span className="notif-switch-knob"/>
      </button>
    </div>
  );
}

function relTime(iso) {
  if (!iso) return '';
  const t = new Date(iso).getTime();
  const s = Math.max(0, Math.floor((Date.now() - t) / 1000));
  if (s < 60) return 'teraz';
  if (s < 3600) return `pred ${Math.floor(s/60)} min`;
  if (s < 86400) return `pred ${Math.floor(s/3600)} h`;
  return `pred ${Math.floor(s/86400)} dňami`;
}

// ───── Sidebar ─────
// Items map to module slugs in the DB; if a module is disabled (or missing in session.modules),
// we hide the sidebar row. Core modules (users/modules/integrations/settings) can't be disabled.
function Sidebar({ active = 'dashboard' }) {
  // Each sidebar item maps to (1) the module slug (for enable/disable) and
  // (2) the permission key required to see it. Admin has '*' which matches all.
  const items = [
    { id: 'dashboard', module: 'dashboard',        perm: 'dashboard.view',       label: 'Prehľad',              ico: <Ico.dashboard className="ico"/> },
    { id: 'orders',    module: 'orders',           perm: 'orders.view',          label: 'Objednávky',           ico: <Ico.orders className="ico"/> },
    { id: 'products',  module: 'products',         perm: 'products.view',        label: 'Produkty',             ico: <Ico.products className="ico"/> },
    { id: 'customers', module: 'users_customers',  perm: 'users_customers.view', label: 'Zákazníci',            ico: <Ico.users className="ico"/> },
    { id: 'pricing',   module: 'pricing',          perm: 'pricing.view',         label: 'Individ. ceny',        ico: <Ico.pricing className="ico"/> },
    { id: 'b2b',       module: 'b2b',              perm: 'b2b.view',             label: 'B2B schvaľ.',          ico: <Ico.b2b className="ico"/> },
    { id: 'datasheets',module: 'datasheets',       perm: 'datasheets.view',      label: 'Datasheety',           ico: <Ico.datasheet className="ico"/> },
    { id: 'gallery',   module: 'gallery',          perm: 'gallery.view',         label: 'Realizácie',           ico: <Ico.gallery className="ico"/> },
    { id: 'ai',        module: 'ai',               perm: 'ai.view',              label: 'AI asistent',          ico: <Ico.ai className="ico"/> },
    { id: 'calendar',  module: 'calendar',         perm: 'calendar.view',        label: 'Kalendár & dovolenky', ico: <Ico.construction className="ico"/> },
    { id: 'pickup',    module: 'calendar',         perm: 'calendar.view',        label: 'Osobný odber',         ico: <Ico.orders className="ico"/> },
    { id: 'forms',     module: 'forms',            perm: 'forms.view',           label: 'Formuláre',            ico: <Ico.mail className="ico"/> },
  ];
  const admin = [
    { id: 'users',        module: 'users',        perm: 'users.view',        label: 'ERP Používatelia', ico: <Ico.users className="ico"/> },
    { id: 'modules',      module: 'modules',      perm: 'modules.view',      label: 'Moduly',           ico: <Ico.modules className="ico"/> },
    { id: 'integrations', module: 'integrations', perm: 'integrations.view', label: 'Integrácie',       ico: <Ico.sparkles className="ico"/> },
    // Terminál — viditeľný IBA pre role 'system' (sysadmin). Wildcard '*' v admin role to neobíde.
    { id: 'terminal',     module: 'terminal',     perm: null, roleRequire: 'system', label: 'Terminál',  ico: <Ico.construction className="ico"/> },
    { id: 'settings',     module: 'settings',     perm: null,                label: 'Nastavenia',       ico: <Ico.settings className="ico"/> },  // always visible for logged-in users
  ];

  const sess = window.currentSession || {};
  const modulesEnabled = sess.modules
    ? Object.fromEntries(sess.modules.map(m => [m.slug, !!m.is_enabled]))
    : null;
  const isEnabled = (slug) => modulesEnabled == null ? true : modulesEnabled[slug] !== false;

  const perms = sess.permissions || null;
  const hasPerm = (key) => {
    if (!perms) return true;              // before session loads, show everything
    if (perms['*'] === true) return true; // admin wildcard
    if (!key) return true;                // sidebar item with no perm requirement
    return perms[key] === true;
  };

  // Stricter check: ak je `roleRequire` nastavené, treba presnú zhodu role (ignoruje wildcard).
  const currentRole = sess.user?.role;
  const passRoleGate = (it) => !it.roleRequire || currentRole === it.roleRequire;
  const visibleItems = items.filter(it => isEnabled(it.module) && hasPerm(it.perm) && passRoleGate(it));
  const visibleAdmin = admin.filter(it => isEnabled(it.module) && hasPerm(it.perm) && passRoleGate(it));
  return (
    <aside className="sidebar">
      <div className="sb-logo">
        <Logo/>
        <div className="sb-logo-text">{(window.currentBranding && window.currentBranding.app_name) || 'D&D Energy'}<small>ERP</small></div>
      </div>
      <div className="sb-nav">
        {visibleItems.length > 0 && <div className="sb-section">Pracovné moduly</div>}
        {visibleItems.map(it => (
          <a key={it.id} className={active === it.id ? 'active' : ''}>
            {it.ico}<span>{it.label}</span>{it.badge && <span className="badge">{it.badge}</span>}
          </a>
        ))}
        {visibleAdmin.length > 0 && <div className="sb-section" style={{marginTop: 10}}>Administrácia</div>}
        {visibleAdmin.map(it => (
          <a key={it.id} className={active === it.id ? 'active' : ''}>
            {it.ico}<span>{it.label}</span>
          </a>
        ))}
      </div>
      <div className="sb-user">
        <div className="sb-avatar">DK</div>
        <div style={{minWidth:0, flex:1}}>
          <div className="sb-user-name">Dávid K.</div>
          <div className="sb-user-role">Administrátor</div>
        </div>
        <Ico.logout style={{color:'#8ea8c9', cursor:'pointer'}}/>
      </div>
    </aside>
  );
}

// ───── Page wrapper ─────
function AppShell({ active, crumbs, children }) {
  return (
    <div className="app">
      <Sidebar active={active}/>
      <div className="main">
        <Topbar crumbs={crumbs}/>
        <div className="content">{children}</div>
      </div>
    </div>
  );
}

// ───── Mock data ─────
const orders = [
  { id: '#DD-2841', date: '23.04.2026', customer: 'Peter Novotný', email: 'p.novotny@sunhome.sk', total: '4 782,00 €', status: 'new',      items: 3 },
  { id: '#DD-2840', date: '23.04.2026', customer: 'SolarTech s.r.o.', email: 'objednavky@solartech.sk', total: '12 450,00 €', status: 'paid',     items: 8 },
  { id: '#DD-2839', date: '22.04.2026', customer: 'Martina Hrušková', email: 'hruskova@gmail.com', total: '1 240,00 €', status: 'processing', items: 2 },
  { id: '#DD-2838', date: '22.04.2026', customer: 'Energomont a.s.', email: 'nakup@energomont.sk', total: '28 990,00 €', status: 'shipped',    items: 14 },
  { id: '#DD-2837', date: '22.04.2026', customer: 'Jozef Varga', email: 'j.varga@zoznam.sk', total: '890,00 €', status: 'paid',       items: 1 },
  { id: '#DD-2836', date: '21.04.2026', customer: 'EkoDom k.s.', email: 'info@ekodom.sk', total: '6 310,00 €', status: 'completed',  items: 5 },
  { id: '#DD-2835', date: '21.04.2026', customer: 'Lukáš Mikuláš', email: 'lmikulas@me.com', total: '2 190,00 €', status: 'cancelled',items: 2 },
  { id: '#DD-2834', date: '21.04.2026', customer: 'GreenGrid s.r.o.', email: 'orders@greengrid.eu', total: '18 720,00 €', status: 'processing',items: 11 },
  { id: '#DD-2833', date: '20.04.2026', customer: 'Andrea Štefanová', email: 'stefanova@azet.sk', total: '3 450,00 €', status: 'paid',       items: 4 },
  { id: '#DD-2832', date: '20.04.2026', customer: 'Slovakia Solar', email: 'dispecing@slovsolar.sk', total: '9 980,00 €', status: 'shipped',    items: 7 },
];

const statusMap = {
  new:        { label: 'Nová',       cls: 'amber' },
  paid:       { label: 'Zaplatená',  cls: 'navy' },
  processing: { label: 'Spracúva sa',cls: 'gray' },
  shipped:    { label: 'Odoslaná',   cls: 'green' },
  completed:  { label: 'Dokončená',  cls: 'green' },
  cancelled:  { label: 'Zrušená',    cls: 'red' },
};

const products = [
  { sku: 'PV-ECO-5',    name: 'Fotovoltická zostava ECO 5kW',       cat: 'Zostavy',    price: '4 980,00', stock: 14, var: 3, img: 'panel' },
  { sku: 'PV-PRE-8',    name: 'Fotovoltická zostava PREMIUM 8kW',   cat: 'Zostavy',    price: '7 290,00', stock: 8,  var: 4, img: 'panel' },
  { sku: 'PV-PRE-10',   name: 'Fotovoltická zostava PREMIUM 10kW',  cat: 'Zostavy',    price: '9 150,00', stock: 5,  var: 4, img: 'panel' },
  { sku: 'INV-GW-5K',   name: 'Menič GoodWe GW5K-ET',               cat: 'Meniče',     price: '1 240,00', stock: 22, var: 1, img: 'inverter' },
  { sku: 'INV-SOL-8K',  name: 'Menič Solax X3-Hybrid 8kW',          cat: 'Meniče',     price: '1 890,00', stock: 11, var: 2, img: 'inverter' },
  { sku: 'BAT-PYLON-5', name: 'Batéria Pylontech US5000 4.8kWh',    cat: 'Batérie',    price: '1 560,00', stock: 18, var: 1, img: 'battery' },
  { sku: 'BAT-BYD-10',  name: 'Batéria BYD Battery-Box HVS 10.2',   cat: 'Batérie',    price: '3 890,00', stock: 4,  var: 1, img: 'battery' },
  { sku: 'PAN-TRINA',   name: 'Panel Trina Solar Vertex 450Wp',     cat: 'Panely',     price: '89,00',    stock: 312,var: 1, img: 'panel' },
  { sku: 'PAN-JINKO',   name: 'Panel Jinko Tiger Pro 425Wp',        cat: 'Panely',     price: '79,00',    stock: 180,var: 1, img: 'panel' },
];

// ───── Typing / thinking indicator ─────
// Animated dots + optional elapsed-time counter. Used by every AI chat surface.
// Typing indicator with optional rotating "steps" — when steps[] is passed,
// each step shows for `rotateMs` (default 2800ms) and the previous one shows
// as ✓ done. Final step sticks. Without steps, falls back to label + dots.
function TypingIndicator({ label, startedAt, variant = 'bubble', steps = null, rotateMs = 2800 }) {
  const [now, setNow] = React.useState(() => Date.now());
  const [stepIdx, setStepIdx] = React.useState(0);

  React.useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 250);
    return () => clearInterval(id);
  }, []);

  React.useEffect(() => {
    if (!steps || !steps.length) return;
    const t = setInterval(() => setStepIdx(i => Math.min(i + 1, steps.length - 1)), rotateMs);
    return () => clearInterval(t);
  }, [steps, rotateMs]);

  const secs = startedAt ? (now - startedAt) / 1000 : 0;

  // No steps → original compact bubble
  if (!steps || !steps.length) {
    return (
      <div className={`ty-wrap ty-${variant}`}>
        {label && <div className="small muted ty-label">{label}</div>}
        <div className="ty-bubble">
          <span className="ty-dots"><span/><span/><span/></span>
          {startedAt && <span className="ty-timer">{secs.toFixed(1)} s</span>}
        </div>
      </div>
    );
  }

  // With steps → progress checklist (matches the wizard ProgressLog style)
  return (
    <div className={`ty-wrap ty-${variant}`}>
      <div className="ty-bubble" style={{display:'block', padding:'10px 12px', minWidth:280}}>
        <div style={{display:'flex', alignItems:'center', marginBottom:8}}>
          {label && <span className="small" style={{fontWeight:600}}>{label}</span>}
          <span className="ty-timer" style={{marginLeft:'auto'}}>{secs.toFixed(1)} s</span>
        </div>
        <div style={{display:'flex', flexDirection:'column', gap:4}}>
          {steps.map((s, i) => {
            const done = i < stepIdx;
            const active = i === stepIdx;
            return (
              <div key={i} style={{
                display:'flex', alignItems:'center', gap:6,
                fontSize: 12,
                color: active ? 'var(--ink-900, #0f172a)' : (done ? 'var(--ink-500, #6b7280)' : 'var(--ink-300, #cbd5e1)'),
                fontWeight: active ? 600 : 400,
                opacity: done || active ? 1 : 0.5,
                transition: 'all 0.3s',
              }}>
                <span style={{width:14, textAlign:'center', fontSize:11}}>
                  {done ? '✓' : (active ? <span className="ty-spin">⟳</span> : '○')}
                </span>
                <span>{s}</span>
              </div>
            );
          })}
        </div>
      </div>
      <style>{`@keyframes ty-spin{to{transform:rotate(360deg)}} .ty-spin{display:inline-block; animation:ty-spin 1.2s linear infinite}`}</style>
    </div>
  );
}

// Heuristically pick a steps[] script based on the user's last message —
// covers the most common slow tool flows so the user sees real progress.
function pickTypingSteps(lastUserText) {
  const t = String(lastUserText || '').toLowerCase();

  // Product creation (woo_create_product tool)
  if (/(pridaj|vytvor|nahraj|uveď|zaeviduj).*(produkt|panel|menič|striedač|batéri)/i.test(lastUserText || '') ||
      /datasheet|datasheety/i.test(t)) {
    return [
      '📄 Čítam datasheet (ak je priložený)',
      '🏷️ Identifikujem značku a typový kód',
      '⚡ Extrahujem technické parametre',
      '📝 Generujem názov a krátky popis',
      '🎯 Skladám SEO titulok a meta description',
      '📄 Píšem dlhý popis s dde-* template',
      '🔧 Pridávam atribúty (taxonomy + custom)',
      '💾 Posielam do WooCommerce',
    ];
  }

  // Lookups / search
  if (/(nájdi|hľadaj|zobraz|aké|kolko|koľko|kto má|stav|prehľad)/i.test(t)) {
    return [
      '🔍 Analyzujem otázku',
      '🔧 Volám nástroje',
      '📊 Spracovávam dáta',
      '✏️ Skladám odpoveď',
    ];
  }

  // Generic fallback — still better than just "pracuje…"
  return [
    '🤔 Premýšľam o tvojej požiadavke',
    '🔧 Volám potrebné nástroje',
    '✏️ Skladám odpoveď',
  ];
}

Object.assign(window, { Ico, Logo, BrandBadge, Sparkline, Topbar, Sidebar, AppShell, TypingIndicator, pickTypingSteps, orders, statusMap, products });
