// anim-chrome.jsx — wspólne komponenty animacji
// Cursor, TypingText, BrowserWindow, AppTopbar, ChaosDoc itd.

// ── Theme tokens (matching tokens.css) ──────────────────────────
const A = {
  cream:    '#f0eee9',
  bg:       '#fafafa',
  surface:  '#ffffff',
  surface2: '#f4f4f5',
  surface3: '#f9fafb',
  border:   '#e5e7eb',
  borderStrong: '#d1d5db',
  ink:      '#0f172a',
  ink2:     '#334155',
  ink3:     '#64748b',
  ink4:     '#94a3b8',
  inkMute:  '#cbd5e1',
  ok:       '#15803d',
  okSoft:   'rgba(21,128,61,0.08)',
  warn:     '#a16207',
  warnSoft: 'rgba(217,119,6,0.10)',
  bad:      '#991b1b',
  // chaos scene tints
  paper:    '#fbfaf6',
  ribbon:   '#0c2960',
  highlight:'#fde68a',
  serif:    "'Newsreader', 'Source Serif 4', Georgia, serif",
  sans:     "'Geist', ui-sans-serif, system-ui, sans-serif",
  mono:     "'Geist Mono', ui-monospace, Menlo, monospace",
};

// ── Cursor ───────────────────────────────────────────────────────
// Drive cursor with a list of keyframes {t, x, y, click?, hide?}.
// Click keyframes show a ripple at that time.
function Cursor({ path, fadeIn = 0.2, fadeOut = 0.3 }) {
  const time = useTime();
  // Find bracketing keyframes
  let prev = path[0], next = path[path.length - 1];
  for (let i = 0; i < path.length - 1; i++) {
    if (time >= path[i].t && time <= path[i + 1].t) {
      prev = path[i]; next = path[i + 1]; break;
    }
  }
  if (time >= path[path.length - 1].t) {
    prev = next = path[path.length - 1];
  }
  const span = next.t - prev.t;
  const u = span > 0 ? clamp((time - prev.t) / span, 0, 1) : 0;
  const eased = Easing.easeInOutCubic(u);
  const x = prev.x + (next.x - prev.x) * eased;
  const y = prev.y + (next.y - prev.y) * eased;

  // Click ripple (any keyframe with click:true within recent 0.4s)
  let clickT = -1;
  for (const k of path) {
    if (k.click && time >= k.t && time <= k.t + 0.5) { clickT = time - k.t; break; }
  }

  // Hide if requested in current segment
  const hide = prev.hide || (u > 0.5 && next.hide);
  const visible = !hide;
  const op = visible
    ? (time < fadeIn ? time / fadeIn : 1)
    : 0;

  return (
    <>
      <div style={{
        position: 'absolute', left: x, top: y,
        width: 28, height: 36,
        transform: 'translate(-4px, -2px)',
        pointerEvents: 'none',
        opacity: op,
        filter: 'drop-shadow(0 2px 6px rgba(15,23,42,0.25))',
        transition: 'opacity 0.2s',
        zIndex: 9999,
      }}>
        <svg viewBox="0 0 28 36" width="28" height="36">
          <path
            d="M3,2 L3,28 L9,22 L13,32 L17,30 L13,20 L21,20 Z"
            fill="#0f172a"
            stroke="#fff"
            strokeWidth="1.5"
            strokeLinejoin="round"
          />
        </svg>
      </div>
      {clickT >= 0 && (
        <div style={{
          position: 'absolute',
          left: x, top: y,
          width: 40, height: 40,
          marginLeft: -8, marginTop: -8,
          borderRadius: '50%',
          border: `2px solid ${A.ink}`,
          transform: `scale(${0.3 + clickT * 2})`,
          opacity: Math.max(0, 1 - clickT * 2.5),
          pointerEvents: 'none',
          zIndex: 9998,
        }} />
      )}
    </>
  );
}

// ── Typing text (uses useSprite localTime) ─────────────────────
function TypingText({
  text, start = 0, cps = 22,
  style, caret = true, caretColor,
  caretBlink = true,
}) {
  const { localTime } = useSprite();
  const t = Math.max(0, localTime - start);
  const shown = Math.min(text.length, Math.floor(t * cps));
  const visible = text.slice(0, shown);
  const showCaret = caret && shown < text.length;
  // Blink caret after typing finished
  const blinking = caretBlink && shown >= text.length && (Math.floor(localTime * 2) % 2 === 0);
  return (
    <span style={style}>
      {visible}
      {(showCaret || blinking) && (
        <span style={{
          display: 'inline-block', width: 2, height: '1em',
          background: caretColor || 'currentColor',
          verticalAlign: 'text-bottom',
          marginLeft: 1,
          marginBottom: 1,
        }} />
      )}
    </span>
  );
}

