// Numeral — central living number that morphs through metrics with
// digit-by-digit scramble animation.

const METRICS = [
  { key: 'ARR',       label: 'PORTFOLIO · ANNUAL RUN-RATE',   value: 3247.2, unit: 'M', prefix: '$', precision: 1, caption: "signal strength across the portfolio, tracked in real time." },
  { key: 'NRR',       label: 'NET REVENUE RETENTION',         value: 127.4,  unit: '%', precision: 1, caption: "expansion outpacing churn — the quietest kind of growth." },
  { key: 'COVERAGE',  label: 'PIPELINE COVERAGE',             value: 412,    unit: '%', precision: 0, caption: "four rounds of pipeline against this quarter's number." },
  { key: 'HEALTH',    label: 'ACCOUNT HEALTH · COMPOSITE',    value: 87.3,   unit: '/100', precision: 1, caption: "usage, support strain, and renewal timing, fused into one read." },
  { key: 'VELOCITY',  label: 'DEAL VELOCITY · DAYS',          value: 41.8,   unit: 'd', precision: 1, caption: "median days from first signal to closed-won." },
  { key: 'SIGNALS',   label: 'LIVE SIGNALS · 24H',            value: 2048,   unit: '', precision: 0, caption: "events flowing through the mart right now." },
];

const SCRAMBLE_CHARS = '0123456789';

const Numeral = () => {
  const t = useTweaks();
  const motion = t.motion !== false;
  const [idx, setIdx] = useState(0);
  const [displayValue, setDisplayValue] = useState(METRICS[0].value);
  const [scrambleMap, setScrambleMap] = useState({}); // per-digit scramble state
  const metric = METRICS[idx];

  // Cycle metrics automatically (unless user override via palette)
  useEffect(() => {
    if (!motion) return;
    const i = setInterval(() => {
      setIdx((v) => (v + 1) % METRICS.length);
    }, 9000);
    return () => clearInterval(i);
  }, [motion]);

  // Allow palette to set metric by index
  useEffect(() => {
    const h = (e) => { if (typeof e.detail === 'number') setIdx(e.detail); };
    window.addEventListener('signal:metric', h);
    return () => window.removeEventListener('signal:metric', h);
  }, []);

  // Scramble + settle animation when idx changes
  useEffect(() => {
    const target = metric.value;
    const duration = 2200;
    const start = performance.now();
    const startVal = displayValue;
    let raf;
    let lastScramble = 0;
    const step = (now) => {
      const p = Math.min(1, (now - start) / duration);
      // quintic ease-out — slower settle toward the end
      const ease = 1 - Math.pow(1 - p, 5);
      const v = startVal + (target - startVal) * ease;
      setDisplayValue(v);

      // throttle scramble updates to ~10fps so digits don't strobe
      if (p < 0.75 && now - lastScramble > 90) {
        lastScramble = now;
        const sm = {};
        const str = target.toFixed(metric.precision);
        for (let i = 0; i < str.length; i++) {
          if (Math.random() < (1 - p) * 0.55) {
            sm[i] = SCRAMBLE_CHARS[Math.floor(Math.random() * SCRAMBLE_CHARS.length)];
          }
        }
        setScrambleMap(sm);
      } else if (p >= 0.75) {
        setScrambleMap({});
      }

      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [idx]);

  // Micro-drift on the displayed value between cycles (feels alive)
  useEffect(() => {
    if (!motion) return;
    const i = setInterval(() => {
      setDisplayValue((v) => {
        const target = metric.value;
        const jitter = target * 0.0004 * (Math.random() - 0.5);
        return v + jitter;
      });
    }, 1800);
    return () => clearInterval(i);
  }, [idx, motion]);

  const str = displayValue.toFixed(metric.precision);
  // Inject thousand separators for large numbers without decimals
  const withCommas = (s) => {
    const [int, dec] = s.split('.');
    const intC = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    return dec ? `${intC}.${dec}` : intC;
  };
  const display = metric.precision > 0 ? str : withCommas(str);

  return (
    <div className="numeral-wrap">
      <div className="numeral-label">{metric.label}</div>
      <div className="numeral">
        {metric.prefix && <span className="digit" style={{ color: 'var(--ink-dim)', minWidth: '0.35em' }}>{metric.prefix}</span>}
        {display.split('').map((ch, i) => (
          <span key={i} className="digit" style={{ minWidth: ch === '.' || ch === ',' ? '0.22em' : '0.58em' }}>
            {scrambleMap[i] ?? ch}
          </span>
        ))}
        {metric.unit && <span className="unit">{metric.unit}</span>}
      </div>
      <div className="numeral-caption">
        <em>{metric.key}</em> · {metric.caption}
      </div>
      <div className="metric-switcher" role="tablist" aria-label="Metric">
        {METRICS.map((m, i) => (
          <div key={m.key} className={`pip ${i === idx ? 'active' : ''}`} />
        ))}
      </div>
    </div>
  );
};

window.Numeral = Numeral;
window.METRICS = METRICS;
