// Apex — chart + telemetry card components
// Globals consumed from window: RACE_META, DRIVERS, ORDER, STATE, LAP_HISTORY,
//   TIRE_DEG, degAt, PIT_LOSS_S, MICROSECTORS, WEATHER_FORECAST, PIT_WINDOW

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

// ──────────────────────────────────────────────────────────────
// Primitives
// ──────────────────────────────────────────────────────────────

function Card({ title, kicker, right, children, className = "", padded = true }) {
  return (
    <section className={`ax-card ${className}`}>
      {(title || right) && (
        <header className="ax-card-hd">
          <div className="ax-card-hd-l">
            {kicker && <span className="ax-kicker">{kicker}</span>}
            {title && <h3 className="ax-card-title">{title}</h3>}
          </div>
          {right && <div className="ax-card-hd-r">{right}</div>}
        </header>
      )}
      <div className={padded ? "ax-card-bd" : "ax-card-bd-flush"}>{children}</div>
    </section>
  );
}

function Kpi({ label, value, unit, accent, mono = true }) {
  return (
    <div className="ax-kpi">
      <div className="ax-kpi-label">{label}</div>
      <div className={`ax-kpi-value ${mono ? "mono" : ""}`} style={accent ? { color: accent } : null}>
        {value}{unit && <span className="ax-kpi-unit">{unit}</span>}
      </div>
    </div>
  );
}

function Pill({ children, tone = "neutral" }) {
  return <span className={`ax-pill ax-pill-${tone}`}>{children}</span>;
}