// ── Count-up number (mono, tabular) ─────────────────────────────
function CountUp({ from = 0, to, start = 0, end = 1, format, style }) {
  const { localTime } = useSprite();
  const u = clamp((localTime - start) / (end - start), 0, 1);
  const eased = Easing.easeOutCubic(u);
  const val = from + (to - from) * eased;
  const out = format ? format(val) : Math.round(val).toLocaleString('pl-PL');
  return <span style={{ fontVariantNumeric: 'tabular-nums', ...style }}>{out}</span>;
}

// ── Browser window chrome ─────────────────────────────────────
function BrowserWindow({ url, children, x, y, w, h, style }) {
  return (
    <div style={{
      position: 'absolute', left: x, top: y, width: w, height: h,
      background: A.surface,
      border: `1px solid ${A.border}`,
      borderRadius: 10,
      overflow: 'hidden',
      boxShadow: '0 30px 80px rgba(15,23,42,0.18), 0 4px 14px rgba(15,23,42,0.08)',
      display: 'flex', flexDirection: 'column',
      ...style,
    }}>
      {/* chrome */}
      <div style={{
        height: 38, padding: '0 14px',
        background: A.surface2,
        borderBottom: `1px solid ${A.border}`,
        display: 'flex', alignItems: 'center', gap: 12,
        flex: '0 0 38px',
      }}>
        <div style={{ display: 'flex', gap: 7 }}>
          {['#ff5f56', '#ffbd2e', '#27c93f'].map((c, i) => (
            <span key={i} style={{ width: 11, height: 11, borderRadius: '50%', background: c, opacity: 0.85 }} />
          ))}
        </div>
        <div style={{
          flex: 1, height: 22,
          background: A.surface, borderRadius: 4,
          border: `1px solid ${A.border}`,
          display: 'flex', alignItems: 'center',
          padding: '0 10px',
          fontFamily: A.mono, fontSize: 11.5, color: A.ink3,
          letterSpacing: '0.02em',
        }}>{url}</div>
      </div>
      <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
        {children}
      </div>
    </div>
  );
}

// ── App topbar — replica of AppShell topbar ──────────────────
function AppTopbar({ breadcrumbs = [], right }) {
  return (
    <div style={{
      height: 56, padding: '0 22px',
      borderBottom: `1px solid ${A.border}`, background: A.surface,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      flex: '0 0 56px',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontFamily: A.serif, fontSize: 20, letterSpacing: '-0.01em', color: A.ink }}>
          <img src="animation/logo.png" alt="" style={{ width: 28, height: 28, objectFit: 'contain', display: 'block' }} />
          <span>Szacownia</span>
        </div>
        <div style={{ width: 1, height: 20, background: A.border }} />
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13.5, color: A.ink3 }}>
          {breadcrumbs.map((b, i) => (
            <React.Fragment key={i}>
              {i > 0 && <span style={{ color: A.ink4 }}>›</span>}
              <span style={{ color: i === breadcrumbs.length - 1 ? A.ink : A.ink3 }}>{b}</span>
            </React.Fragment>
          ))}
        </div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>{right}</div>
    </div>
  );
}

