/* PRAXIS Loop — shared instrument primitives + app chrome */
/* global React */
const { useState, useEffect, useRef } = React;

/* ── hooks ──────────────────────────────────────────────────────────────── */
function useCountUp(target, dur = 1200, start = 0, delay = 0) {
  const [v, setV] = useState(start);
  useEffect(() => {
    // interval-based (not rAF) so it completes even in throttled/hidden frames
    const t0 = performance.now() + delay;
    const ease = t => 1 - Math.pow(1 - t, 3);
    const iv = setInterval(() => {
      const el = performance.now() - t0;
      if (el < 0) return;
      const t = Math.min(1, el / dur);
      setV(start + (target - start) * ease(t));
      if (t >= 1) clearInterval(iv);
    }, 33);
    return () => clearInterval(iv);
  }, [target, dur, start, delay]);
  return v;
}

/* ── atoms ──────────────────────────────────────────────────────────────── */
function Kicker({ children, tone, style }) {
  return <div className="kicker" style={{ color: tone, ...style }}>{children}</div>;
}

function Chip({ children, tone = 'neutral' }) {
  return <span className={`lp-chip lp-chip--${tone}`}>{children}</span>;
}

function MasteryBar({ value, height = 4, animate = true }) {
  const color = window.PRAXIS.ramp(value);
  return (
    <div className="lp-bar" style={{ height }}>
      <i style={{ width: `${Math.max(value, 1.5)}%`, background: color,
                  boxShadow: `0 0 8px ${value >= 75 ? 'rgba(63,216,192,0.5)' : 'transparent'}`,
                  transition: animate ? 'width 900ms cubic-bezier(0.16,1,0.3,1)' : 'none' }}></i>
    </div>
  );
}

function Ring({ value, size = 200, stroke = 5, label, sub, tone = 'var(--signal)', delay = 0 }) {
  const v = useCountUp(value, 1400, 0, delay);
  const r = (size - stroke * 2) / 2;
  const C = 2 * Math.PI * r;
  return (
    <div className="lp-ring" style={{ width: size, height: size }}>
      <svg width={size} height={size}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--line-subtle)" strokeWidth={1}></circle>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={tone} strokeWidth={stroke}
          strokeLinecap="round" strokeDasharray={C} strokeDashoffset={C * (1 - v / 100)}
          transform={`rotate(-90 ${size/2} ${size/2})`}
          style={{ filter: `drop-shadow(0 0 8px ${tone === 'var(--signal)' ? 'rgba(232,178,58,0.45)' : 'rgba(63,216,192,0.45)'})` }}></circle>
      </svg>
      <div className="lp-ring__txt">
        <div className="lp-ring__num"><span style={{ fontFamily: 'var(--font-display)', fontSize: size * 0.26, color: 'var(--text-primary)' }}>{Math.round(v)}</span><span className="lp-ring__den mono">/100</span></div>
        {label ? <div className="kicker" style={{ marginTop: 4 }}>{label}</div> : null}
        {sub ? <div className="lp-ring__sub">{sub}</div> : null}
      </div>
    </div>
  );
}

function Btn({ primary, children, onClick, small, style, disabled }) {
  return (
    <button
      className={`lp-btn ${primary ? 'lp-btn--primary' : 'lp-btn--ghost'} ${small ? 'lp-btn--sm' : ''}`}
      style={style} disabled={disabled}
      onMouseEnter={() => window.PraxisSound.hover()}
      onClick={e => { window.PraxisSound.tick(); onClick && onClick(e); }}>
      {children}
    </button>
  );
}

function StatCell({ k, v, tone }) {
  return (
    <div className="lp-stat">
      <div className="lp-stat__v data" style={{ color: tone }}>{v}</div>
      <div className="lp-stat__k">{k}</div>
    </div>
  );
}

/* ── top bar: wordmark · search · breadcrumb cosmology · telemetry · sound ── */
function TopBar({ crumbs, onCrumb, sound, onSound, lex }) {
  const atUniverse = crumbs.length === 1;
  return (
    <header className="lp-topbar">
      <button className="lp-mark" onClick={() => onCrumb(0)} aria-label="Praxis home">
        <svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true">
          <circle cx="11" cy="11" r="9.5" fill="none" stroke="var(--signal)" strokeWidth="1.4"></circle>
          <circle cx="11" cy="11" r="4.5" fill="none" stroke="var(--cyan)" strokeWidth="1.2"></circle>
          <circle cx="11" cy="11" r="1.6" fill="var(--text-primary)"></circle>
        </svg>
        <span className="lp-mark__name">{lex.appName}</span>
      </button>

      {atUniverse ? (
        <div className="lp-search" role="search">
          <svg width="13" height="13" viewBox="0 0 13 13" aria-hidden="true">
            <circle cx="5.5" cy="5.5" r="4" fill="none" stroke="currentColor" strokeWidth="1.3"></circle>
            <path d="M8.7 8.7l3 3" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"></path>
          </svg>
          <span className="lp-search__ph">Ask the universe…</span>
          <span className="lp-search__kbd mono">⌘K</span>
        </div>
      ) : (
        <nav className="lp-crumbs" aria-label="Altitude">
          {crumbs.map((c, i) => (
            <React.Fragment key={i}>
              {i > 0 ? <span className="lp-crumbs__sep" aria-hidden="true">›</span> : null}
              <button className={`lp-crumbs__item ${i === crumbs.length - 1 ? 'is-here' : ''}`}
                onClick={() => onCrumb(i)} disabled={i === crumbs.length - 1}>{c}</button>
            </React.Fragment>
          ))}
        </nav>
      )}

      <div className="lp-topbar__right">
        <span className="lp-tele">sectors <b className="data">13</b></span>
        <span className="lp-tele">explored <b className="data">13</b></span>
        <span className="lp-tele">stars <b className="data">71</b></span>
        <span className="lp-tele">loop <b className="data" style={{ color: 'var(--signal)' }}>amber</b></span>
        <button className={`lp-sound ${sound ? 'is-on' : ''}`} onClick={onSound}
          aria-label={sound ? 'Mute ambient sound' : 'Enable ambient sound'} title="Ambient sound">
          <svg width="15" height="15" viewBox="0 0 15 15" aria-hidden="true">
            <path d="M2 5.5v4h2.6L8 12.4V2.6L4.6 5.5H2z" fill="currentColor"></path>
            {sound
              ? <path d="M10 4.5c1 .8 1.6 1.8 1.6 3s-.6 2.2-1.6 3M11.5 2.8c1.6 1.2 2.5 2.8 2.5 4.7s-.9 3.5-2.5 4.7" fill="none" stroke="currentColor" strokeWidth="1.1" strokeLinecap="round"></path>
              : <path d="M10.5 5.5l3.5 4M14 5.5l-3.5 4" fill="none" stroke="currentColor" strokeWidth="1.1" strokeLinecap="round"></path>}
          </svg>
        </button>
      </div>
    </header>
  );
}

/* ── status strip (bottom) ──────────────────────────────────────────────── */
function StatusStrip({ items, hint }) {
  return (
    <footer className="lp-status">
      <div className="lp-status__items">
        {items.map((it, i) => (
          <span key={i} className="lp-status__cell">
            <span className="kicker">{it.k}</span>
            <b className="data" style={{ color: it.tone || 'var(--text-primary)' }}>{it.v}</b>
          </span>
        ))}
      </div>
      {hint ? <span className="lp-status__hint mono">{hint}</span> : null}
    </footer>
  );
}

Object.assign(window, { useCountUp, Kicker, Chip, MasteryBar, Ring, Btn, StatCell, TopBar, StatusStrip });
