// global.jsx — CDG Global · cómo están los pilotos de CDG en el Global Qualifier
// La página carga al instante con data/global.js y, si existe el puente
// gq-bridge.php, refresca tiempos/posiciones EN VIVO desde MultiGP al abrirse.
const { useState, useEffect, useRef, useMemo } = React;

// ---------- Directorio de pilotos (foto + país + IG) ----------
const GL_DIR = (typeof window !== "undefined" && window.CDG_PILOTS) ? window.CDG_PILOTS : {};
const _GL_NORM = Object.fromEntries(Object.entries(GL_DIR).map(([k, v]) => [k.toLowerCase().trim(), v]));
function pilotMeta(name) { if (!name) return {}; return _GL_NORM[name.toLowerCase().trim()] || {}; }
function flagUrl(code) { return (!code || code.length !== 2) ? "" : `https://flagcdn.com/${code.toLowerCase()}.svg`; }

// ---------- Tiempo ----------
function parseTime(str) {
  if (str === null || str === undefined) return Infinity;
  const s = String(str);
  const m = s.match(/(\d+):(\d+(?:\.\d+)?)/);
  if (m) { const ms = parseInt(m[1]) * 60000 + parseFloat(m[2]) * 1000; return ms > 0 ? ms : Infinity; }
  const n = parseFloat(s); // segundos sueltos "39.4"
  return Number.isFinite(n) && n > 0 ? n * 1000 : Infinity;
}
function fmtDelta(ms) {
  const s = ms / 1000;
  return (s >= 10 ? s.toFixed(1) : s.toFixed(2)) + "s";
}

// ---------- MultiGP profile link ----------
function mgpLink(p, cfg) {
  if (p.mgpUrl) return p.mgpUrl;
  return cfg.leaderboardUrl || "https://www.multigp.com/2026-global-qualifier-leaderboard/";
}

// ---------- localStorage: mejores tiempos vistos la última visita ----------
const GL_STORE = "cdg_gq_best_v1";
function readBests() {
  try { return JSON.parse(localStorage.getItem(GL_STORE) || "{}") || {}; } catch (e) { return {}; }
}
function writeBests(obj) {
  try { localStorage.setItem(GL_STORE, JSON.stringify(obj)); } catch (e) {}
}

// ---------- UI helpers ----------
function PilotAvatar({ name, size }) {
  const meta = pilotMeta(name); const s = size || 88;
  if (meta.photo) return <img className="pilot-avatar" src={meta.photo} alt={name} loading="lazy" style={{ width: s, height: s }} />;
  const initials = String(name || "?").split(/\s+/).map(w => w[0]).slice(0, 2).join("").toUpperCase();
  return <div className="pilot-avatar pa-fallback" style={{ width: s, height: s, fontSize: s * 0.34 }}>{initials}</div>;
}
function IgGlyph({ size = 12 }) {
  return (<svg className="ig-ico" viewBox="0 0 24 24" width={size} height={size} aria-hidden="true"><rect x="2.5" y="2.5" width="19" height="19" rx="5.5" fill="none" stroke="currentColor" strokeWidth="2"/><circle cx="12" cy="12" r="4.2" fill="none" stroke="currentColor" strokeWidth="2"/><circle cx="17.3" cy="6.7" r="1.4" fill="currentColor"/></svg>);
}

// ---------- Tarjeta de piloto ----------
function PilotCard({ p, pos, cfg, status, animate, idx }) {
  const meta = pilotMeta(p.handle);
  const flag = flagUrl(meta.country || p.country);
  const ms = parseTime(p.gqTime);
  const hasTime = Number.isFinite(ms);
  const isCDG = (p.where || "").toUpperCase() === "CDG";
  const chapter = p.chapter || meta.chapter || "";
  return (
    <div className={`gl-card ${animate ? "in" : ""} ${p.globalRank ? "ranked" : "prov"}`} style={{ animationDelay: `${Math.min(idx, 12) * 0.05}s` }}>
      <div className={`gl-where ${isCDG ? "cdg" : "out"}`}>{isCDG ? "Marcó en CDG" : "Marcó afuera"}</div>

      <div className={`gl-bigrank ${p.globalRank ? "" : "na"}`}>
        <span className="gl-bigrank-hash">#</span><span className="gl-bigrank-num">{p.globalRank ? p.globalRank : pos}</span>
        <span className="gl-bigrank-lbl">{p.globalRank ? "Posición mundial" : "Provisional · entre CDG"}</span>
      </div>

      <div className="gl-ava-wrap">
        <PilotAvatar name={p.handle} size={84} />
        {status && status.kind === "up" && <div className="gl-imp up" title={`Bajó ${fmtDelta(status.delta)}`}>▼ {fmtDelta(status.delta)}</div>}
        {status && status.kind === "new" && <div className="gl-imp new">NUEVO</div>}
      </div>

      <div className="gl-name">{flag && <img className="gl-flag" src={flag} alt={meta.country} loading="lazy" />}{p.handle}</div>
      {meta.ig
        ? <a className="gl-ig" href={`https://instagram.com/${meta.ig}`} target="_blank" rel="noopener noreferrer"><IgGlyph />@{meta.ig}</a>
        : <span className="gl-ig none">@—</span>}

      {!isCDG && chapter && (
        <div className="gl-home"><span className="gl-home-lbl">Home chapter</span><b>{chapter}</b></div>
      )}

      <div className="gl-time-block">
        <div className="gl-time-lbl">Mejor tiempo GQ</div>
        <div className={`gl-time ${hasTime ? "" : "na"}`}>{hasTime ? p.gqTime : "—"}</div>
      </div>

      <div className="gl-region">
        <span className="gl-region-lbl">{cfg.region || "Región"}</span>
        <span className="gl-region-num">{p.regionalRank ? `#${p.regionalRank}` : "—"}</span>
      </div>

      <a className="gl-link" href={mgpLink(p, cfg)} target="_blank" rel="noopener noreferrer">Ver en MultiGP <span aria-hidden="true">→</span></a>

      <button
        className="rx-btn gl-dl-btn"
        onClick={() => window.CDGShare && window.CDGShare.pilotGlobalReel(p, pos, cfg)}
        title="Imagen 1080×1920 de tu posición mundial para reel/historia de Instagram"
      >
        <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="5" fill="none" stroke="currentColor" strokeWidth="2"/><path d="M3 9h18M9 21V9" stroke="currentColor" strokeWidth="2"/></svg>
        Descargar mi posición
      </button>
    </div>
  );
}

