// crovi-primitives.jsx — small reusable visual atoms for the hero animation.

// Theme: 'dark' (default) or 'light'. Set window.CROVI_THEME = 'light' before
// this script loads to get the cream/editorial palette.
const __THEME = (typeof window !== 'undefined' && window.CROVI_THEME === 'light') ? 'light' : 'dark';
const __isLight = __THEME === 'light';

// T(str): tokenizing transform that maps dark-ink surface oklch values to the
// cream palette when in light mode. Identity in dark mode.
//
// Rule:
//   - Keep colors that are clearly brand (chroma > 0.06) or already light
//     (L >= 0.7). Brand teal, amber, and brand-fill highlights pass through.
//   - Flip everything else by mapping the dark luminance to a warm light one:
//       newL = clamp(0.96 - L * 0.78, 0, 1)
//     and warming blue surfaces (H 230–270) to hue 80. Brand-tinted surfaces
//     (e.g. oklch(0.16 0.02 195)) keep their hue but become light tints.
//
// Template literals resolve before T() runs, so dynamic heat/pulse-based
// colors arrive here as fully-numeric strings and flip cleanly.
function paletteTransform(str) {
  if (typeof str !== 'string' || str.indexOf('oklch(') === -1) return str;
  return str.replace(
    /oklch\(\s*([\d.]+)\s+([\d.]+)\s+(\d+(?:\.\d+)?)\s*(\/\s*[^)]+)?\s*\)/g,
    function (full, lStr, cStr, hStr, alpha) {
      const L = parseFloat(lStr), Ch = parseFloat(cStr), H = parseFloat(hStr);
      if (L >= 0.7) return full;
      if (Ch > 0.06) return full;
      const newL = Math.max(0, Math.min(1, 0.96 - L * 0.78));
      const newH = (H >= 230 && H <= 270) ? 80 : H;
      return `oklch(${newL.toFixed(3)} ${cStr} ${newH}${alpha || ''})`;
    }
  );
}
const Tx = __isLight ? paletteTransform : function (s) { return s; };

const C = __isLight ? {
  // Light/warm cream palette (anchors: --bg #F2EFE9, --bg-card #E8E5DE,
  // --bg-feed #DFDBD4, --bg-sunk #D6D1C8, text #1A1814 / #6B6560 / #A09890)
  ink:     '#F2EFE9',
  ink2:    '#E8E5DE',
  ink3:    '#D6D1C8',
  inkText: '#1A1814',
  inkText2:'#6B6560',
  inkText3:'#A09890',
  brand:   'oklch(0.68 0.12 195)',
  brandHi: 'oklch(0.74 0.14 195)',
  brandInk:'oklch(0.42 0.11 195)',
  brandFill:'oklch(0.92 0.05 195)',
  brandGlow:'oklch(0.74 0.14 195 / 0.35)',
  amber:   'oklch(0.72 0.13 45)',
  amberHi: 'oklch(0.78 0.13 45)',
  amberGlow:'oklch(0.78 0.13 45 / 0.3)',
  display: "'Lustria', Georgia, serif",
  body: "'IBM Plex Sans', system-ui, sans-serif",
  mono: "'Geist Mono', 'JetBrains Mono', ui-monospace, monospace",
} : {
  ink: '#0B0E14',
  ink2: '#11151D',
  ink3: '#1A202C',
  inkText: '#E8E5DE',
  inkText2: '#8A94A6',
  inkText3: '#4A5469',
  brand:   'oklch(0.68 0.12 195)',
  brandHi: 'oklch(0.74 0.14 195)',
  brandInk:'oklch(0.42 0.11 195)',
  brandFill:'oklch(0.92 0.05 195)',
  brandGlow:'oklch(0.74 0.14 195 / 0.35)',
  amber:   'oklch(0.72 0.13 45)',
  amberHi: 'oklch(0.78 0.13 45)',
  amberGlow:'oklch(0.78 0.13 45 / 0.3)',
  display: "'Lustria', Georgia, serif",
  body: "'IBM Plex Sans', system-ui, sans-serif",
  mono: "'Geist Mono', 'JetBrains Mono', ui-monospace, monospace",
};