// ── Sidebar (compact) ─────────────────────────────────────────
function AppSidebar({ active = 'operaty' }) {
  const items = [
    { k: 'operaty',   l: 'Operaty',   n: '14' },
    { k: 'blokoteka', l: 'Blokoteka', n: '86' },
    { k: 'branding',  l: 'Branding' },
    { k: 'zmienne',   l: 'Zmienne' },
    { k: 'rciwn',     l: 'RCN' },
    { k: 'klienci',   l: 'Klienci', n: '8' },
  ];
  return (
    <aside style={{
      width: 230, borderRight: `1px solid ${A.border}`, background: A.surface,
      padding: '22px 14px', display: 'flex', flexDirection: 'column', gap: 4,
      flex: '0 0 230px',
    }}>
      <div style={{ fontFamily: A.mono, fontSize: 10.5, color: A.ink3, letterSpacing: '0.14em', textTransform: 'uppercase', paddingLeft: 8, marginBottom: 10 }}>Przestrzeń Robocza</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '9px 10px', border: `1px solid ${A.border}`, borderRadius: 4, marginBottom: 18 }}>
        <div style={{ width: 24, height: 24, borderRadius: 3, background: A.ink, color: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontFamily: A.serif, fontStyle: 'italic', fontSize: 14 }}>k</div>
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <span style={{ fontSize: 13, color: A.ink, fontWeight: 500 }}>Kowalski RM</span>
        </div>
      </div>
      {items.map((it) => (
        <div key={it.k} style={{
          display: 'flex', alignItems: 'center', gap: 10,
          padding: '8px 10px', borderRadius: 4,
          background: it.k === active ? A.surface2 : 'transparent',
          color: it.k === active ? A.ink : A.ink2,
          fontWeight: it.k === active ? 500 : 400, fontSize: 13.5,
        }}>
          <span style={{ flex: 1 }}>{it.l}</span>
          {it.n && <span style={{ fontFamily: A.mono, fontSize: 10.5, color: A.ink3, letterSpacing: '0.04em' }}>{it.n}</span>}
        </div>
      ))}
    </aside>
  );
}

// ── Pill ──────────────────────────────────────────────────────
function Pill({ tone = 'neutral', children, style }) {
  const dot = { neutral: A.ink3, ok: A.ok, warn: A.warn, bad: A.bad }[tone];
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      height: 24, padding: '0 10px',
      border: `1px solid ${A.border}`, borderRadius: 999,
      fontSize: 11.5, color: A.ink2, background: A.surface,
      fontFamily: A.mono, letterSpacing: '0.02em',
      ...style,
    }}>
      <span style={{ width: 5, height: 5, borderRadius: '50%', background: dot }} />
      {children}
    </span>
  );
}

// ── Section caption ──────────────────────────────────────────
function Caps({ children, style }) {
  return (
    <div style={{
      fontFamily: A.mono, fontSize: 11, color: A.ink3,
      letterSpacing: '0.14em', textTransform: 'uppercase',
      ...style,
    }}>{children}</div>
  );
}

// ── Check icon ────────────────────────────────────────────────
function Check({ size = 14, color = '#fff', stroke = 2 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
      <path d="m5 12 4 4 10-10" stroke={color} strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ── Spinner ──────────────────────────────────────────────────
function Spinner({ size = 14, color = '#fff' }) {
  // Driven by Stage time so it spins regardless of Sprite localTime
  const t = useTime();
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      border: `1.8px solid ${color}`, borderRightColor: 'transparent',
      transform: `rotate(${t * 360 * 1.2}deg)`,
    }} />
  );
}

// ── Cascade reveal — fades + slides children in one by one ────
// Children must be array. Each child appears at start + i*stagger.
function Cascade({ start = 0, stagger = 0.12, dur = 0.35, children }) {
  const { localTime } = useSprite();
  const arr = React.Children.toArray(children);
  return (
    <>
      {arr.map((c, i) => {
        const s = start + i * stagger;
        const u = clamp((localTime - s) / dur, 0, 1);
        const eased = Easing.easeOutCubic(u);
        return (
          <div key={i} style={{
            opacity: eased,
            transform: `translateY(${(1 - eased) * 8}px)`,
            transition: 'none',
          }}>
            {c}
          </div>
        );
      })}
    </>
  );
}

// ── Variable chip (yellow highlight, can morph to a value) ───
function Variable({ before, after, swapAt = 999, scope }) {
  const { localTime } = useSprite();
  const t = scope ?? localTime;
  const swapped = t >= swapAt;
  const glow = !swapped && t >= swapAt - 0.4 && t < swapAt;
  return (
    <span style={{
      display: 'inline-block',
      whiteSpace: 'nowrap',
      background: swapped ? 'transparent' : A.warnSoft,
      boxShadow: swapped ? 'none' : `inset 0 0 0 1px rgba(217,119,6,0.45)`,
      padding: '1px 4px', borderRadius: 3,
      transition: 'background 0.2s, box-shadow 0.2s',
      transform: glow ? 'scale(1.06)' : 'scale(1)',
      fontStyle: 'normal', color: A.ink,
    }}>
      {swapped ? after : before}
    </span>
  );
}

Object.assign(window, {
  A, Cursor, TypingText, CountUp,
  BrowserWindow, AppTopbar, AppSidebar,
  Pill, Caps, Check, Spinner, Cascade, Variable,
});