function Global() {
  const cfg = (window.CDG_GLOBAL || { pilots: [] });
  const basePilots = cfg.pilots || [];

  // Snapshot de "mejores vistos" tomado UNA vez al abrir → para detectar mejoras.
  const snapshot = useRef(readBests());

  const [pilots, setPilots] = useState(basePilots);
  const [source, setSource] = useState("local");   // local | live | cache
  const [updated, setUpdated] = useState(cfg.updated || "");
  const [animate, setAnimate] = useState(false);

  useEffect(() => { const id = requestAnimationFrame(() => setAnimate(true)); return () => cancelAnimationFrame(id); }, []);

  // ---- Intento de refresco EN VIVO vía el puente PHP ----
  useEffect(() => {
    // Le preguntamos al puente por TODOS los pilotos relevantes:
    //  · el directorio (data/pilots.js)
    //  · todos los que aparecen en el ranking 5" y Tiny (data/results.js)
    //  · los de la base local (data/global.js)
    // Además, el puente añade solo a cualquiera cuyo home chapter sea CDG.
    const roster = (window.CDG_PILOTS && Object.keys(window.CDG_PILOTS)) || [];
    const rankHandles = [];
    (window.CDG_RESULTS || []).forEach(race => {
      [...(race.qualification || []), ...(race.bracket || [])].forEach(row => {
        const h = (row.handle || row.pilot || "").trim();
        if (h) rankHandles.push(h);
      });
    });
    const handles = Array.from(new Set([
      ...roster, ...rankHandles, ...basePilots.map(p => p.handle)
    ])).join(",");
    let cancelled = false;
    fetch(`gq-bridge.php?handles=${encodeURIComponent(handles)}`, { cache: "no-store" })
      .then(r => r.ok ? r.json() : null)
      .then(json => {
        if (cancelled || !json || !json.ok || !json.pilots) return;
        const live = json.pilots; // { handleLower: {...} }
        const merged = basePilots.map(p => {
          const l = live[p.handle.toLowerCase()];
          if (!l) return p;
          return {
            ...p,
            gqTime:       l.gqTime || p.gqTime,
            globalRank:   (l.globalRank != null) ? l.globalRank : p.globalRank,
            regionalRank: (l.regionalRank != null) ? l.regionalRank : p.regionalRank,
            where:        p.where || l.where,
            chapter:      l.chapter || p.chapter,
            mgpUrl:       l.mgpUrl || p.mgpUrl,
          };
        });
        // Pilotos que el live trae pero no estaban en la base local (marcaron afuera)
        const known = new Set(basePilots.map(p => p.handle.toLowerCase()));
        Object.keys(live).forEach(k => {
          if (!known.has(k)) merged.push({ ...live[k], where: live[k].where || "Afuera" });
        });
        setPilots(merged);
        setSource(json.source === "cache" ? "cache" : "live");
        if (json.updated) setUpdated(new Date(json.updated).toLocaleString("es-GT", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }));
      })
      .catch(() => { /* sin puente: nos quedamos con data/global.js */ });
    return () => { cancelled = true; };
  }, []);

  // ---- Ordenar: por rank global; sin rank → por tiempo ----
  const ordered = useMemo(() => {
    return [...pilots].map((p, i) => ({ p, i })).sort((a, b) => {
      const ra = a.p.globalRank ?? Infinity, rb = b.p.globalRank ?? Infinity;
      if (ra !== rb) return ra - rb;
      return parseTime(a.p.gqTime) - parseTime(b.p.gqTime);
    }).map(x => x.p);
  }, [pilots]);

  // ---- Detección de mejoras vs última visita + persistir nuevos bests ----
  const statuses = useMemo(() => {
    const snap = snapshot.current;
    const map = {};
    const nextBests = { ...snap };
    pilots.forEach(p => {
      const key = p.handle.toLowerCase();
      const ms = parseTime(p.gqTime);
      if (!Number.isFinite(ms)) return;
      const prev = snap[key];
      if (prev == null) {
        map[key] = { kind: "new" };
        nextBests[key] = ms;
      } else if (ms < prev - 1) { // mejoró (al menos 1ms)
        map[key] = { kind: "up", delta: prev - ms };
        nextBests[key] = ms;
      } else {
        nextBests[key] = Math.min(prev, ms);
      }
    });
    writeBests(nextBests);
    return map;
  }, [pilots]);

  const improvedCount = Object.values(statuses).filter(s => s.kind === "up").length;
  const withRank = ordered.filter(p => p.globalRank).length;

  // Mostramos a quien tenga POSICIÓN oficial O un TIEMPO GQ registrado.
  // (Así los pilotos de CDG siempre aparecen con su tiempo; cuando el puente
  //  está en vivo, además les llega la posición mundial de la tabla.)
  const shown = ordered.filter(p => p.globalRank || Number.isFinite(parseTime(p.gqTime)));

  const srcLabel = source === "live" ? "En vivo desde MultiGP"
    : source === "cache" ? "En vivo (caché)" : "Archivo local";

  return (
    <>
      <header className="gl-hero">
        <div className="gl-hero-inner">
          <div className="eyebrow"><span className="num">// CDG EN EL MUNDO · GLOBAL QUALIFIER {cfg.season || 2026}</span></div>
          <h1 className="gl-title">CDG<br/><span className="accent">Global</span></h1>
          <p className="gl-lead">
            Dónde están parados nuestros pilotos en el leaderboard mundial del <b>MultiGP Global Qualifier</b>.
            Sus tiempos en la pista oficial GQ — marcados en una fecha de CDG o afuera en el mundo — comparados contra todo el planeta.
          </p>

          <div className="gl-status">
            <span className={`gl-src ${source}`}>
              <span className="gl-dot"></span>{srcLabel}
            </span>
            {updated && <span className="gl-upd">Actualizado · {updated}</span>}
            {improvedCount > 0 && <span className="gl-improved">▼ {improvedCount} {improvedCount === 1 ? "piloto mejoró" : "pilotos mejoraron"} desde tu última visita</span>}
            <a className="gl-board-link" href={cfg.leaderboardUrl || "https://www.multigp.com/2026-global-qualifier-leaderboard/"} target="_blank" rel="noopener noreferrer">Leaderboard oficial →</a>
          </div>

          <div className="gl-hero-export">
            <button
              className="rx-btn big"
              onClick={() => window.CDGShare && window.CDGShare.globalBoardReel(cfg, shown)}
              title="Imagen 1080×1920 con todas las posiciones de CDG en el mundo, lista para IG"
            >
              <svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="5" fill="none" stroke="currentColor" strokeWidth="2"/><path d="M3 9h18M9 21V9" stroke="currentColor" strokeWidth="2"/></svg>
              Descargar posiciones de CDG (IG)
            </button>
          </div>
        </div>
      </header>

      <main className="gl-main">
        {shown.length === 0 ? (
          <div className="gl-empty"><div className="re-icon">—</div><div className="re-text">Aún no hay pilotos de CDG en la tabla oficial del Global Qualifier.</div></div>
        ) : (<>
          <div className="gl-grid">
            {shown.map((p, i) => (
              <PilotCard key={p.handle + i} p={p} pos={i + 1} cfg={cfg} status={statuses[p.handle.toLowerCase()]} animate={animate} idx={i} />
            ))}
          </div>

          <div className="gl-foot">
            <span><b>{shown.length}</b> pilotos CDG en la tabla GQ</span>
            <span className="gl-foot-sep">·</span>
            <span><b>{withRank}</b> con posición mundial oficial</span>
            <span className="gl-foot-sep">·</span>
            <span>Track: {cfg.trackName || "Global Qualifier"}</span>
          </div>

          <div className="gl-note">
            <b>Cómo se ordena.</b> Quien ya tiene <b>posición oficial en la tabla del MultiGP Global Qualifier</b>
            va primero (por número de posición); abajo, el resto de pilotos de CDG por su mejor tiempo GQ.
            Se actualiza directamente desde MultiGP. <span className="gl-note-chip">▼ MEJORÓ</span> marca a quien bajó su tiempo desde tu última visita.
          </div>
        </>)}
      </main>
    </>
  );
}

ReactDOM.createRoot(document.getElementById("gl-root")).render(<Global />);
