// SnapSnel Oefenen — shared primitives for Brief 03 deliverable
// All components attach to window so other Babel script blocks can use them.

const { useState, useEffect, useRef } = React;

/* ───────────────────────── Lucide ───────────────────────── */
function LucideIcon({ name, size = 20, style, strokeWidth = 2, fill = 'none' }) {
  const icons = window.lucide && window.lucide.icons;
  if (!icons) return null;
  const pascal = name.split('-').map(s => s[0].toUpperCase() + s.slice(1)).join('');
  const data = icons[pascal] || icons[name];
  if (!data) return null;
  const children = Array.isArray(data) ? (data[2] || []) : (data.children || []);
  const html = children.map(c => {
    let tag, a;
    if (Array.isArray(c)) { tag = c[0]; a = c[1] || {}; }
    else if (c && typeof c === 'object') { tag = c.tag || c[0]; a = c.attrs || c.attributes || c[1] || {}; }
    else return '';
    const attrs = Object.entries(a).map(([k, v]) => `${k}="${v}"`).join(' ');
    return `<${tag} ${attrs}/>`;
  }).join('');
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill}
      stroke="currentColor" strokeWidth={strokeWidth}
      strokeLinecap="round" strokeLinejoin="round"
      style={{ flexShrink: 0, ...(style || {}) }}
      dangerouslySetInnerHTML={{ __html: html }} />
  );
}

/* ───────────────────────── Pulse Lottie ───────────────────────── */
let pulsePromise = null;
function getPulseData() {
  if (!pulsePromise) {
    pulsePromise = fetch('ds/assets/pulse-idle.json').then(r => r.json());
  }
  return pulsePromise;
}
function PulseLottie({ size = 48, animate = true }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    let anim, cancelled = false;
    getPulseData().then(data => {
      if (cancelled || !ref.current) return;
      anim = window.lottie.loadAnimation({
        container: ref.current, renderer: 'svg', loop: true, autoplay: animate,
        animationData: JSON.parse(JSON.stringify(data)),
      });
      if (!animate) anim.goToAndStop(0, true);
    });
    return () => { cancelled = true; if (anim) anim.destroy(); };
  }, [animate]);
  return <div ref={ref} style={{ width: size, height: size * 1.4, flexShrink: 0 }} />;
}

/* ───────────────────────── Theme ───────────────────────── */
function theme(dark) {
  return dark ? {
    bg: '#0A0F1E', sidebar: '#0F1525', card: '#1A2235',
    topbar: '#111827', input: '#1A2235',
    text: '#F1F5F9', textSub: '#94A3B8', textDim: '#64748B',
    border: '#1E293B', borderSoft: '#1E293B',
    primary: '#00B4D8', primaryDim: 'rgba(0,180,216,0.12)',
    gold: '#FFD000', goldDim: 'rgba(255,208,0,0.15)',
    correct: '#22C55E', wrong: '#F87171', partial: '#FBBF24',
    dark: true,
  } : {
    bg: '#F8FAFC', sidebar: '#FFFFFF', card: '#FFFFFF',
    topbar: '#FFFFFF', input: '#FFFFFF',
    text: '#0F172A', textSub: '#64748B', textDim: '#94A3B8',
    border: '#E2E8F0', borderSoft: '#F1F5F9',
    primary: '#0096C7', primaryDim: 'rgba(0,150,199,0.10)',
    gold: '#D97706', goldDim: 'rgba(217,119,6,0.10)',
    correct: '#16A34A', wrong: '#DC2626', partial: '#D97706',
    dark: false,
  };
}

/* ───────────────────────── Lockup ───────────────────────── */
function SnapSnelLockup({ size = 'md', color = '#0F172A', primaryColor = '#0096C7' }) {
  const cfg = { sm: { m: 24, f: 16 }, md: { m: 36, f: 20 }, lg: { m: 48, f: 28 } }[size];
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
      <PulseLottie size={cfg.m} />
      <span style={{ fontFamily: 'Fredoka One', fontSize: cfg.f, lineHeight: 1, letterSpacing: '-0.02em' }}>
        <span style={{ color }}>Snap</span><span style={{ color: primaryColor }}>Snel</span>
      </span>
    </div>
  );
}