// ── Live pulsing dot (teal) ─────────────────────────────────────────────────
function LiveDot({ size = 8, color = C.brandHi, time = 0 }) {
  // pulse: 2.2s, opacity 1→0.55, scale 1→0.85
  const phase = (time % 2.2) / 2.2;
  const wave = Math.sin(phase * Math.PI * 2) * 0.5 + 0.5; // 0..1
  const opacity = 1 - wave * 0.45;
  const scale = 1 - wave * 0.15;
  return (
    <span style={{
      display: 'inline-block', verticalAlign: 'middle',
      width: size, height: size, borderRadius: '50%',
      background: color, boxShadow: `0 0 ${size*1.6}px ${color}`,
      opacity, transform: `scale(${scale})`,
      flexShrink: 0,
    }} />
  );
}

// ── Mono eyebrow / chip text ─────────────────────────────────────────────────
function Mono({ children, size = 11, color = C.inkText3, tracking = '0.14em', style }) {
  return (
    <span style={{
      fontFamily: C.mono, fontSize: size, color,
      letterSpacing: tracking, textTransform: 'uppercase',
      ...style,
    }}>{children}</span>
  );
}

// ── Generic node card (for agents, providers, humans) ───────────────────────
// Renders a small rounded card with optional glow. Children = inside.
function NodeCard({
  x, y, w = 220, h = 84,
  variant = 'system', // 'agent' | 'human' | 'system' | 'audit'
  active = false,
  faded = false,
  children,
  style,
}) {
  const isAgent = variant === 'agent' || variant === 'audit';
  const border = isAgent && active ? C.brandHi
              : variant === 'audit' ? C.brandHi
              : C.ink3;
  const glow = isAgent && active ? `0 0 24px ${C.brandGlow}` : 'none';
  const bg = variant === 'human'
    ? Tx('oklch(0.18 0.012 60 / 0.9)')
    : Tx('oklch(0.14 0.015 250 / 0.92)');

  return (
    <div style={{
      position: 'absolute', left: x, top: y,
      width: w, height: h,
      background: bg,
      border: `1px solid ${border}`,
      borderRadius: 14,
      backdropFilter: 'blur(10px)',
      boxShadow: glow,
      opacity: faded ? 0.28 : 1,
      transition: 'opacity 360ms var(--ease, ease)',
      padding: '12px 14px',
      boxSizing: 'border-box',
      color: C.inkText,
      fontFamily: C.body,
      ...style,
    }}>
      {children}
    </div>
  );
}

// ── Sparkle icon (agent marker) ─────────────────────────────────────────────
function Sparkle({ size = 14, color = C.brandHi }) {
  return (
    <svg width={size} height={size} viewBox="0 0 14 14" fill="none" style={{ flexShrink: 0 }}>
      <path d="M7 1 L8.2 5.8 L13 7 L8.2 8.2 L7 13 L5.8 8.2 L1 7 L5.8 5.8 Z"
            fill={color} />
    </svg>
  );
}

