/* Tempos.jsx — one event per row design. No collisions possible. */

const TEMPO_TRACKS = [
  { id: 'physical',  label: 'Physical supply',     accent: '#111',     speed: 'days',     col: 0 },
  { id: 'community', label: 'Community / lateral', accent: '#c4881f',  speed: 'hours',    col: 1 },
  { id: 'financial', label: 'Financial-instrument',accent: '#1a365d',  speed: 'quarters', col: 2 },
  { id: 'govt',      label: 'Government policy',   accent: '#8b1a1a',  speed: 'months',   col: 3 },
];

const ALL_EVENTS = [
  // physical
  { trackId:'physical', t: 1,   label: 'Hormuz close threat',         kind: 'shock' },
  { trackId:'physical', t: 2,   label: 'JWC zone reclassed',          kind: 'event' },
  { trackId:'physical', t: 4,   label: 'Tanker rerouted via Cape',    kind: 'event' },
  { trackId:'physical', t: 12,  label: 'Terminal stock −18%',         kind: 'event' },
  { trackId:'physical', t: 16,  label: 'Last pre-closure cargo',      kind: 'milestone' },
  { trackId:'physical', t: 24,  label: 'Sub-cover threshold (28 d)',  kind: 'crit' },
  { trackId:'physical', t: 38,  label: 'Replenishment cargo',         kind: 'recover' },
  // community
  { trackId:'community', t: 1.5, label: 'Marae cluster activate',     kind: 'event' },
  { trackId:'community', t: 3,   label: 'Iwi fuel pooling',           kind: 'event' },
  { trackId:'community', t: 6,   label: 'Rural ride-share grids',     kind: 'event' },
  { trackId:'community', t: 14,  label: 'Volunteer logistics formalises', kind: 'event' },
  // financial
  { trackId:'financial', t: 1,   label: 'War-risk surcharge spike',   kind: 'event' },
  { trackId:'financial', t: 5,   label: 'Marine cargo reprice',       kind: 'event' },
  { trackId:'financial', t: 14,  label: 'Forward curve inversion',    kind: 'event' },
  { trackId:'financial', t: 30,  label: 'Trade credit calls open',    kind: 'event' },
  { trackId:'financial', t: 90,  label: 'BI policy renewals reprice', kind: 'milestone' },
  { trackId:'financial', t: 180, label: 'Reinsurance treaty roll',    kind: 'milestone' },
  { trackId:'financial', t: 270, label: 'Capital adequacy retest',    kind: 'event' },
  // govt
  { trackId:'govt', t: 4,   label: 'Ministerial statement',     kind: 'event' },
  { trackId:'govt', t: 12,  label: 'Cabinet paper drafted',     kind: 'event' },
  { trackId:'govt', t: 60,  label: 'Select committee referred', kind: 'event' },
  { trackId:'govt', t: 180, label: 'First reading',             kind: 'milestone' },
  { trackId:'govt', t: 365, label: 'Royal assent (optimistic)', kind: 'milestone' },
];

const TIME_DOMAIN = 365;

function fmtElapsed(t) {
  if (t < 7)   return `${t.toFixed(t < 1.5 ? 1 : 0)} day${t < 1.5 ? '' : 's'}`;
  if (t < 30)  return `${(t/7).toFixed(1)} weeks`;
  if (t < 365) return `${(t/30).toFixed(1)} months`;
  return `${(t/365).toFixed(1)} years`;
}
function fmtShort(t) {
  if (t < 1.5) return `${t}d`;
  if (t < 7)   return `d${Math.round(t)}`;
  if (t < 30)  return `wk${Math.round(t/7)}`;
  if (t < 365) return `mo${Math.round(t/30)}`;
  return `yr${(t/365).toFixed(1)}`;
}

