// H2Learn — shared UI primitives. Plain JSX, no globals collision.

const { useState, useMemo, useRef, useEffect } = React;

const Icon = ({name, size=16, stroke=2, color='currentColor', style}) => {
  // tiny inline icon set — feather-style
  const paths = {
    search: 'M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16z',
    user: 'M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2 M16 7a4 4 0 11-8 0 4 4 0 018 0z',
    network: 'M12 3v3m0 12v3M3 12h3m12 0h3M5.6 5.6l2.1 2.1m8.6 8.6l2.1 2.1M5.6 18.4l2.1-2.1m8.6-8.6l2.1-2.1M9 12a3 3 0 106 0 3 3 0 00-6 0z',
    sparkles: 'M12 3l1.8 4.2 4.2 1.8-4.2 1.8L12 15l-1.8-4.2L6 9l4.2-1.8L12 3z M19 14l.9 2.1L22 17l-2.1.9L19 20l-.9-2.1L16 17l2.1-.9L19 14z',
    grid: 'M3 3h7v7H3zm11 0h7v7h-7zM3 14h7v7H3zm11 0h7v7h-7z',
    layers: 'M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5',
    bell: 'M6 8a6 6 0 1112 0c0 7 3 9 3 9H3s3-2 3-9zM10.3 21a1.94 1.94 0 003.4 0',
    settings: 'M12 15a3 3 0 100-6 3 3 0 000 6z M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z',
    plus: 'M12 5v14M5 12h14',
    x: 'M18 6L6 18M6 6l12 12',
    check: 'M20 6L9 17l-5-5',
    arrowRight: 'M5 12h14M12 5l7 7-7 7',
    arrowLeft: 'M19 12H5M12 19l-7-7 7-7',
    chevronRight: 'M9 18l6-6-6-6',
    chevronLeft: 'M15 18l-6-6 6-6',
    chevronDown: 'M6 9l6 6 6-6',
    chevronUp: 'M18 15l-6-6-6 6',
    info: 'M12 16v-4M12 8h.01M22 12a10 10 0 11-20 0 10 10 0 0120 0z',
    alert: 'M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0zM12 9v4M12 17h.01',
    star: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z',
    bookmark: 'M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2v16z',
    send: 'M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z',
    eye: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 15a3 3 0 100-6 3 3 0 000 6z',
    book: 'M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2zM22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z',
    trending: 'M23 6l-9.5 9.5-5-5L1 18M17 6h6v6',
    map: 'M1 6v16l7-4 8 4 7-4V2l-7 4-8-4-7 4z M8 2v16M16 6v16',
    zap: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z',
    home: 'M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2zM9 22V12h6v10',
    compass: 'M12 22a10 10 0 100-20 10 10 0 000 20z M16.24 7.76l-2.12 6.36-6.36 2.12 2.12-6.36 6.36-2.12z',
    bridge: 'M3 12h18M5 12V8a3 3 0 016 0v4M13 12V8a3 3 0 016 0v4M3 19h18',
    flask: 'M9 2h6v5l5 11a2 2 0 01-2 3H6a2 2 0 01-2-3L9 7V2z M9 11h6',
    target: 'M12 22a10 10 0 100-20 10 10 0 000 20z M12 18a6 6 0 100-12 6 6 0 000 12z M12 14a2 2 0 100-4 2 2 0 000 4z',
    git: 'M6 3v12 M18 9a3 3 0 100-6 3 3 0 000 6z M6 21a3 3 0 100-6 3 3 0 000 6z M18 9a9 9 0 01-9 9',
    sliders: 'M4 21V14M4 10V3M12 21V12M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6',
    download: 'M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4 M7 10l5 5 5-5 M12 15V3',
    bot: 'M12 8V4H8 M2 14h2 M20 14h2 M15 13a2 2 0 100-4 2 2 0 000 4z M9 13a2 2 0 100-4 2 2 0 000 4z M5 22h14a2 2 0 002-2v-8a4 4 0 00-4-4H7a4 4 0 00-4 4v8a2 2 0 002 2z',
    arrowUpRight: 'M7 17L17 7M7 7h10v10',
  };
  const d = paths[name] || paths.info;
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0, ...style}}>
      <path d={d} />
    </svg>
  );
};

const Btn = ({children, onClick, variant='', size='', icon, iconRight, className='', style, disabled}) => (
  <button
    onClick={onClick}
    disabled={disabled}
    className={['btn', variant, size, className].filter(Boolean).join(' ')}
    style={{...(disabled?{opacity:0.5,cursor:'not-allowed'}:{}), ...style}}>
    {icon && <Icon name={icon} size={14}/>}
    {children}
    {iconRight && <Icon name={iconRight} size={14}/>}
  </button>
);

const Chip = ({children, variant='', size='', dot=false, className='', style, onClick}) => (
  <span
    onClick={onClick}
    className={['chip', variant, size, dot && 'dot', className].filter(Boolean).join(' ')}
    style={onClick?{cursor:'pointer',...style}:style}>
    {children}
  </span>
);

const Card = ({children, variant='', className='', style, onClick}) => (
  <div
    onClick={onClick}
    className={['card', variant, className].filter(Boolean).join(' ')}
    style={style}>
    {children}
  </div>
);

const Avatar = ({initials, size='', kind='person', tone, style, className=''}) => {
  const tones = { person: { bg: 'var(--accent-soft)', fg: 'var(--accent)' },
                  project:{ bg: 'var(--warm-soft)',   fg: 'var(--warm)' },
                  org:    { bg: 'var(--moss-soft)',   fg: 'var(--moss)' } };
  const t = tone || tones[kind] || tones.person;
  return (
    <span className={['avatar', size, className].filter(Boolean).join(' ')}
          style={{background: t.bg, color: t.fg, ...style}}>{initials}</span>
  );
};

