// ============================================================================
// Principia White Paper — Chart library (part 1: shared utils + crisis charts)
// Hand-built SVG/CSS data visualizations. On-brand: navy / blue / accent,
// restrained semantic accents (danger for decline, success for gains).
// Exports to window for use by wp-sections.jsx + whitepaper-app.jsx.
// ============================================================================

// ───── Data-viz palette ─────
const C_INK     = '#0A1F3D';
const C_BLUE    = '#1C4A8F';
const C_ACCENT  = '#3B7BC9';
const C_ACCENT2 = '#7FB0E6';
const C_DANGER  = '#B83227';
const C_DANGER2 = '#D98379';
const C_SUCCESS = '#1F7A4D';
const C_MUTED   = '#5B6B85';
const C_RULE    = 'rgba(10,31,61,0.10)';
const C_RULE_STRONG = 'rgba(10,31,61,0.20)';
const C_PAPER   = '#F4F6FB';
const C_SURF2   = '#F8FAFC';
const FONT_MONO = "'JetBrains Mono', ui-monospace, monospace";
const FONT_SANS = "'Source Sans 3', system-ui, sans-serif";
const FONT_DISP = "'Fraunces', Georgia, serif";
const EASE      = 'cubic-bezier(0.22, 1, 0.36, 1)';

// ───── Reveal hook — fires once when element scrolls into view ─────
function useReveal(threshold = 0.22) {
  const ref = React.useRef(null);
  const [seen, setSeen] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce || window.__WP_STATIC__) { setSeen(true); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } });
    }, { threshold });
    io.observe(el);
    return () => io.disconnect();
  }, [threshold]);
  return [ref, seen];
}