/* ───────────────────────── MasteryDots ───────────────────────── */
function MasteryDots({ level = 0, size = 'sm', dark = false }) {
  const px = { xs: 5, sm: 7, md: 9, lg: 12 }[size];
  const gap = { xs: 3, sm: 4, md: 5, lg: 6 }[size];
  const primary = dark ? '#00B4D8' : '#0096C7';
  const correct = '#16A34A';
  const ringColor = dark ? '#475569' : '#94A3B8';
  const fill = level === 4 ? correct : primary;
  const lvl = Math.max(0, Math.min(4, level));
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap }}>
      {[0,1,2,3].map(i => {
        const f = i < lvl;
        return <span key={i} style={{
          width: px, height: px, borderRadius: 9999, boxSizing: 'border-box',
          background: f ? fill : 'transparent',
          border: f ? `1.5px solid ${fill}` : `1.5px solid ${ringColor}`,
        }} />;
      })}
    </span>
  );
}

/* ───────────────────────── Cross-pijler ───────────────────────── */
const PILLAR = {
  leren:   { label: 'Leren',   light: '#0096C7', dark: '#00B4D8', icon: 'book-open' },
  oefenen: { label: 'Oefenen', light: '#16A34A', dark: '#22C55E', icon: 'layers' },
  plannen: { label: 'Plannen', light: '#7C3AED', dark: '#9D4EDD', icon: 'calendar-clock' },
};
function HandoffCard({ pillar, direction = 'to', title, subtitle, dark = false }) {
  const c = dark ? PILLAR[pillar].dark : PILLAR[pillar].light;
  return (
    <div style={{
      borderRadius: 10, padding: 12,
      border: `1.5px dashed ${c}`,
      background: `color-mix(in oklch, ${c} 5%, transparent)`,
      display: 'flex', alignItems: 'center', gap: 12, minWidth: 0,
    }}>
      <div style={{
        width: 32, height: 32, borderRadius: 8, flexShrink: 0,
        background: `color-mix(in oklch, ${c} 14%, transparent)`,
        color: c, display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <LucideIcon name={PILLAR[pillar].icon} size={16} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontFamily: 'Nunito', fontWeight: 800, fontSize: 13,
          color: dark ? '#F1F5F9' : '#0F172A', lineHeight: 1.3,
        }}>
          <span style={{ color: c, marginRight: 6 }}>{direction === 'to' ? '→' : '←'}</span>
          {title}
        </div>
        {subtitle && <div style={{
          fontFamily: 'Nunito', fontWeight: 600, fontSize: 11,
          color: dark ? '#94A3B8' : '#64748B', marginTop: 2,
        }}>{subtitle}</div>}
      </div>
    </div>
  );
}

