/* global React */
// Command Center — shared visual primitives

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

// ---------- Hooks ----------
function useTicker(intervalMs = 1000) {
  const [, setT] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setT(t => t + 1), intervalMs);
    return () => clearInterval(id);
  }, [intervalMs]);
}

// Animated count-up
function useCountUp(target, duration = 1200, decimals = 0, deps = []) {
  const [v, setV] = useState(0);
  useEffect(() => {
    const start = performance.now();
    const from = 0;
    let raf;
    const tick = (now) => {
      const t = Math.min(1, (now - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      setV(from + (target - from) * eased);
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [target, ...deps]);
  return v.toFixed(decimals);
}

function fmtNum(n, decimals = 0) {
  const fixed = Number(n).toFixed(decimals);
  const [int, dec] = fixed.split('.');
  const withCommas = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return dec ? `${withCommas}.${dec}` : withCommas;
}

function fmtTimeAgo(s) {
  if (s < 60) return s + 's';
  if (s < 3600) return Math.floor(s / 60) + 'm';
  return Math.floor(s / 3600) + 'h';
}

// ---------- Clock ----------
function CcClock({ showDate = true }) {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const time = now.toLocaleTimeString('en-AU', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
  const date = now.toLocaleDateString('en-AU', { weekday: 'short', day: '2-digit', month: 'short' });
  return (
    <div className="cc-row" style={{ gap: 14 }}>
      {showDate && <span className="cc-label-sm">{date}</span>}
      <span className="cc-clock">{time}</span>
    </div>
  );
}
window.CcClock = CcClock;

// ---------- Topbar ----------
function CcTopbar({ section = 'Overview', extras }) {
  return (
    <header className="cc-topbar">
      <div className="cc-row" style={{ gap: 14 }}>
        <div className="cc-row" style={{ gap: 10 }}>
          <span className="cc-brand-mark"></span>
          <div className="cc-col" style={{ gap: 0, lineHeight: 1 }}>
            <span style={{ fontWeight: 700, letterSpacing: '-0.5px', fontSize: 16 }}>
              Command<span style={{ color: 'var(--color-accent)' }}>Center</span>
            </span>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--color-subtle)', letterSpacing: '1.2px', marginTop: 2, textTransform: 'uppercase' }}>
              ops / portfolio
            </span>
          </div>
        </div>
        <span style={{ width: 1, height: 24, background: 'var(--color-line)', margin: '0 4px' }}></span>
        <span className="cc-label" style={{ fontSize: 11 }}>{section}</span>
        <span className="cc-live-dot">live</span>
      </div>

      <div className="cc-topbar-meta">
        {extras}
        <CcClock />
      </div>
    </header>
  );
}
window.CcTopbar = CcTopbar;

// ---------- Sparkline ----------
function CcSparkline({ values, w = 70, h = 22, color = 'var(--color-accent)', fill = true, animate = true }) {
  const max = Math.max(...values);
  const min = Math.min(...values);
  const range = max - min || 1;
  const points = values.map((v, i) => {
    const x = (i / (values.length - 1)) * w;
    const y = h - ((v - min) / range) * h;
    return [x, y];
  });
  const d = 'M ' + points.map(p => p.join(',')).join(' L ');
  const fillD = d + ` L ${w},${h} L 0,${h} Z`;
  const len = w * 2;

  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none">
      {fill && (
        <path d={fillD} fill={color} opacity="0.18" />
      )}
      <path
        d={d}
        fill="none"
        stroke={color}
        strokeWidth="1.5"
        strokeLinecap="round"
        strokeLinejoin="round"
        className={animate ? 'cc-draw' : ''}
        style={{ '--len': len }}
      />
      <circle
        cx={points[points.length - 1][0]}
        cy={points[points.length - 1][1]}
        r="2"
        fill={color}
      />
    </svg>
  );
}
window.CcSparkline = CcSparkline;

// ---------- KPI Card ----------
function CcKpi({ kpi, mega = false }) {
  const v = useCountUp(kpi.value, 1400, kpi.decimals || 0);
  return (
    <div className={'cc-kpi' + (mega ? ' cc-kpi-mega' : '')} data-tint={kpi.tint}>
      <div className="cc-kpi-label">
        <span>{kpi.label}</span>
        <span className={'cc-kpi-delta ' + kpi.deltaDir}>
          {kpi.deltaDir === 'pos' ? '▲' : kpi.deltaDir === 'neg' ? '▼' : '·'} {kpi.delta}
        </span>
      </div>
      <div className="cc-kpi-value">
        {kpi.prefix && <span className="cc-kpi-prefix">{kpi.prefix}</span>}
        {fmtNum(v, kpi.decimals || 0)}
        {kpi.suffix && <span className="cc-kpi-suffix">{kpi.suffix}</span>}
      </div>
      <div className="cc-kpi-meta">
        <span>{kpi.sub}</span>
      </div>
      {kpi.spark && (
        <div className="cc-kpi-spark">
          <CcSparkline values={kpi.spark} w={92} h={28} color={
            kpi.tint === 'rl' ? 'var(--rl-accent)' :
            kpi.tint === 'ba' ? 'var(--ba-accent)' :
            'var(--color-accent)'
          } />
        </div>
      )}
    </div>
  );
}
window.CcKpi = CcKpi;

// ---------- Line / Area Chart ----------
function CcLineChart({ series, w = 600, h = 220, color = 'var(--color-accent)', showAxis = true, label }) {
  const padL = showAxis ? 36 : 4;
  const padR = 8, padT = 8, padB = showAxis ? 22 : 4;
  const innerW = w - padL - padR;
  const innerH = h - padT - padB;

  const allVals = series.flatMap(s => s.values);
  const max = Math.max(...allVals);
  const min = 0;
  const range = max - min || 1;

  const xFor = (i, n) => padL + (i / (n - 1)) * innerW;
  const yFor = (v) => padT + innerH - ((v - min) / range) * innerH;

  // y-axis ticks
  const yTicks = 4;
  const ticks = [];
  for (let i = 0; i <= yTicks; i++) {
    const v = min + (i / yTicks) * range;
    ticks.push({ v, y: yFor(v) });
  }

  return (
    <svg width="100%" height="100%" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: 'block' }}>
      {/* gridlines */}
      {showAxis && ticks.map((t, i) => (
        <g key={i}>
          <line x1={padL} x2={w - padR} y1={t.y} y2={t.y} stroke="var(--color-line-soft)" strokeWidth="1" strokeDasharray={i === 0 ? '' : '2 3'} />
          <text x={padL - 6} y={t.y + 3} textAnchor="end" fill="var(--color-subtle)" fontSize="9" fontFamily="var(--font-mono)" style={{ letterSpacing: '0.5px' }}>
            {t.v >= 1000 ? (t.v / 1000).toFixed(t.v >= 10000 ? 0 : 1) + 'k' : Math.round(t.v)}
          </text>
        </g>
      ))}

      {/* x-axis dates */}
      {showAxis && series[0] && series[0].days && (() => {
        const days = series[0].days;
        const n = days.length;
        const stepIdx = [0, Math.floor(n / 3), Math.floor((2 * n) / 3), n - 1];
        return stepIdx.map((idx, i) => (
          <text key={i}
            x={xFor(idx, n)}
            y={h - 6}
            textAnchor={i === 0 ? 'start' : i === stepIdx.length - 1 ? 'end' : 'middle'}
            fill="var(--color-subtle)" fontSize="9" fontFamily="var(--font-mono)"
            style={{ letterSpacing: '0.5px' }}
          >
            {days[idx].toLocaleDateString('en-AU', { day: '2-digit', month: 'short' }).toUpperCase()}
          </text>
        ));
      })()}

      {/* series */}
      {series.map((s, si) => {
        const pts = s.values.map((v, i) => [xFor(i, s.values.length), yFor(v)]);
        const d = 'M ' + pts.map(p => p.join(',')).join(' L ');
        const fillD = d + ` L ${pts[pts.length - 1][0]},${padT + innerH} L ${pts[0][0]},${padT + innerH} Z`;
        const c = s.color || color;
        const len = innerW * 1.5;
        return (
          <g key={si}>
            <path d={fillD} fill={c} opacity="0.14" />
            <path d={d} fill="none" stroke={c} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"
              className="cc-draw" style={{ '--len': len }} />
            {/* last point */}
            <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="3" fill={c} />
            <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="6" fill="none" stroke={c} strokeWidth="1" opacity="0.5">
              <animate attributeName="r" from="3" to="10" dur="2s" repeatCount="indefinite" />
              <animate attributeName="opacity" from="0.6" to="0" dur="2s" repeatCount="indefinite" />
            </circle>
          </g>
        );
      })}
    </svg>
  );
}
window.CcLineChart = CcLineChart;

// ---------- Donut ----------
function CcDonut({ segments, size = 150, stroke = 18 }) {
  const total = segments.reduce((s, x) => s + x.value, 0);
  const r = size / 2 - stroke / 2;
  const c = 2 * Math.PI * r;
  let offset = 0;

  return (
    <div className="cc-donut-wrap">
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ flexShrink: 0 }}>
        <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--color-bar-bg)" strokeWidth={stroke} />
        {segments.map((seg, i) => {
          const frac = seg.value / total;
          const len = c * frac;
          const dash = `${len} ${c}`;
          const dashOff = -offset;
          offset += len;
          return (
            <circle
              key={i}
              cx={size / 2} cy={size / 2} r={r}
              fill="none"
              stroke={seg.color}
              strokeWidth={stroke}
              strokeDasharray={dash}
              strokeDashoffset={dashOff}
              strokeLinecap="butt"
              transform={`rotate(-90 ${size / 2} ${size / 2})`}
              style={{ transition: 'stroke-dasharray 800ms cubic-bezier(0.2, 0.8, 0.2, 1)' }}
            />
          );
        })}
        <text x={size / 2} y={size / 2 - 4} textAnchor="middle"
          fontFamily="var(--font-mono)" fontSize="22" fontWeight="600" fill="var(--color-fg)"
          style={{ letterSpacing: '-0.5px' }}>
          {fmtNum(total)}
        </text>
        <text x={size / 2} y={size / 2 + 14} textAnchor="middle"
          fontFamily="var(--font-mono)" fontSize="9" fill="var(--color-subtle)"
          style={{ letterSpacing: '1.5px' }}>
          TOTAL
        </text>
      </svg>
      <div className="cc-donut-legend">
        {segments.map((seg, i) => (
          <div key={i} className="cc-donut-legend-row">
            <span className="cc-donut-legend-swatch" style={{ background: seg.color }}></span>
            <span className="cc-donut-legend-name">{seg.name}</span>
            <span className="cc-donut-legend-val">
              {fmtNum(seg.value)}
              <span style={{ color: 'var(--color-subtle)', marginLeft: 6 }}>
                {((seg.value / total) * 100).toFixed(0)}%
              </span>
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}
window.CcDonut = CcDonut;

// ---------- Activity Feed ----------
function CcFeed({ initial = [], maxRows = 9, intervalMs = 2200 }) {
  const [items, setItems] = useState(initial);
  const counterRef = useRef(initial.length);

  useEffect(() => {
    if (!intervalMs) return;
    const id = setInterval(() => {
      const data = window.CC_DATA;
      const tpl = data.makeFeed ? data.makeFeed(1, 0)[0] : null;
      if (!tpl) return;
      const newItem = { ...tpl, id: 'live-' + (counterRef.current++), secondsAgo: 0 };
      setItems(prev => [newItem, ...prev].slice(0, maxRows));
    }, intervalMs);
    return () => clearInterval(id);
  }, [intervalMs, maxRows]);

  // Age items every second
  useEffect(() => {
    const id = setInterval(() => {
      setItems(prev => prev.map(it => ({ ...it, secondsAgo: it.secondsAgo + 1 })));
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return (
    <div className="cc-feed">
      {items.slice(0, maxRows).map(it => (
        <div className="cc-feed-row" key={it.id}>
          <span className="cc-feed-time">{fmtTimeAgo(it.secondsAgo)}</span>
          <span className="cc-feed-dot" data-kind={it.kind}></span>
          <span className="cc-feed-msg">
            <span className="cc-feed-strong">{it.msg}</span>
            <span className="cc-feed-dim"> · {it.tag}</span>
          </span>
          <span className="cc-feed-amt">{it.amt}</span>
        </div>
      ))}
    </div>
  );
}
window.CcFeed = CcFeed;

// ---------- Bars list ----------
function CcBars({ items, maxValue, color = 'var(--color-accent)' }) {
  const max = maxValue || Math.max(...items.map(i => i.value));
  return (
    <div className="cc-bars">
      {items.map((it, i) => (
        <div className="cc-bar-row" key={i}>
          <span className="cc-bar-name">{it.name}</span>
          <span className="cc-bar-val">{fmtNum(it.value)}</span>
          <div className="cc-bar-track">
            <div className="cc-bar-fill" style={{
              width: (it.value / max * 100) + '%',
              background: it.color || color,
              animation: `cc-bar-grow 900ms cubic-bezier(0.2, 0.8, 0.2, 1) ${i * 80}ms both`,
            }} />
          </div>
        </div>
      ))}
      <style>{`@keyframes cc-bar-grow { from { width: 0; } }`}</style>
    </div>
  );
}
window.CcBars = CcBars;

// ---------- Pillar (BlynkAudit) ----------
function CcPillars({ pillars }) {
  const order = ['technical', 'agentic', 'market', 'sentiment'];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {order.map(p => {
        const v = pillars[p] || 0;
        const level = v >= 78 ? 'high' : v >= 56 ? 'mid' : v >= 32 ? 'low' : 'bad';
        const color = level === 'high' ? 'var(--color-pos)' :
                      level === 'mid' ? 'var(--color-accent)' :
                      level === 'low' ? 'var(--color-warn)' : 'var(--color-neg)';
        return (
          <div className="cc-pillar" key={p}>
            <span className="cc-pillar-name">{p}</span>
            <div className="cc-pillar-bar">
              <div className="cc-pillar-fill" style={{ width: v + '%', background: color, boxShadow: `0 0 6px ${color}` }} />
            </div>
            <span className="cc-pillar-val">{v}</span>
          </div>
        );
      })}
    </div>
  );
}
window.CcPillars = CcPillars;

// ---------- Ticker (bottom rolling strip) ----------
function CcTicker({ items }) {
  // duplicate for seamless loop
  const doubled = [...items, ...items];
  return (
    <div className="cc-ticker">
      <div className="cc-ticker-label">
        <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--color-accent)', boxShadow: '0 0 4px var(--color-accent-glow)' }}></span>
        FEED
      </div>
      <div className="cc-ticker-track">
        <div className="cc-ticker-content">
          {doubled.map((it, i) => (
            <span key={i} className="cc-ticker-item">
              <span style={{ color: 'var(--color-subtle)', fontSize: 10, letterSpacing: '1px', textTransform: 'uppercase' }}>{it.label}</span>
              <span style={{ color: 'var(--color-fg)', fontWeight: 500 }}>{it.value}</span>
              {it.delta && (
                <span style={{
                  color: it.dir === 'pos' ? 'var(--color-pos)' :
                         it.dir === 'neg' ? 'var(--color-neg)' :
                         it.dir === 'warn' ? 'var(--color-warn)' :
                         'var(--color-subtle)',
                  fontSize: 11,
                }}>
                  {it.dir === 'pos' ? '▲' : it.dir === 'neg' ? '▼' : '·'} {it.delta}
                </span>
              )}
              <span className="cc-ticker-sep">·</span>
            </span>
          ))}
        </div>
      </div>
    </div>
  );
}
window.CcTicker = CcTicker;

// ---------- Funnel ----------
function CcFunnel({ stages, color = 'var(--rl-accent)' }) {
  const max = stages[0].value;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      {stages.map((s, i) => {
        const pct = (s.value / max) * 100;
        const conv = i > 0 ? ((s.value / stages[i - 1].value) * 100) : 100;
        return (
          <div key={i} style={{ display: 'grid', gridTemplateColumns: '110px 1fr 80px', alignItems: 'center', gap: 10 }}>
            <span style={{ fontSize: 11, color: 'var(--color-muted)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.8px' }}>
              {s.stage}
            </span>
            <div style={{ background: 'var(--color-bar-bg)', borderRadius: 4, height: 22, overflow: 'hidden', position: 'relative' }}>
              <div style={{
                width: pct + '%',
                height: '100%',
                background: color,
                opacity: 0.75 - i * 0.08,
                animation: `cc-bar-grow 900ms cubic-bezier(0.2, 0.8, 0.2, 1) ${i * 100}ms both`,
                display: 'flex', alignItems: 'center', paddingLeft: 8,
                fontFamily: 'var(--font-mono)', fontSize: 11, fontWeight: 600, color: 'var(--color-bg)'
              }}>
                {fmtNum(s.value)}
              </div>
            </div>
            <span style={{ fontSize: 11, color: i === 0 ? 'var(--color-subtle)' : 'var(--color-muted)', fontFamily: 'var(--font-mono)', textAlign: 'right' }}>
              {i === 0 ? '—' : conv.toFixed(1) + '%'}
            </span>
          </div>
        );
      })}
    </div>
  );
}
window.CcFunnel = CcFunnel;

// ---------- Pipeline tile ----------
function CcPipelineCard({ items }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      {items.map((p, i) => (
        <div key={i} className="cc-card" data-tint="fp" style={{ padding: '12px 14px', gap: 4, borderRadius: 6 }}>
          <div className="cc-row" style={{ justifyContent: 'space-between' }}>
            <span style={{ fontWeight: 600, fontSize: 13 }}>{p.name}</span>
            <span className="cc-chip fp">{p.stage}</span>
          </div>
          <div className="cc-row" style={{ justifyContent: 'space-between', color: 'var(--color-subtle)', fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '1px', textTransform: 'uppercase' }}>
            <span>{p.kind}</span>
            <span>{p.eta}</span>
          </div>
        </div>
      ))}
    </div>
  );
}
window.CcPipelineCard = CcPipelineCard;

// ---------- Rotator indicator ----------
function CcRotator({ tabs, active, progress }) {
  return (
    <div className="cc-rotator">
      {tabs.map((t, i) => (
        <span key={t} className={'cc-rotator-tab' + (i === active ? ' active' : '')}>
          {t}
          {i === active && <span className="cc-rotator-progress" style={{ width: (progress * 100) + '%' }}></span>}
        </span>
      ))}
    </div>
  );
}
window.CcRotator = CcRotator;

// ---------- Stat row (compact KPI for inside cards) ----------
function CcStat({ label, value, sub, delta, deltaDir = 'pos' }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
      <span className="cc-label-sm">{label}</span>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 22, fontWeight: 600, letterSpacing: '-0.5px', color: 'var(--color-fg)', lineHeight: 1.1, fontVariantNumeric: 'tabular-nums' }}>
        {value}
      </div>
      {(sub || delta) && (
        <div style={{ fontSize: 11, color: 'var(--color-muted)', fontFamily: 'var(--font-mono)', display: 'flex', gap: 6 }}>
          {sub && <span>{sub}</span>}
          {delta && <span className={'cc-kpi-delta ' + deltaDir}>{delta}</span>}
        </div>
      )}
    </div>
  );
}
window.CcStat = CcStat;

// Export
Object.assign(window, {
  fmtNum, fmtTimeAgo, useCountUp,
});