// ───── Count-up hook ─────
function useCountUp(target, run, { duration = 1100, decimals = 0 } = {}) {
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    if (!run) return;
    let raf, start;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce || window.__WP_STATIC__) { setVal(target); return; }
    const tick = (t) => {
      if (start == null) start = t;
      const p = Math.min((t - start) / duration, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(target * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
      else setVal(target);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [run, target, duration]);
  const f = decimals > 0 ? val.toFixed(decimals) : Math.round(val).toString();
  return f;
}

// ───── Viewport hook — returns true when width ≤ breakpoint ─────
function useIsMobile(bp = 640) {
  const [mobile, setMobile] = React.useState(() => window.innerWidth <= bp);
  React.useEffect(() => {
    const mq = window.matchMedia(`(max-width: ${bp}px)`);
    const onChange = (e) => setMobile(e.matches);
    mq.addEventListener('change', onChange);
    setMobile(mq.matches);
    return () => mq.removeEventListener('change', onChange);
  }, [bp]);
  return mobile;
}

// ───── Figure wrapper — figure number, title, the chart, source caption ─────
function Figure({ n, title, sub, source, children, tint, style = {} }) {
  return (
    <figure style={{
      margin: '0 0 0 0',
      background: tint || '#fff',
      border: `1px solid ${C_RULE}`,
      borderRadius: 16,
      padding: 'clamp(20px, 4vw, 34px) clamp(16px, 3.5vw, 38px) clamp(18px, 3vw, 30px)',
      boxShadow: '0 1px 2px rgba(10,31,61,0.05), 0 10px 30px rgba(10,31,61,0.05)',
      ...style,
    }}>
      <figcaption style={{ marginBottom: 26 }}>
        <div style={{
          fontFamily: FONT_MONO, fontSize: 10.5, letterSpacing: '0.18em',
          textTransform: 'uppercase', color: C_ACCENT, fontWeight: 600,
        }}>FIG. {n}</div>
        <div style={{
          fontFamily: FONT_SANS, fontSize: 19, fontWeight: 600, color: C_INK,
          marginTop: 8, letterSpacing: '-0.01em', lineHeight: 1.25,
        }}>{title}</div>
        {sub && <div style={{
          fontFamily: FONT_SANS, fontSize: 14.5, color: C_MUTED, marginTop: 6,
          lineHeight: 1.5, maxWidth: 640,
        }}>{sub}</div>}
      </figcaption>
      <div className="wp-figbody">{children}</div>
      {source && <div style={{
        fontFamily: FONT_MONO, fontSize: 9.5, letterSpacing: '0.12em',
        textTransform: 'uppercase', color: C_MUTED, marginTop: 24,
        paddingTop: 14, borderTop: `1px solid ${C_RULE}`, opacity: 0.85,
      }}>{source}</div>}
    </figure>
  );
}

// ───── Horizontal bar row (shared) ─────
function HBar({ label, note, value, display, max, color, track, seen, delay = 0, height = 34, signed }) {
  const pct = Math.min(Math.abs(value) / max, 1) * 100;
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'min(168px, 30vw) 1fr', gap: 'clamp(10px, 2vw, 18px)', alignItems: 'center' }}>
      <div style={{ textAlign: 'right' }}>
        <div style={{ fontFamily: FONT_SANS, fontSize: 'clamp(13px, 2vw, 15px)', fontWeight: 600, color: C_INK }}>{label}</div>
        {note && <div style={{ fontFamily: FONT_MONO, fontSize: 9, letterSpacing: '0.1em', color: C_MUTED, textTransform: 'uppercase', marginTop: 2 }}>{note}</div>}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 'clamp(8px, 1.5vw, 14px)' }}>
        <div style={{ flex: 1, height, background: track || C_SURF2, borderRadius: 6, position: 'relative', overflow: 'hidden' }}>
          <div style={{
            position: 'absolute', top: 0, left: 0, bottom: 0,
            width: seen ? pct + '%' : '0%',
            background: color, borderRadius: 6,
            transition: `width 1.05s ${EASE} ${delay}s`,
          }} />
        </div>
        <div style={{ minWidth: 64, fontFamily: FONT_MONO, fontWeight: 600, fontSize: 16, color, letterSpacing: '0.02em' }}>{display}</div>
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════
// FIG — Correspondent banking decline by region (horizontal bars, decline)
// ════════════════════════════════════════════════════════════════════════
function RegionalDeclineChart() {
  const [ref, seen] = useReveal();
  const rows = [
    { label: 'Global', value: -22, display: '−22%', color: C_DANGER2 },
    { label: 'Latin America', value: -34, display: '−34%', color: '#C95B4E' },
    { label: 'Caribbean', value: -41, display: '−41%', color: C_DANGER },
    { label: 'Sub-Saharan Africa', note: 'mirrors Caribbean', value: -41, display: '≈−41%', color: C_DANGER },
  ];
  return (
    <div ref={ref} style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      {rows.map((r, i) => (
        <HBar key={r.label} {...r} max={45} track={'rgba(184,50,39,0.07)'} seen={seen} delay={i * 0.12} />
      ))}
      <div style={{ display: 'grid', gridTemplateColumns: 'min(168px, 30vw) 1fr', gap: 'clamp(10px, 2vw, 18px)', marginTop: 2 }}>
        <div />
        <div style={{ fontFamily: FONT_MONO, fontSize: 9.5, letterSpacing: '0.14em', color: C_MUTED, textTransform: 'uppercase' }}>
          Decline in active correspondent relationships · since 2011
        </div>
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════
// FIG — 5 of 192 UK banks willing to safeguard (unit / dot grid)
// ════════════════════════════════════════════════════════════════════════
function BankAccessUnitChart() {
  const [ref, seen] = useReveal(0.15);
  const total = 192, hit = 5, cols = 24;
  const cells = Array.from({ length: total });
  return (
    <div ref={ref}>
      <div style={{ display: 'flex', gap: 36, flexWrap: 'wrap', alignItems: 'flex-start' }}>
        <div style={{
          display: 'grid',
          gridTemplateColumns: `repeat(${cols}, 1fr)`,
          gap: 5, flex: 1, minWidth: 360,
        }}>
          {cells.map((_, i) => {
            const on = i < hit;
            return (
              <div key={i} style={{
                aspectRatio: '1 / 1',
                borderRadius: 2.5,
                background: on ? C_INK : 'rgba(10,31,61,0.08)',
                outline: on ? `1.5px solid ${C_ACCENT}` : 'none',
                outlineOffset: on ? 1.5 : 0,
                opacity: seen ? 1 : 0,
                transform: seen ? 'scale(1)' : 'scale(0.4)',
                transition: `opacity .5s ${EASE} ${(on ? 0.05 : 0.4 + i * 0.0016)}s, transform .5s ${EASE} ${(on ? 0.05 : 0.4 + i * 0.0016)}s`,
              }} />
            );
          })}
        </div>
        <div style={{ width: 'min(196px, 100%)', flexShrink: 0 }}>
          <div style={{ fontFamily: FONT_DISP, fontSize: 64, lineHeight: 0.95, color: C_INK, letterSpacing: '-0.03em' }}>
            5<span style={{ color: C_MUTED, fontSize: 34 }}> / 192</span>
          </div>
          <div style={{ fontFamily: FONT_SANS, fontSize: 14.5, color: C_MUTED, marginTop: 12, lineHeight: 1.55 }}>
            UK banks willing to hold safeguarding for the entire payment sector.
          </div>
          <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 7 }}>
            {['ClearBank', 'Griffin', 'Banking Circle', 'LHV', 'Starling'].map((b) => (
              <div key={b} style={{ display: 'flex', alignItems: 'center', gap: 9, fontFamily: FONT_MONO, fontSize: 11, letterSpacing: '0.06em', color: C_INK }}>
                <span style={{ width: 8, height: 8, background: C_INK, borderRadius: 2, outline: `1.5px solid ${C_ACCENT}`, outlineOffset: 1 }} />{b}
              </div>
            ))}
          </div>
          <div style={{ fontFamily: FONT_MONO, fontSize: 9.5, letterSpacing: '0.1em', color: C_MUTED, marginTop: 14, textTransform: 'uppercase' }}>
            None settle to Africa via SWIFT
          </div>
        </div>
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════
// FIG — Donut: 65% shortfall on EMI insolvency (≈35p recovered on the pound)
// ════════════════════════════════════════════════════════════════════════
function ShortfallDonut() {
  const [ref, seen] = useReveal();
  const R = 78, SW = 26, C = 2 * Math.PI * R;
  const lostFrac = 0.65;
  const lostLen = C * lostFrac;
  const big = useCountUp(65, seen, { duration: 1100 });
  return (
    <div ref={ref} style={{ display: 'flex', gap: 'clamp(20px, 4vw, 44px)', alignItems: 'center', flexWrap: 'wrap' }}>
      <div style={{ position: 'relative', width: 'min(210px, 45vw)', height: 'min(210px, 45vw)', flexShrink: 0 }}>
        <svg viewBox="0 0 210 210" style={{ width: '100%', height: '100%', transform: 'rotate(-90deg)' }}>
          <circle cx="105" cy="105" r={R} fill="none" stroke={C_INK} strokeWidth={SW} opacity="0.16" />
          <circle cx="105" cy="105" r={R} fill="none" stroke={C_DANGER} strokeWidth={SW}
            strokeLinecap="butt"
            strokeDasharray={`${lostLen} ${C - lostLen}`}
            strokeDashoffset={seen ? 0 : lostLen}
            style={{ transition: `stroke-dashoffset 1.3s ${EASE} 0.1s` }} />
        </svg>
        <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ fontFamily: FONT_DISP, fontSize: 52, color: C_DANGER, lineHeight: 1, letterSpacing: '-0.03em' }}>{big}%</div>
          <div style={{ fontFamily: FONT_MONO, fontSize: 9.5, letterSpacing: '0.14em', color: C_MUTED, textTransform: 'uppercase', marginTop: 4 }}>shortfall</div>
        </div>
      </div>
      <div style={{ flex: 1, minWidth: 240 }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <LegendRow swatch={C_DANGER} label="Customer funds lost" value="≈ 65p" sub="per £1 safeguarded" />
          <LegendRow swatch={'rgba(10,31,61,0.16)'} label="Recovered on the pound" value="≈ 35p" sub="average across 12 insolvencies" />
        </div>
        <div style={{ marginTop: 20, paddingTop: 16, borderTop: `1px solid ${C_RULE}`, display: 'flex', gap: 30 }}>
          <MiniStat v="12" l="UK EMIs insolvent 2018–2023" />
          <MiniStat v="No" l="statutory trust over funds" />
        </div>
      </div>
    </div>
  );
}

function LegendRow({ swatch, label, value, sub }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
      <span style={{ width: 14, height: 14, borderRadius: 3, background: swatch, flexShrink: 0 }} />
      <div style={{ flex: 1 }}>
        <div style={{ fontFamily: FONT_SANS, fontSize: 15, fontWeight: 600, color: C_INK }}>{label}</div>
        {sub && <div style={{ fontFamily: FONT_SANS, fontSize: 13, color: C_MUTED }}>{sub}</div>}
      </div>
      <div style={{ fontFamily: FONT_MONO, fontSize: 18, fontWeight: 600, color: C_INK }}>{value}</div>
    </div>
  );
}
function MiniStat({ v, l }) {
  return (
    <div>
      <div style={{ fontFamily: FONT_DISP, fontSize: 30, color: C_INK, lineHeight: 1, letterSpacing: '-0.02em' }}>{v}</div>
      <div style={{ fontFamily: FONT_SANS, fontSize: 12.5, color: C_MUTED, marginTop: 5, lineHeight: 1.4, maxWidth: 150 }}>{l}</div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════
// FIG — Remittance cost: 8.78% today vs <3% SDG target vs our model
// ════════════════════════════════════════════════════════════════════════
function RemittanceCostChart() {
  const [ref, seen] = useReveal();
  const max = 10;
  const bars = [
    { label: 'Send $200 to SSA', sub: 'today, global average', value: 8.78, display: '8.78%', color: C_DANGER },
    { label: 'Typical MTO', sub: 'all-in to customer', value: 8.0, display: '8.00%', color: C_DANGER2 },
    { label: 'On our rails', sub: 'settlement + payout', value: 2.65, display: '<3%', color: C_SUCCESS },
  ];
  const threshold = 3;
  return (
    <div ref={ref}>
      <div style={{ display: 'flex', gap: 30, alignItems: 'flex-end', height: 250, position: 'relative', padding: '0 8px' }}>
        {/* SDG threshold line */}
        <div style={{
          position: 'absolute', left: 0, right: 0,
          bottom: (threshold / max) * 250,
          borderTop: `2px dashed ${C_SUCCESS}`, opacity: seen ? 0.7 : 0,
          transition: `opacity .6s ${EASE} .9s`,
        }}>
          <span style={{ position: 'absolute', right: 0, top: -20, fontFamily: FONT_MONO, fontSize: 10, letterSpacing: '0.12em', color: C_SUCCESS, textTransform: 'uppercase', background: '#fff', padding: '0 6px' }}>
            SDG 10.c target · &lt;3%
          </span>
        </div>
        {bars.map((b, i) => (
          <div key={b.label} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-end', height: '100%' }}>
            <div style={{ fontFamily: FONT_MONO, fontSize: 18, fontWeight: 600, color: b.color, marginBottom: 8, opacity: seen ? 1 : 0, transition: `opacity .5s ${EASE} ${0.5 + i * 0.12}s` }}>{b.display}</div>
            <div style={{
              width: '74%', maxWidth: 130,
              height: seen ? (b.value / max) * 250 : 0,
              background: `linear-gradient(180deg, ${b.color}, ${b.color}cc)`,
              borderRadius: '6px 6px 0 0',
              transition: `height 1.05s ${EASE} ${i * 0.12}s`,
            }} />
          </div>
        ))}
      </div>
      <div style={{ display: 'flex', gap: 30, marginTop: 14, padding: '0 8px', borderTop: `1px solid ${C_RULE}`, paddingTop: 14 }}>
        {bars.map((b) => (
          <div key={b.label} style={{ flex: 1, textAlign: 'center' }}>
            <div style={{ fontFamily: FONT_SANS, fontSize: 14, fontWeight: 600, color: C_INK }}>{b.label}</div>
            <div style={{ fontFamily: FONT_SANS, fontSize: 12.5, color: C_MUTED, marginTop: 2 }}>{b.sub}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════
// FIG — Cost per transaction: intermediary hops vs direct rail (log scale)
// ════════════════════════════════════════════════════════════════════════
function CostScaleChart() {
  const [ref, seen] = useReveal();
  // log scale from €0.002 to €35
  const lo = 0.002, hi = 35;
  const logPos = (v) => (Math.log10(v) - Math.log10(lo)) / (Math.log10(hi) - Math.log10(lo)) * 100;
  const ticks = [0.002, 0.02, 0.2, 2, 35];
  const items = [
    { label: 'Direct rail (TIPS)', range: [0.002, 0.49], display: '€0.002–£0.49', color: C_SUCCESS, side: 'our' },
    { label: 'Intermediary hop', range: [15, 35], display: '$15–35 × 1–3 hops', color: C_DANGER, side: 'today' },
  ];
  return (
    <div ref={ref}>
      <div style={{ position: 'relative', padding: '8px 0 0' }}>
        {items.map((it, i) => {
          const x1 = logPos(it.range[0]), x2 = logPos(it.range[1]);
          return (
            <div key={it.label} style={{ marginBottom: 26 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
                <span style={{ fontFamily: FONT_SANS, fontSize: 15, fontWeight: 600, color: C_INK }}>{it.label}</span>
                <span style={{ fontFamily: FONT_MONO, fontSize: 14, fontWeight: 600, color: it.color }}>{it.display}</span>
              </div>
              <div style={{ position: 'relative', height: 18 }}>
                <div style={{ position: 'absolute', top: 7, left: 0, right: 0, height: 2, background: C_RULE }} />
                <div style={{
                  position: 'absolute', top: 4, height: 8, borderRadius: 4,
                  left: x1 + '%',
                  width: seen ? (x2 - x1) + '%' : 0,
                  minWidth: 8,
                  background: it.color,
                  transition: `width 1s ${EASE} ${0.2 + i * 0.15}s`,
                }} />
                <div style={{
                  position: 'absolute', top: 0, left: `calc(${x1}% - 1px)`, width: 2, height: 16, background: it.color,
                  opacity: seen ? 1 : 0, transition: `opacity .5s ${EASE} ${0.2 + i * 0.15}s`,
                }} />
              </div>
            </div>
          );
        })}
        {/* axis */}
        <div style={{ position: 'relative', height: 28, marginTop: 6, borderTop: `1px solid ${C_RULE}` }}>
          {ticks.map((t) => (
            <div key={t} style={{ position: 'absolute', left: logPos(t) + '%', transform: 'translateX(-50%)', top: 6, fontFamily: FONT_MONO, fontSize: 9.5, color: C_MUTED, letterSpacing: '0.06em' }}>
              {t < 1 ? '€' + t : '$' + t}
            </div>
          ))}
        </div>
        <div style={{ textAlign: 'center', fontFamily: FONT_MONO, fontSize: 9.5, letterSpacing: '0.14em', color: C_MUTED, textTransform: 'uppercase', marginTop: 4 }}>
          cost per transaction · logarithmic scale
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  C_INK, C_BLUE, C_ACCENT, C_ACCENT2, C_DANGER, C_DANGER2, C_SUCCESS, C_MUTED,
  C_RULE, C_RULE_STRONG, C_PAPER, C_SURF2, FONT_MONO, FONT_SANS, FONT_DISP, EASE,
  useReveal, useCountUp, useIsMobile, Figure, HBar, LegendRow, MiniStat,
  RegionalDeclineChart, BankAccessUnitChart, ShortfallDonut, RemittanceCostChart, CostScaleChart,
});