function Tempos() {
  const tw = React.useContext(window.TweakCtx);
  const NS = tw.nodeScale, LS = tw.labelScale, AS = tw.axisLabelScale;
  const [day, setDay] = React.useState(18);
  const [hover, setHover] = React.useState(null);

  // sort all events strictly by time -> defines row order; one row per event = no overlap possible.
  const ROWS = React.useMemo(() => {
    const arr = ALL_EVENTS.map(e => ({ ...e, track: TEMPO_TRACKS.find(t => t.id === e.trackId) }));
    return arr.sort((a, b) => a.t - b.t || a.track.col - b.track.col);
  }, []);

  // chart geometry
  const W = 1100;
  const ROW_H = 36;
  const padT = 78;
  const padB = 40;
  const H = padT + ROWS.length * ROW_H + padB;

  // 4 lane x positions (centers) — pushed apart for breathable headers
  const laneStart = 320;
  const laneEnd   = W - 290;
  const laneXs = TEMPO_TRACKS.map((_, i) =>
    laneStart + (i / (TEMPO_TRACKS.length - 1)) * (laneEnd - laneStart));

  // currentRowIdx = first row whose t > day (events at-or-before day are revealed)
  const currentRowIdx = ROWS.findIndex(r => r.t > day);
  const revealedCount = currentRowIdx === -1 ? ROWS.length : currentRowIdx;
  // NOW line y = between revealed and next, or just below the last revealed row
  const nowY = padT + (revealedCount === 0 ? 0
              : revealedCount === ROWS.length ? ROWS.length * ROW_H
              : revealedCount * ROW_H);

  // running counts per track at scrub
  const revealedByTrack = TEMPO_TRACKS.map(tr =>
    ALL_EVENTS.filter(e => e.trackId === tr.id && e.t <= day).length);
  const totalsByTrack = TEMPO_TRACKS.map(tr =>
    ALL_EVENTS.filter(e => e.trackId === tr.id).length);

  const physRev = revealedByTrack[0];
  const govRev  = revealedByTrack[3];
  const gapMultiple = govRev === 0 ? '∞' : (physRev / Math.max(1, govRev)).toFixed(1) + '×';

  // SVG row click: jump scrubber to that row's t
  const onRowClick = (ev) => setDay(clamp(ev.t, 1, TIME_DOMAIN));

  return (
    <section id="tempos">
      <div className="container">
        <SectionHeader
          num="01 / 06"
          tag="What was observed"
          title="Three tempos, not two."
          lede={`The article framed it as a two-tempo gap. Reading down a year of events shows there are three — and the financial-instrument tempo is the silent one. It absorbs shocks the government cannot move fast enough to absorb. That is why pump prices can fall during a supply crisis.`}
        />

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: 32, alignItems: 'start' }}>
          <div className="card flat" style={{ padding: '24px 28px 20px' }}>
            {/* Header strip */}
            <div style={{
              display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
              gap: 24, marginBottom: 18, flexWrap: 'wrap',
            }}>
              <div style={{ minWidth: 240 }}>
                <div style={{ fontSize: 11, letterSpacing: '.16em', textTransform: 'uppercase', color: 'var(--muted-2)', fontWeight: 600 }}>
                  Time elapsed since Hormuz disruption
                </div>
                <div style={{ fontFamily: 'Source Serif 4', fontSize: 32, letterSpacing: '-.02em', marginTop: 8, lineHeight: 1.1 }}>
                  {fmtElapsed(day)}
                </div>
                <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 6 }}>
                  {revealedCount} of {ROWS.length} events revealed
                </div>
              </div>
              <div className="flex-row">
                {[1, 7, 30, 90, 180, 365].map(t => (
                  <button key={t} className="chip"
                          onClick={() => setDay(t)}
                          style={Math.abs(day - t) < 0.5 ? { background: '#111', color: '#fff', borderColor: '#111' } : {}}>
                    {t === 1 ? '1 day' : t === 7 ? '1 week' : t === 30 ? '1 month'
                      : t === 90 ? '1 quarter' : t === 180 ? '6 months' : '1 year'}
                  </button>
                ))}
              </div>
            </div>

            <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
              {/* lane headers */}
              {TEMPO_TRACKS.map((tr, i) => {
                const words = tr.label.split(/[\s/]+/);
                return (
                  <g key={tr.id}>
                    <text x={laneXs[i]} y={20} fontSize={11.5*AS} fontWeight="700"
                          fill={tr.accent} textAnchor="middle"
                          style={{ textTransform: 'uppercase', letterSpacing: '.06em' }}>
                      {words[0]}
                    </text>
                    {words.length > 1 && (
                      <text x={laneXs[i]} y={36} fontSize={11.5*AS} fontWeight="700"
                            fill={tr.accent} textAnchor="middle"
                            style={{ textTransform: 'uppercase', letterSpacing: '.06em' }}>
                        {words.slice(1).join(' ')}
                      </text>
                    )}
                    <text x={laneXs[i]} y={56} fontSize={9.5*AS}
                          fill="#8a8a8a" textAnchor="middle"
                          style={{ textTransform: 'uppercase', letterSpacing: '.08em' }}>
                      {tr.speed}
                    </text>
                    {/* lane spine — full height of timeline area */}
                    <line x1={laneXs[i]} y1={padT} x2={laneXs[i]} y2={padT + ROWS.length * ROW_H}
                          stroke={tr.accent} strokeWidth="1" opacity="0.12" />
                  </g>
                );
              })}

              {/* column labels — left (time) and right (event) */}
              <text x={140} y={56} fontSize={9.5*AS} fill="#8a8a8a" textAnchor="end"
                    style={{ textTransform: 'uppercase', letterSpacing: '.1em' }}>
                Time ↓
              </text>
              <text x={W - 30} y={56} fontSize={9.5*AS} fill="#8a8a8a" textAnchor="end"
                    style={{ textTransform: 'uppercase', letterSpacing: '.1em' }}>
                Event
              </text>

              {/* rows */}
              {ROWS.map((ev, idx) => {
                const yRow = padT + idx * ROW_H + ROW_H / 2;
                const passed = day >= ev.t;
                const isHover = hover === idx;
                const x = laneXs[ev.track.col];
                const r = (ev.kind === 'milestone' ? 6.5
                        : ev.kind === 'crit' ? 7
                        : ev.kind === 'shock' ? 7.5
                        : ev.kind === 'recover' ? 5.5
                        : 5) * NS;

                return (
                  <g key={idx}
                     onMouseEnter={() => setHover(idx)}
                     onMouseLeave={() => setHover(null)}
                     onClick={() => onRowClick(ev)}
                     style={{ cursor: 'pointer' }}>
                    {/* row hover band */}
                    {isHover && (
                      <rect x={20} y={yRow - ROW_H/2} width={W - 40} height={ROW_H}
                            fill="rgba(0,0,0,.04)" />
                    )}

                    {/* time labels — short on left, long on right of left-block */}
                    <text x={140} y={yRow + 4} fontSize={11*AS}
                          fill={passed ? 'var(--ink)' : '#bdb6a4'}
                          textAnchor="end" fontFamily="JetBrains Mono, monospace"
                          fontWeight={passed ? 600 : 400}>
                      {fmtShort(ev.t)}
                    </text>
                    <text x={156} y={yRow + 4} fontSize={10.5*AS}
                          fill={passed ? '#7a7568' : '#cbc5b3'}
                          textAnchor="start">
                      {ev.t < 7 ? `day ${ev.t.toFixed(ev.t < 1.5 ? 1 : 0)}`
                        : ev.t < 30 ? `wk ${(ev.t/7).toFixed(1)}`
                        : ev.t < 365 ? `mo ${(ev.t/30).toFixed(1)}`
                        : 'yr 1.0'}
                    </text>

                    {/* leader line from time block to dot */}
                    <line x1={250} y1={yRow} x2={x - r - 4} y2={yRow}
                          stroke={ev.track.accent}
                          strokeWidth="1"
                          opacity={passed ? 0.25 : 0.08}
                          strokeDasharray="2 3" />

                    {/* dot */}
                    {ev.kind === 'crit' && passed && (
                      <circle cx={x} cy={yRow} r={16*NS} fill={ev.track.accent} opacity="0.14" />
                    )}
                    {ev.kind === 'shock' && passed && (
                      <circle cx={x} cy={yRow} r={12*NS} fill="none"
                              stroke={ev.track.accent} strokeWidth="1" opacity="0.4" />
                    )}
                    <circle cx={x} cy={yRow} r={r}
                            fill={passed ? ev.track.accent : 'var(--bg-card)'}
                            stroke={ev.track.accent}
                            strokeWidth={passed ? 0 : 1.5} />
                    {ev.kind === 'milestone' && passed && (
                      <circle cx={x} cy={yRow} r={2.5*NS} fill="#fff" />
                    )}

                    {/* event label — far right */}
                    <text x={W - 30} y={yRow + 4} fontSize={12.5*LS}
                          fontWeight={passed ? 600 : 400}
                          fill={passed ? 'var(--ink)' : '#a8a195'}
                          textAnchor="end"
                          opacity={isHover ? 1 : (passed ? 0.95 : 0.65)}>
                      {ev.label}
                    </text>
                  </g>
                );
              })}

              {/* NOW line — horizontal sweep */}
              <g style={{ pointerEvents: 'none' }}>
                <line x1={20} y1={nowY} x2={W - 20} y2={nowY}
                      stroke="#111" strokeWidth="1.5" />
                <rect x={W - 78} y={nowY - 11} width={48} height={22}
                      fill="#111" rx="2" />
                <text x={W - 54} y={nowY + 4} fontSize={11*AS} fill="#fff"
                      textAnchor="middle" fontWeight="700"
                      letterSpacing=".08em">NOW</text>
              </g>
            </svg>

            <div className="aside" style={{ marginTop: 14, textAlign: 'center' }}>
              Click any event to jump the scrubber there. Each row is one event,
              ordered by time — read down to see how Physical and Community fully populate
              before Government posts its first event.
            </div>

            <div style={{ marginTop: 12 }}>
              <input type="range" min={1} max={365} step={0.5} value={day}
                     onChange={(e) => setDay(parseFloat(e.target.value))}
                     className="slider" style={{ width: '100%' }} />
            </div>
          </div>

          {/* sidebar */}
          <div className="flex-col">
            <div className="card flat card-pad-sm">
              <div className="bignum-sub" style={{ marginBottom: 8 }}>EVENTS REVEALED</div>
              {TEMPO_TRACKS.map((tr, i) => (
                <div key={tr.id} style={{
                  display: 'flex', justifyContent: 'space-between',
                  padding: '6px 0', borderBottom: i < 3 ? '1px solid var(--rule)' : 'none',
                  fontSize: 13,
                }}>
                  <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ width: 8, height: 8, borderRadius: '50%', background: tr.accent }} />
                    {tr.label}
                  </span>
                  <span className="mono" style={{ color: 'var(--muted)' }}>
                    {revealedByTrack[i]} / {totalsByTrack[i]}
                  </span>
                </div>
              ))}
            </div>

            <div className="card flat card-pad-sm" style={{ background: 'var(--tint)' }}>
              <div className="bignum-sub" style={{ marginBottom: 4 }}>TEMPO GAP</div>
              <div style={{
                fontFamily: 'Source Serif 4', fontSize: 44, lineHeight: 1,
                letterSpacing: '-.02em',
              }}>
                {gapMultiple}
              </div>
              <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 8, lineHeight: 1.45 }}>
                Physical-supply events resolved per government-policy event,
                at the current scrub position.
              </div>
            </div>

            <blockquote className="pull" style={{ fontSize: 19, margin: '4px 0 0' }}>
              The financial-instrument tempo is silent during stable periods, structural during shocks, and almost completely absent from public discourse.
            </blockquote>
          </div>
        </div>
      </div>
    </section>
  );
}

window.Tempos = Tempos;