function CompoundDot({ compound, size = 10 }) {
  const c = TIRE_DEG[compound]?.color || "#888";
  return (
    <svg width={size} height={size} viewBox="0 0 10 10" style={{ display: "inline-block", verticalAlign: "middle" }}>
      <circle cx="5" cy="5" r="4" fill="none" stroke={c} strokeWidth="1.4" />
      <circle cx="5" cy="5" r="1.6" fill={c} />
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────
// Lap chart — gap to leader over laps for top-N drivers
// ──────────────────────────────────────────────────────────────

function LapChart({ focusDriver = "LEC", height = 240 }) {
  const top = ORDER.slice(0, 6);
  const width = 760;
  const padL = 36, padR = 12, padT = 14, padB = 24;
  const maxLap = RACE_META.currentLap;
  const maxGap = Math.max(...top.map(c => Math.max(...LAP_HISTORY[c]))) * 1.1;

  const x = (lap) => padL + ((lap - 1) / (maxLap - 1)) * (width - padL - padR);
  const y = (gap) => padT + (gap / maxGap) * (height - padT - padB);

  const gridLaps = [1, 5, 10, 15, 20, 25, 30].filter(l => l <= maxLap);
  const gridGaps = [0, 10, 20, 30, 40].filter(g => g < maxGap);

  return (
    <svg viewBox={`0 0 ${width} ${height}`} style={{ width: "100%", height: "auto", display: "block" }}>
      <defs>
        <filter id="focusGlow" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur stdDeviation="3" result="blur" />
          <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
      </defs>
      {/* grid */}
      {gridGaps.map(g => (
        <g key={g}>
          <line x1={padL} x2={width - padR} y1={y(g)} y2={y(g)} stroke="#1a1a1a" strokeWidth="1" />
          <text x={padL - 6} y={y(g) + 3} fill="#5a5a5a" fontSize="9" textAnchor="end" fontFamily="IBM Plex Mono">
            {g === 0 ? "LEADER" : `+${g}s`}
          </text>
        </g>
      ))}
      {gridLaps.map(l => (
        <text key={l} x={x(l)} y={height - 6} fill="#5a5a5a" fontSize="9" textAnchor="middle" fontFamily="IBM Plex Mono">L{l}</text>
      ))}

      {/* safety car zone */}
      <rect x={x(1)} y={padT} width={x(3) - x(1)} height={height - padT - padB} fill="#FFB02010" />
      <text x={x(2)} y={padT + 10} fill="#FFB020" fontSize="8" textAnchor="middle" fontFamily="IBM Plex Mono" opacity="0.7">SC</text>

      {/* current lap line */}
      <line x1={x(maxLap)} x2={x(maxLap)} y1={padT} y2={height - padB} stroke="#FFB020" strokeWidth="1" strokeDasharray="2 2" />

      {/* lines */}
      {top.map((code, idx) => {
        const d = DRIVERS.find(x => x.code === code);
        const data = LAP_HISTORY[code];
        const isFocus = code === focusDriver;
        const path = data.map((g, i) => `${i === 0 ? "M" : "L"} ${x(i + 1)} ${y(g)}`).join(" ");
        const endX = x(maxLap);
        const endY = y(data[data.length - 1]);
        return (
          <g key={code}>
            <path d={path} fill="none" stroke={d.color}
              strokeWidth={isFocus ? 2 : 1}
              opacity={isFocus ? 1 : 0.38}
              strokeLinecap="round" strokeLinejoin="round" />
            {/* live endpoint — pulsing ring for focus driver */}
            {isFocus && (
              <>
                <circle cx={endX} cy={endY} r="8" fill="none" stroke={d.color} strokeWidth="1" opacity="0">
                  <animate attributeName="r" values="4;10;4" dur="2.4s" repeatCount="indefinite" />
                  <animate attributeName="opacity" values="0.5;0;0.5" dur="2.4s" repeatCount="indefinite" />
                </circle>
                <circle cx={endX} cy={endY} r="3.5" fill={d.color}
                  filter="url(#focusGlow)" />
              </>
            )}
            {!isFocus && (
              <circle cx={endX} cy={endY} r="2" fill={d.color} opacity="0.7" />
            )}
            <text x={endX + 6} y={endY + 3}
              fill={isFocus ? "#fff" : "#666"} fontSize="9" fontFamily="IBM Plex Mono">
              {code}
            </text>
          </g>
        );
      })}
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────
// Tire degradation — predicted lap time delta over remaining laps
// ──────────────────────────────────────────────────────────────

function TireDegChart({ driver = "LEC", height = 200 }) {
  const state = STATE[driver];
  const compound = state.compound;
  const startAge = state.tireAge;
  const projectionLaps = 22;
  const width = 540;
  const padL = 38, padR = 14, padT = 14, padB = 24;

  const points = Array.from({ length: projectionLaps }, (_, i) => {
    const age = startAge + i;
    return { age, lap: RACE_META.currentLap + i, delta: degAt(compound, age) };
  });
  const maxDelta = Math.max(...points.map(p => p.delta)) * 1.05;

  const x = (i) => padL + (i / (projectionLaps - 1)) * (width - padL - padR);
  const y = (d) => padT + (d / maxDelta) * (height - padT - padB);

  const path = points.map((p, i) => `${i === 0 ? "M" : "L"} ${x(i)} ${y(p.delta)}`).join(" ");
  const cliffStart = TIRE_DEG[compound].cliffStart;
  const cliffIdx = points.findIndex(p => p.age >= cliffStart);

  return (
    <svg viewBox={`0 0 ${width} ${height}`} style={{ width: "100%", height: "auto", display: "block" }}>
      {/* grid */}
      {[0, 0.5, 1.0, 1.5, 2.0].filter(v => v < maxDelta).map(v => (
        <g key={v}>
          <line x1={padL} x2={width - padR} y1={y(v)} y2={y(v)} stroke="#1a1a1a" />
          <text x={padL - 6} y={y(v) + 3} fill="#5a5a5a" fontSize="9" textAnchor="end" fontFamily="IBM Plex Mono">
            {v === 0 ? "0" : `+${v.toFixed(1)}s`}
          </text>
        </g>
      ))}

      {/* x-axis labels */}
      {[0, 5, 10, 15, 20].map(i => (
        <text key={i} x={x(i)} y={height - 6} fill="#5a5a5a" fontSize="9" textAnchor="middle" fontFamily="IBM Plex Mono">
          L{RACE_META.currentLap + i}
        </text>
      ))}

      {/* cliff zone */}
      {cliffIdx > 0 && (
        <>
          <rect x={x(cliffIdx)} y={padT} width={width - padR - x(cliffIdx)} height={height - padT - padB}
            fill="#FF3B3008" />
          <line x1={x(cliffIdx)} x2={x(cliffIdx)} y1={padT} y2={height - padB} stroke="#FF3B30" strokeWidth="1" strokeDasharray="3 2" opacity="0.6" />
          <text x={x(cliffIdx) + 4} y={padT + 10} fill="#FF3B30" fontSize="9" fontFamily="IBM Plex Mono">CLIFF</text>
        </>
      )}

      {/* pit window */}
      <rect x={x(PIT_WINDOW.optimalLap - RACE_META.currentLap - 2)} y={padT}
        width={x(PIT_WINDOW.optimalLap - RACE_META.currentLap + 4) - x(PIT_WINDOW.optimalLap - RACE_META.currentLap - 2)}
        height={height - padT - padB}
        fill="#FFB02012" />

      {/* projection */}
      <path d={path} fill="none" stroke={TIRE_DEG[compound].color} strokeWidth="1.6" strokeLinecap="round" />
      <circle cx={x(0)} cy={y(points[0].delta)} r="3" fill="#FFB020" />
      <text x={x(0)} y={padT - 2} fill="#FFB020" fontSize="9" fontFamily="IBM Plex Mono" textAnchor="middle">NOW</text>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────
// Pit window strip — visual window 30→50
// ──────────────────────────────────────────────────────────────

function PitWindowStrip() {
  const startLap = 30, endLap = 50;
  const total = endLap - startLap;
  const pos = (lap) => `${((lap - startLap) / total) * 100}%`;

  return (
    <div className="ax-pitwin">
      <div className="ax-pitwin-track">
        <div className="ax-pitwin-window" style={{
          left: pos(PIT_WINDOW.earliestLap),
          right: `calc(100% - ${pos(PIT_WINDOW.latestLap)})`,
        }}></div>
        <div className="ax-pitwin-optimal" style={{ left: pos(PIT_WINDOW.optimalLap) }}></div>
        <div className="ax-pitwin-now" style={{ left: pos(RACE_META.currentLap) }}>
          <div className="ax-pitwin-now-dot"></div>
          <div className="ax-pitwin-now-label">L{RACE_META.currentLap}</div>
        </div>
      </div>
      <div className="ax-pitwin-axis">
        {[30, 35, 40, 45, 50].map(l => (
          <span key={l} style={{ left: pos(l), position: "absolute", transform: "translateX(-50%)" }}>L{l}</span>
        ))}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Mini-sector bar — 24 micro-sectors, delta LEC vs PIA
// ──────────────────────────────────────────────────────────────

function MicroSectors() {
  const max = 90;
  return (
    <div className="ax-microsec">
      <div className="ax-microsec-row">
        {MICROSECTORS.map((d, i) => {
          const mag = Math.min(1, Math.abs(d) / max);
          const isLoss = d > 0;
          return (
            <div key={i} className="ax-microsec-cell" title={`MS${i+1}: ${d > 0 ? "+" : ""}${d}ms`}>
              <div className={`ax-microsec-fill ${isLoss ? "loss" : "gain"}`}
                style={{ height: `${mag * 100}%`, alignSelf: isLoss ? "flex-start" : "flex-end" }}></div>
            </div>
          );
        })}
      </div>
      <div className="ax-microsec-axis">
        <span>S1</span><span>S2</span><span>S3</span>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Standings rail
// ──────────────────────────────────────────────────────────────

function Standings({ focusDriver, onFocus }) {
  return (
    <div className="ax-stand">
      <div className="ax-stand-hd">
        <span>POS</span><span>DRV</span><span>GAP</span><span>INT</span><span>TIRE</span><span>LAST</span>
      </div>
      {ORDER.map(code => {
        const d = DRIVERS.find(x => x.code === code);
        const s = STATE[code];
        const isFocus = code === focusDriver;
        return (
          <button key={code} className={`ax-stand-row ${isFocus ? "focus" : ""}`} onClick={() => onFocus(code)}>
            <span className="ax-stand-pos">{s.pos}</span>
            <span className="ax-stand-drv">
              <span className="ax-stand-bar" style={{ background: d.color }}></span>
              <span className="ax-stand-code">{code}</span>
            </span>
            <span className="mono ax-stand-gap">{s.pos === 1 ? "LEADER" : `+${s.gap.toFixed(3)}`}</span>
            <span className="mono ax-stand-int">{s.pos === 1 ? "—" : `+${s.interval.toFixed(3)}`}</span>
            <span className="ax-stand-tire">
              <CompoundDot compound={s.compound} />
              <span className="mono">{s.tireAge}</span>
            </span>
            <span className="mono ax-stand-last">{formatLapTime(s.lastLap)}</span>
          </button>
        );
      })}
    </div>
  );
}

function formatLapTime(s) {
  const m = Math.floor(s / 60);
  const sec = (s - m * 60).toFixed(3);
  return `${m}:${sec.padStart(6, "0")}`;
}

// ──────────────────────────────────────────────────────────────
// Weather forecast strip
// ──────────────────────────────────────────────────────────────

function WeatherStrip() {
  const w = WEATHER_FORECAST.slice(0, 16);
  return (
    <div className="ax-weather">
      <div className="ax-weather-row">
        {w.map((p) => (
          <div key={p.lap} className="ax-weather-cell">
            <div className="ax-weather-bar" style={{ height: `${p.rainPct}%` }}></div>
          </div>
        ))}
      </div>
      <div className="ax-weather-axis">
        {w.map((p, i) => (
          <span key={p.lap} className="mono" style={{ opacity: i % 4 === 0 ? 1 : 0 }}>L{p.lap}</span>
        ))}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Track map — minimal Monaco abstract
// ──────────────────────────────────────────────────────────────

function TrackMap({ focusDriver }) {
  // Schematic monaco-ish path
  const d = "M 20 80 L 60 30 L 140 25 Q 170 25 175 50 L 190 90 Q 195 110 175 120 L 110 130 Q 80 130 70 110 L 55 95 Q 40 90 35 100 L 25 105 Z";
  // Driver positions along path 0..1
  const positions = [0.05, 0.10, 0.16, 0.22, 0.35, 0.41, 0.55, 0.66, 0.74, 0.83];
  // Helper to sample point along path
  const pathRef = useRef(null);
  const [pts, setPts] = useState([]);
  useEffect(() => {
    if (!pathRef.current) return;
    const len = pathRef.current.getTotalLength();
    setPts(positions.map(t => pathRef.current.getPointAtLength(t * len)));
  }, []);

  return (
    <svg viewBox="0 0 220 160" style={{ width: "100%", height: "auto", display: "block" }}>
      <path ref={pathRef} d={d} fill="none" stroke="#262626" strokeWidth="2" strokeLinejoin="round" />
      <path d={d} fill="none" stroke="#3a3a3a" strokeWidth="0.6" strokeDasharray="1 1" strokeLinejoin="round" />
      {/* start-finish */}
      <line x1="20" y1="78" x2="20" y2="86" stroke="#FFB020" strokeWidth="1.4" />
      <text x="22" y="74" fill="#FFB020" fontSize="6" fontFamily="IBM Plex Mono">S/F</text>
      {/* drivers */}
      {pts.map((p, i) => {
        const code = ORDER[i];
        const drv = DRIVERS.find(x => x.code === code);
        const isFocus = code === focusDriver;
        return (
          <g key={i}>
            <circle cx={p.x} cy={p.y} r={isFocus ? 3.2 : 2.2}
              fill={drv.color}
              stroke={isFocus ? "#FFB020" : "none"}
              strokeWidth={isFocus ? "1" : "0"} />
            {isFocus && (
              <text x={p.x + 5} y={p.y + 2} fill="#fff" fontSize="6" fontFamily="IBM Plex Mono">{code}</text>
            )}
          </g>
        );
      })}
    </svg>
  );
}

Object.assign(window, {
  Card, Kpi, Pill, CompoundDot,
  LapChart, TireDegChart, PitWindowStrip, MicroSectors,
  Standings, WeatherStrip, TrackMap, formatLapTime,
});