/* ───────────────────────── Slim sidebar (collapsed look) ───────────────────────── */
// For small artboards we use a slim 64px collapsed sidebar so the main area breathes.
function SlimSidebar({ t, active = 'oefenen' }) {
  const items = [
    { id: 'dashboard', icon: 'layout-grid' },
    { id: 'leren',     icon: 'graduation-cap', color: t.dark ? '#00B4D8' : '#0096C7' },
    { id: 'oefenen',   icon: 'target',         color: t.dark ? '#22C55E' : '#16A34A' },
    { id: 'plannen',   icon: 'calendar',       color: t.dark ? '#9D4EDD' : '#7C3AED' },
    { id: 'maker',     icon: 'wand-2' },
    { id: 'ranglijst', icon: 'trophy' },
    { id: 'winkel',    icon: 'store' },
  ];
  return (
    <aside style={{
      width: 64, flexShrink: 0, background: t.sidebar,
      borderRight: `1px solid ${t.border}`,
      display: 'flex', flexDirection: 'column', alignItems: 'center',
      padding: '14px 0 12px', gap: 4,
    }}>
      <div style={{
        width: 36, height: 36, borderRadius: 9, marginBottom: 10,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        background: t.primaryDim, color: t.primary,
        fontFamily: 'Fredoka One', fontSize: 18,
      }}>S</div>
      {items.map(it => {
        const isActive = it.id === active;
        const c = it.color || t.primary;
        return (
          <div key={it.id} style={{
            width: 40, height: 40, borderRadius: 10,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            background: isActive ? `color-mix(in srgb, ${c} ${t.dark ? 14 : 10}%, transparent)` : 'transparent',
            color: isActive ? c : t.textSub,
            position: 'relative',
          }}>
            <LucideIcon name={it.icon} size={18} />
            {isActive && <span style={{
              position: 'absolute', right: 4, top: 4,
              width: 6, height: 6, borderRadius: 9999, background: '#22C55E',
            }} />}
          </div>
        );
      })}
      <div style={{ flex: 1 }} />
      <div style={{ width: 40, height: 40, borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', color: t.textSub }}>
        <LucideIcon name="bell" size={16} />
      </div>
      <div style={{ width: 40, height: 40, borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', color: t.textSub }}>
        <LucideIcon name="settings" size={16} />
      </div>
    </aside>
  );
}

/* ───────────────────────── Mode badge ───────────────────────── */
function ModeBadge({ mode, dark = false }) {
  // Tier-badges differentiëren binnen één Pulse-blue family.
  // Geen Snap-gold (= Snaps), geen amber/indigo (niet in DS).
  // Differentiatie: shade + border-style + icoon.
  const palette = {
    quiz:       { c: dark ? '#48CAE4' : '#0096C7', label: 'Quiz',                 icon: 'zap',          borderStyle: 'solid' },
    oefentoets: { c: dark ? '#00B4D8' : '#0077B6', label: 'Oefentoets · Premium', icon: 'star',         borderStyle: 'solid' },
    examensim:  { c: dark ? '#90E0EF' : '#023E8A', label: 'Examensim · Pro',      icon: 'shield-check', borderStyle: 'double' },
  };
  const p = palette[mode];
  // 'double' needs ≥3px border to render visibly
  const borderWidth = p.borderStyle === 'double' ? 3 : 1;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '4px 10px', borderRadius: 9999,
      background: `color-mix(in srgb, ${p.c} ${dark ? 16 : 12}%, transparent)`,
      color: p.c,
      border: `${borderWidth}px ${p.borderStyle} color-mix(in srgb, ${p.c} ${dark ? 38 : 32}%, transparent)`,
      fontFamily: 'Nunito', fontWeight: 800, fontSize: 11, letterSpacing: 0.4, textTransform: 'uppercase',
    }}>
      <LucideIcon name={p.icon} size={11} />
      {p.label}
    </span>
  );
}

/* ───────────────────────── Generic atoms ───────────────────────── */
function Btn({ kind = 'primary', size = 'md', children, style, t, icon, iconRight, full }) {
  const base = {
    fontFamily: 'Nunito', fontWeight: 800, letterSpacing: 0.2,
    border: 0, cursor: 'pointer', borderRadius: 10,
    display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
    width: full ? '100%' : 'auto',
  };
  const sizes = {
    sm: { padding: '7px 12px', fontSize: 12 },
    md: { padding: '10px 16px', fontSize: 13 },
    lg: { padding: '13px 20px', fontSize: 14 },
  };
  const kinds = {
    primary:  { background: t.primary, color: '#fff' },
    danger:   { background: '#DC2626', color: '#fff' },
    serious:  { background: t.dark ? '#F1F5F9' : '#0F172A', color: t.dark ? '#0A0F1E' : '#F8FAFC' },
    ghost:    { background: 'transparent', color: t.textSub, border: `1px solid ${t.border}` },
    secondary:{ background: t.dark ? '#1E293B' : '#F1F5F9', color: t.text },
  };
  return (
    <button style={{ ...base, ...sizes[size], ...kinds[kind], ...style }}>
      {icon && <LucideIcon name={icon} size={size === 'lg' ? 18 : 14} />}
      <span>{children}</span>
      {iconRight && <LucideIcon name={iconRight} size={size === 'lg' ? 18 : 14} />}
    </button>
  );
}

function Card({ t, children, style, padding = 16 }) {
  return (
    <div style={{
      background: t.card, border: `1px solid ${t.border}`,
      borderRadius: 12, padding, boxSizing: 'border-box', ...style,
    }}>{children}</div>
  );
}

Object.assign(window, {
  LucideIcon, PulseLottie, SnapSnelLockup, theme,
  MasteryDots, HandoffCard, PILLAR,
  SlimSidebar, ModeBadge, Btn, Card,
});