// ── Status row inside a node card ───────────────────────────────────────────
function NodeHeader({ icon, title, sub, accent = C.brandHi }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      {icon}
      <div style={{ minWidth: 0, flex: 1 }}>
        <div style={{
          fontFamily: C.display, fontSize: 15, color: C.inkText,
          letterSpacing: '-0.01em', lineHeight: 1.1,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>{title}</div>
        {sub && (
          <div style={{
            fontFamily: C.mono, fontSize: 9.5, color: C.inkText3,
            letterSpacing: '0.12em', textTransform: 'uppercase',
            marginTop: 3, lineHeight: 1,
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          }}>{sub}</div>
        )}
      </div>
    </div>
  );
}

// ── Connector — straight line w/ optional draw-in progress ─────────────────
// progress: 0..1
function Connector({ x1, y1, x2, y2, progress = 1, dashed = true, color = C.ink3, width = 1, glow = false }) {
  const len = Math.hypot(x2 - x1, y2 - y1);
  return (
    <svg style={{
      position: 'absolute', left: 0, top: 0,
      width: '100%', height: '100%', pointerEvents: 'none',
      overflow: 'visible',
    }}>
      <line
        x1={x1} y1={y1} x2={x2} y2={y2}
        stroke={color} strokeWidth={width}
        strokeDasharray={dashed ? '4 4' : `${len} ${len}`}
        strokeDashoffset={dashed ? 0 : (1 - progress) * len}
        opacity={dashed ? progress : 1}
        style={glow ? { filter: `drop-shadow(0 0 4px ${color})` } : {}}
      />
    </svg>
  );
}

// ── Curved connector for tree fan-outs ─────────────────────────────────────
function Curve({ x1, y1, x2, y2, progress = 1, color = C.ink3, glow = false, width = 1 }) {
  // simple cubic with horizontal tangents
  const dx = (x2 - x1) * 0.5;
  const path = `M ${x1} ${y1} C ${x1 + dx} ${y1}, ${x2 - dx} ${y2}, ${x2} ${y2}`;
  const ref = React.useRef(null);
  const [len, setLen] = React.useState(400);
  React.useEffect(() => { if (ref.current) setLen(ref.current.getTotalLength()); }, [path]);
  return (
    <svg style={{
      position: 'absolute', left: 0, top: 0,
      width: '100%', height: '100%', pointerEvents: 'none',
      overflow: 'visible',
    }}>
      <path
        ref={ref}
        d={path}
        stroke={color}
        strokeWidth={width}
        fill="none"
        strokeDasharray={len}
        strokeDashoffset={(1 - progress) * len}
        style={glow ? { filter: `drop-shadow(0 0 6px ${color})` } : {}}
      />
    </svg>
  );
}

// ── Small icons used across nodes ───────────────────────────────────────────
const Icons = {
  building: (color = C.inkText2) => (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <rect x="2" y="3" width="12" height="11" stroke={color} strokeWidth="1.2"/>
      <rect x="4.5" y="5.5" width="2" height="2" fill={color}/>
      <rect x="9.5" y="5.5" width="2" height="2" fill={color}/>
      <rect x="4.5" y="9.5" width="2" height="2" fill={color}/>
      <rect x="9.5" y="9.5" width="2" height="2" fill={color}/>
    </svg>
  ),
  archive: (color = C.inkText2) => (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <rect x="2" y="3" width="12" height="3" stroke={color} strokeWidth="1.2"/>
      <rect x="3" y="6" width="10" height="8" stroke={color} strokeWidth="1.2"/>
      <line x1="6" y1="9" x2="10" y2="9" stroke={color} strokeWidth="1.2"/>
    </svg>
  ),
  doc: (color = C.inkText2) => (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <path d="M3 2 H10 L13 5 V14 H3 Z" stroke={color} strokeWidth="1.2" fill="none"/>
      <line x1="5" y1="7" x2="11" y2="7" stroke={color} strokeWidth="1"/>
      <line x1="5" y1="9.5" x2="11" y2="9.5" stroke={color} strokeWidth="1"/>
      <line x1="5" y1="12" x2="9" y2="12" stroke={color} strokeWidth="1"/>
    </svg>
  ),
  flask: (color = C.inkText2) => (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <path d="M6 2 V6 L3 13 H13 L10 6 V2" stroke={color} strokeWidth="1.2" fill="none"/>
      <line x1="5" y1="2" x2="11" y2="2" stroke={color} strokeWidth="1.2"/>
      <circle cx="7" cy="10" r="0.6" fill={color}/>
      <circle cx="9.5" cy="11" r="0.6" fill={color}/>
    </svg>
  ),
  check: (color = C.brandHi, size = 12) => (
    <svg width={size} height={size} viewBox="0 0 12 12" fill="none">
      <path d="M2 6 L5 9 L10 3" stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  ),
  cross: (color = C.inkText3, size = 10) => (
    <svg width={size} height={size} viewBox="0 0 10 10" fill="none">
      <path d="M2 2 L8 8 M8 2 L2 8" stroke={color} strokeWidth="1.4" strokeLinecap="round"/>
    </svg>
  ),
  alert: (color = C.amber, size = 14) => (
    <svg width={size} height={size} viewBox="0 0 14 14" fill="none">
      <path d="M7 1.5 L13 12 H1 Z" stroke={color} strokeWidth="1.3" fill="none"/>
      <line x1="7" y1="5.5" x2="7" y2="8.5" stroke={color} strokeWidth="1.3" strokeLinecap="round"/>
      <circle cx="7" cy="10.5" r="0.7" fill={color}/>
    </svg>
  ),
  mail: (color = C.inkText2, size = 12) => (
    <svg width={size} height={size} viewBox="0 0 12 12" fill="none">
      <rect x="1" y="3" width="10" height="7" stroke={color} strokeWidth="1.1"/>
      <path d="M1.5 3.5 L6 7 L10.5 3.5" stroke={color} strokeWidth="1.1" fill="none"/>
    </svg>
  ),
  phone: (color = C.inkText2, size = 12) => (
    <svg width={size} height={size} viewBox="0 0 12 12" fill="none">
      <path d="M2.5 1.5 H4.5 L5.5 4 L4 5 C4.5 6.5 5.5 7.5 7 8 L8 6.5 L10.5 7.5 V9.5 C10.5 10.3 9.8 11 9 11 C5.5 11 1 6.5 1 3 C1 2.2 1.7 1.5 2.5 1.5 Z"
            stroke={color} strokeWidth="1.1" fill="none"/>
    </svg>
  ),
  chat: (color = C.inkText2, size = 12) => (
    <svg width={size} height={size} viewBox="0 0 12 12" fill="none">
      <path d="M1.5 2.5 H10.5 V8 H4.5 L2.5 10 V8 H1.5 Z"
            stroke={color} strokeWidth="1.1" fill="none" strokeLinejoin="round"/>
      <circle cx="4" cy="5.2" r="0.6" fill={color}/>
      <circle cx="6" cy="5.2" r="0.6" fill={color}/>
      <circle cx="8" cy="5.2" r="0.6" fill={color}/>
    </svg>
  ),
  loop: (color = C.inkText2, size = 12) => (
    <svg width={size} height={size} viewBox="0 0 12 12" fill="none">
      <path d="M2.2 5.5 A 3.8 3.8 0 0 1 9.5 4.2"
            stroke={color} strokeWidth="1.2" fill="none" strokeLinecap="round"/>
      <path d="M9.8 5.5 A 3.8 3.8 0 0 1 2.5 6.8"
            stroke={color} strokeWidth="1.2" fill="none" strokeLinecap="round"/>
      <path d="M7.2 4.2 L9.5 4.2 L9.5 1.9" stroke={color} strokeWidth="1.2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
      <path d="M4.8 6.8 L2.5 6.8 L2.5 9.1" stroke={color} strokeWidth="1.2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  ),
  zip: (color = C.brandHi, size = 36) => (
    <svg width={size} height={size} viewBox="0 0 36 36" fill="none">
      <path d="M6 4 H22 L30 12 V32 H6 Z" stroke={color} strokeWidth="1.4" fill={C.ink2}/>
      <path d="M22 4 V12 H30" stroke={color} strokeWidth="1.4" fill="none"/>
      <rect x="14" y="6" width="3" height="3" fill={color} opacity="0.5"/>
      <rect x="14" y="9" width="3" height="3" fill={color} opacity="0.5"/>
      <rect x="14" y="12" width="3" height="3" fill={color} opacity="0.5"/>
      <rect x="14" y="15" width="3" height="3" fill={color} opacity="0.5"/>
      <rect x="11" y="20" width="14" height="9" rx="1" fill={color} opacity="0.15" stroke={color} strokeWidth="1"/>
    </svg>
  ),
  user: (color = C.inkText, size = 32) => (
    <svg width={size} height={size} viewBox="0 0 32 32" fill="none">
      <circle cx="16" cy="16" r="15" stroke={color} strokeWidth="1" fill={Tx('oklch(0.22 0.015 60)')}/>
      <circle cx="16" cy="13" r="4" fill={color} opacity="0.85"/>
      <path d="M7 26 C9 21 13 19 16 19 C19 19 23 21 25 26" fill={color} opacity="0.85"/>
    </svg>
  ),
};

window.C = C;
window.T = Tx;
window.CROVI_IS_LIGHT = __isLight;
window.LiveDot = LiveDot;
window.Mono = Mono;
window.NodeCard = NodeCard;
window.Sparkle = Sparkle;
window.NodeHeader = NodeHeader;
window.Connector = Connector;
window.Curve = Curve;
window.Icons = Icons;