const Badge = ({children, level='', className='', style}) => (
  <span className={['badge', level, className].filter(Boolean).join(' ')} style={style}>{children}</span>
);

const Bar = ({value=0, max=100, variant='', height='thin', label, showVal=true}) => (
  <div className="col gap-1 w-full">
    {label && <div className="row justify-between" style={{fontSize: 'var(--t-xs)', color:'var(--ink-3)'}}>
      <span>{label}</span>{showVal && <span className="tnum">{value}{max===100?'%':''}</span>}
    </div>}
    <div className={['bar', height].join(' ')}>
      <div className={'fill ' + variant} style={{width: Math.max(0, Math.min(100, value/max*100)) + '%'}}/>
    </div>
  </div>
);

const ScoreRing = ({value=0, size=88, stroke=8, color='var(--accent)', sub='match'}) => {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const dash = c * (value/100);
  return (
    <div className="score-ring" style={{width: size, height: size}}>
      <svg width={size} height={size}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--surface-3)" strokeWidth={stroke}/>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={stroke}
                strokeDasharray={`${dash} ${c}`} strokeLinecap="round" />
      </svg>
      <div className="num"><span>{value}<span style={{fontSize:'14px',marginLeft:1}}>%</span></span><small>{sub}</small></div>
    </div>
  );
};

// Relationship type → label/color/icon
const RelMeta = {
  Similarity:           { color: 'var(--e-similar)',   chip: 'rel-similar',    icon: 'sparkles' },
  Complementarity:      { color: 'var(--e-comp)',      chip: 'rel-comp',       icon: 'git' },
  'Anti-duplication':   { color: 'var(--e-antidup)',   chip: 'rel-antidup',    icon: 'alert' },
  Bridge:               { color: 'var(--e-bridge)',    chip: 'rel-bridge',     icon: 'bridge' },
  'Method transfer':    { color: 'var(--e-method)',    chip: 'rel-method',     icon: 'flask' },
  'Shared uncertainty': { color: 'var(--e-uncertain)', chip: 'rel-uncertain',  icon: 'compass' },
  'Need-offer':         { color: 'var(--e-needoffer)', chip: 'rel-needoffer',  icon: 'arrowRight' },
  'Project compatibility': { color: 'var(--e-comp)',   chip: 'rel-comp',       icon: 'target' },
};

const RelChip = ({type, size}) => {
  const m = RelMeta[type] || RelMeta.Similarity;
  return <Chip variant={m.chip} size={size}><Icon name={m.icon} size={11}/>{type}</Chip>;
};

// Entity colors (nodes)
const NodeColor = {
  person: 'var(--c-person)', project: 'var(--c-project)', org: 'var(--c-org)',
  domain: 'var(--c-domain)', method: 'var(--c-method)', challenge: 'var(--c-challenge)',
  bottleneck: 'var(--c-bottleneck)', uncertainty: 'var(--c-uncertainty)',
  need: 'var(--c-need)', offer: 'var(--c-offer)',
};

// Entity row — used in sidebar lists, detail drawer "top connections", etc.
const EntityRow = ({entity, kind='person', meta, action, onClick, selected}) => {
  const e = entity;
  return (
    <div onClick={onClick} className={'lift'} style={{
      display:'flex', alignItems:'center', gap: 12, padding: '10px 12px',
      borderRadius: 'var(--r-2)', cursor: onClick ? 'pointer' : 'default',
      background: selected ? 'var(--accent-soft)' : 'transparent',
      border: '1px solid ' + (selected ? 'transparent' : 'var(--line)'),
    }}>
      <Avatar initials={e.initials || (e.name||'?').slice(0,2).toUpperCase()} kind={kind} size="sm"/>
      <div className="col" style={{flex:1, minWidth:0}}>
        <div style={{fontSize:'var(--t-sm)', fontWeight: 600, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>{e.name}</div>
        <div style={{fontSize: 'var(--t-xs)', color:'var(--ink-3)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>
          {e.role || e.type || e.sector || ''}{e.org ? ' · ' + e.org : ''}
        </div>
      </div>
      {meta && <div style={{textAlign:'right', flexShrink:0}}>{meta}</div>}
      {action}
    </div>
  );
};

// Helper: format a fingerprint as horizontal weighted bars
const Fingerprint = ({data, max=50, color='var(--accent)', compact=false}) => (
  <div className="col gap-2">
    {data.map((row, i) => (
      <div key={i} className="row gap-3 items-center">
        <div style={{flex: '0 0 130px', fontSize: compact?'var(--t-xs)':'var(--t-sm)', color:'var(--ink-2)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>{row.d}</div>
        <div className="bar" style={{flex:1, height: compact?5:7}}>
          <div className="fill" style={{background: color, width: (row.w/max*100) + '%'}}/>
        </div>
        <div className="tnum" style={{flex:'0 0 28px', textAlign:'right', fontSize:'var(--t-xs)', color:'var(--ink-3)'}}>{row.w}</div>
      </div>
    ))}
  </div>
);

// useNav — tiny router using location.hash#screen
function useNav() {
  const [screen, setScreenState] = useState(() => (location.hash.replace('#','') || 'landing'));
  useEffect(() => {
    const h = () => setScreenState(location.hash.replace('#','') || 'landing');
    window.addEventListener('hashchange', h);
    return () => window.removeEventListener('hashchange', h);
  }, []);
  const go = (s) => { location.hash = s; window.scrollTo(0,0); };
  return [screen, go];
}

Object.assign(window, {
  Icon, Btn, Chip, Card, Avatar, Badge, Bar, ScoreRing,
  RelMeta, RelChip, NodeColor, EntityRow, Fingerprint, useNav,
});
