/* ============================================================
   Admin panel — whitelist, invitations, matches/scores, participants
   ============================================================ */
const INVITE_BASE = "https://quiniela.quantia.pe";
const DAY_MS = 24 * 3600 * 1000;

/* ------------------------------------------------------------
   Engagement (Historia 2) — pronósticos pendientes que vencen
   en las próximas 24h. 100% client-side: deriva de users/matches/
   predictions ya espejados en QStore. "Esperados" = colaboradores
   de la quiniela global (no-invitados), igual que el leaderboard.
   Solo cuenta partidos PRONOSTICABLES (matchReady) que aún no inician.
   ------------------------------------------------------------ */
function qmUpcoming24h(Q) {
  const now = Q.NOW, end = now + DAY_MS;
  return Q.db.matches
    .filter((m) => m.kickoff > now && m.kickoff <= end && Q.matchReady(m))
    .sort((a, b) => a.kickoff - b.kickoff);
}
function qmPendingStats(Q) {
  const matches = qmUpcoming24h(Q);
  const participants = Q.db.users.filter((u) => !u.guest);
  const perMatch = matches.map((m) => {
    let predicted = 0;
    participants.forEach((u) => { if (Q.prediction(m.id, u.userId)) predicted++; });
    return { match: m, expected: participants.length, predicted, pending: participants.length - predicted };
  });
  const perUser = {};
  participants.forEach((u) => {
    let miss = 0;
    matches.forEach((m) => { if (!Q.prediction(m.id, u.userId)) miss++; });
    perUser[u.userId] = miss;
  });
  const totalExpected = matches.length * participants.length;
  const totalPredicted = perMatch.reduce((s, x) => s + x.predicted, 0);
  const topUsers = participants
    .map((u) => ({ user: u, missing: perUser[u.userId] }))
    .filter((x) => x.missing > 0)
    .sort((a, b) => b.missing - a.missing || a.user.name.localeCompare(b.user.name, "es"));
  return {
    matches, perMatch, perUser, topUsers, participants: participants.length,
    totalExpected, totalPredicted, totalPending: totalExpected - totalPredicted,
    coverage: totalExpected ? totalPredicted / totalExpected : 1,
  };
}

/* ------------------------------------------------------------
   Ligas e invitados externos (Historia 1) — agrega TODAS las ligas
   por creador (commissionerUid). "Gente externa invitada" = miembros
   con guest:true que efectivamente se unieron, contados como personas
   distintas (por uid). Solo ligas activas.
   ------------------------------------------------------------ */
function qmLeagueStats(leagues) {
  const active = (leagues || []).filter((l) => l && l.active !== false);
  const byCreator = new Map();
  const globalExternal = new Set();
  active.forEach((l) => {
    const key = l.commissionerUid || "—";
    let row = byCreator.get(key);
    if (!row) {
      row = {
        uid: key, name: l.commissionerName || "(sin nombre)",
        leagues: 0, externalUids: new Set(), internalUids: new Set(), externalSeats: 0,
      };
      byCreator.set(key, row);
    }
    row.leagues++;
    (l.members || []).forEach((m) => {
      if (m.guest) {
        row.externalUids.add(m.uid || m.name);
        row.externalSeats++;
        globalExternal.add(m.uid || m.name);
      } else if (m.uid !== key) {
        row.internalUids.add(m.uid || m.name);
      }
    });
  });
  const rows = [...byCreator.values()].map((r) => ({
    uid: r.uid, name: r.name, leagues: r.leagues,
    externalUnique: r.externalUids.size, externalSeats: r.externalSeats,
    internalUnique: r.internalUids.size,
  })).sort((a, b) => b.externalUnique - a.externalUnique || b.leagues - a.leagues || a.name.localeCompare(b.name, "es"));
  return {
    rows,
    totalLeagues: active.length,
    totalCreators: byCreator.size,
    totalExternalUnique: globalExternal.size,
  };
}

function AdminMatchRow({ match }) {
  const Q = window.QStore;
  const status = Q.matchStatus(match);
  const result = Q.db.results[match.id] || {};
  const home = Q.team(match.home), away = Q.team(match.away);
  const ready = Q.matchReady(match);
  const isKO = Q.isKnockout(match);
  const codes = Object.keys(Q.TEAMS).filter((c) => c !== "TBD");
  const [fh, setFh] = useState(result.homeGoals ?? result.liveHome ?? 0);
  const [fa, setFa] = useState(result.awayGoals ?? result.liveAway ?? 0);
  const [editing, setEditing] = useState(false);

  // auto-marcador de eliminación (status 'review'): cuando llega/cambia el
  // resultado auto-detectado, precargamos los inputs para que el admin sólo
  // revise y confirme (puede corregirlo antes, p.ej. si trajera penales).
  useEffect(() => {
    if (!editing && status === "review") {
      if (result.homeGoals != null) setFh(result.homeGoals);
      if (result.awayGoals != null) setFa(result.awayGoals);
    }
  }, [result.homeGoals, result.awayGoals, status, editing]);

  // meta-edit (teams / group / phase / kickoff) — kickoff shown/edited in Lima time
  const [meta, setMeta] = useState(false);
  const [eh, setEh] = useState(match.home || "");
  const [ea, setEa] = useState(match.away || "");
  const [eg, setEg] = useState(match.group || "");
  const [ep, setEp] = useState(match.phase || "");
  const [ek, setEk] = useState(tsToLimaInput(match.kickoff));

  const saveFinal = () => {
    Q.actions.setFinal(match.id, fh, fa);
    const o = Q.outcome(Q.db.currentUserId, match);
    if (o.kind === "exact") fireConfetti();
    toast("Resultado final guardado · ranking recalculado", "ok");
    setEditing(false);
  };
  // marcador parcial en vivo: NO finaliza el partido; puntúa de forma provisional
  // (los rankings cambian con un indicador "en vivo" y pueden variar al cerrar).
  const saveLive = () => {
    Q.actions.setLive(match.id, fh, fa);
    toast("Marcador en vivo guardado · puntaje provisional", "ok");
  };
  const correct = () => { Q.actions.correctFinal(match.id, fh, fa); toast("Resultado corregido · recálculo completo", "ok"); setEditing(false); };
  const saveMeta = () => {
    Q.actions.updateMatch(match.id, { home: eh, away: ea, group: eg, phase: ep, kickoff: limaInputToTs(ek) });
    toast("Datos del partido actualizados", "ok"); setMeta(false);
  };

  const ScoreInputs = () => (
    <div className="qm-mini-score">
      <input type="number" min="0" value={fh} onChange={(e) => setFh(+e.target.value)} />
      <span style={{ fontWeight: 800, color: "var(--fg-3)" }}>:</span>
      <input type="number" min="0" value={fa} onChange={(e) => setFa(+e.target.value)} />
    </div>
  );

  const hLabel = home ? `${home.flag} ${home.name}` : Q.slotLabel(match.homeSrc);
  const aLabel = away ? `${away.name} ${away.flag}` : Q.slotLabel(match.awaySrc);

  return (
    <div className="qm-admin-match">
      <div className="info">
        <b>{hLabel} <span style={{ color: "var(--fg-3)" }}>vs</span> {aLabel}</b>
        <div className="meta">
          {match.group ? roundLabel(match.group) : match.phase} · {fmtWhen(match.kickoff)}
          {status === "live" && <span style={{ color: "var(--accent)", fontWeight: 700 }}> · 🔴 En juego{result.liveHome != null ? ` ${result.liveHome}–${result.liveAway} (provisional)` : ""}</span>}
          {status === "locked" && <span style={{ fontWeight: 700 }}> · Por confirmar</span>}
          {status === "review" && <span style={{ color: "var(--accent)", fontWeight: 700 }}> · ⚠️ Auto {result.homeGoals}–{result.awayGoals} · por confirmar</span>}
          {status === "final" && <span style={{ color: "var(--ok)", fontWeight: 700 }}> · ✓ Final {result.homeGoals}–{result.awayGoals}</span>}
          {status === "open" && ready && <span> · Aún no inicia</span>}
          {!ready && <span style={{ fontWeight: 700, color: "var(--accent)" }}> · Equipos por definir</span>}
          {" · "}
          <button onClick={() => setMeta((m) => !m)} style={{ background: "none", border: "none", color: "var(--accent)", cursor: "pointer", font: "inherit", padding: 0, display: "inline-flex", gap: 3, alignItems: "center" }}><Icon name="edit" size={12} />Editar datos</button>
        </div>
        {meta && (
          <div className="qm-row-inline" style={{ marginTop: 8, flexWrap: "wrap", gap: 6, alignItems: "flex-end" }}>
            <div><span className="qm-mini">Local</span><select className="qm-sel" value={eh} onChange={(e) => setEh(e.target.value)}><option value="">(por definir)</option>{codes.map((c) => <option key={c} value={c}>{(Q.TEAMS[c] || {}).flag} {(Q.TEAMS[c] || {}).name}</option>)}</select></div>
            <div><span className="qm-mini">Visitante</span><select className="qm-sel" value={ea} onChange={(e) => setEa(e.target.value)}><option value="">(por definir)</option>{codes.map((c) => <option key={c} value={c}>{(Q.TEAMS[c] || {}).flag} {(Q.TEAMS[c] || {}).name}</option>)}</select></div>
            <div><span className="qm-mini">Grupo/Llave</span><input className="qm-sel" value={eg} onChange={(e) => setEg(e.target.value)} style={{ width: 90 }} /></div>
            <div><span className="qm-mini">Fase</span><input className="qm-sel" value={ep} onChange={(e) => setEp(e.target.value)} /></div>
            <div><span className="qm-mini">Inicio (Lima)</span><input className="qm-sel" type="datetime-local" value={ek} onChange={(e) => setEk(e.target.value)} /></div>
            <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={saveMeta}>Guardar datos</button>
            <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => setMeta(false)}>Cancelar</button>
          </div>
        )}
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 8, alignItems: "flex-end" }}>
        {!ready ? (
          <span style={{ fontSize: 12, color: "var(--fg-3)", textAlign: "right", maxWidth: 160 }}>Asigna ambos equipos para habilitar el resultado.</span>
        ) : (
          <>
            {(status === "live" || status === "locked") && (
              <>
                <ScoreInputs />
                <div style={{ display: "flex", gap: 6 }}>
                  <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={saveLive} title="Guarda un marcador parcial: el partido sigue 'en juego' y el puntaje cuenta de forma provisional.">🔴 Parcial en vivo</button>
                  <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={saveFinal}>Guardar final</button>
                </div>
              </>
            )}
            {status === "review" && (
              <>
                <span style={{ fontSize: 12, color: "var(--accent)", fontWeight: 700, textAlign: "right", maxWidth: 210 }}>
                  ⚠️ Marcador automático{isKO ? " — revisa alargue/penales antes de confirmar" : " — revísalo antes de confirmar"}
                </span>
                <ScoreInputs />
                <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={saveFinal}>Confirmar resultado</button>
              </>
            )}
            {status === "final" && !editing && (
              <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => { setFh(result.homeGoals); setFa(result.awayGoals); setEditing(true); }}><Icon name="edit" size={14} />Corregir</button>
            )}
            {status === "final" && editing && (
              <>
                <ScoreInputs />
                <div style={{ display: "flex", gap: 6 }}>
                  <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => setEditing(false)}>Cancelar</button>
                  <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={correct}>Guardar</button>
                </div>
              </>
            )}
            {status === "open" && (
              <span style={{ fontSize: 12, color: "var(--fg-3)", display: "inline-flex", gap: 6, alignItems: "center" }}><Icon name="lock" size={13} />Inicia {fmtTime(match.kickoff)}</span>
            )}
            {/* knockout tie → admin picks who advances (penalties) */}
            {isKO && status === "final" && result.homeGoals === result.awayGoals && (
              <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12 }}>
                <span style={{ color: "var(--accent)", fontWeight: 700 }}>Avanza:</span>
                <select className="qm-sel" value={result.advances || ""} onChange={(e) => Q.actions.setAdvances(match.id, e.target.value)}>
                  <option value="">—</option>
                  <option value={match.home}>{home ? home.name : match.home}</option>
                  <option value={match.away}>{away ? away.name : match.away}</option>
                </select>
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

function AdminScreen() {
  const Q = useStore();
  const [tab, setTab] = useState("matches");
  const [newEmail, setNewEmail] = useState("");
  const [newDom, setNewDom] = useState("");
  const [inviteInput, setInviteInput] = useState("");
  const [links, setLinks] = useState([]);
  const [sending, setSending] = useState(false);

  const [showAdd, setShowAdd] = useState(false);

  const parseInviteEmails = () =>
    inviteInput.split(/[\s,;]+/).map((e) => e.trim()).filter(Boolean);
  const genLinks = () => {
    setLinks(parseInviteEmails().map((e) => `${INVITE_BASE}?email=${encodeURIComponent(e)}`));
  };
  const sendInvites = async () => {
    if (!Q.actions.sendInvitations) { toast("Envío de correo no disponible en este entorno", "warn"); return; }
    const emails = parseInviteEmails();
    if (emails.length === 0) { toast("Pega al menos un correo"); return; }
    setSending(true);
    const r = await Q.actions.sendInvitations(emails);
    setSending(false);
    if (r.error) { toast(r.error, "warn"); return; }
    const okN = (r.sent || []).length, badN = (r.failed || []).length;
    if (badN === 0) toast(`Invitaciones enviadas (${okN}) ✓`, "ok");
    else toast(`Enviadas ${okN}, fallaron ${badN}`, badN && !okN ? "warn" : "ok");
  };

  const sendActivation = async () => {
    if (!Q.actions.sendActivationEmails) { toast("Envío de correo no disponible en este entorno", "warn"); return; }
    const emails = parseInviteEmails();
    if (!emails.length) { toast("Pega al menos un correo arriba"); return; }
    setSending(true);
    const r = await Q.actions.sendActivationEmails(emails);
    setSending(false);
    if (r.error) { toast(r.error, "warn"); return; }
    if (r.failed) toast(`Activación: enviados ${r.sent} · fallaron ${r.failed}`, r.sent ? "ok" : "warn");
    else toast(`Correo de activación enviado (${r.sent}) ✓`, "ok");
  };
  const copy = (txt) => { navigator.clipboard?.writeText(txt); toast("Enlace copiado", "ok"); };

  const matches = Q.db.matches.slice().sort((a, b) => a.kickoff - b.kickoff);

  return (
    <div>
      <div className="qm-eyebrow"><Icon name="shield" size={14} />Panel de administración</div>
      <h1 className="qm-h">Admin</h1>
      <p className="qm-sub">Gestiona accesos, partidos y resultados. El acceso real es vía <code style={{ background: "var(--bg-3)", padding: "1px 6px", borderRadius: 6 }}>?admin=KEY</code>.</p>

      <div className="qm-admin-tabs">
        {[["matches", "Partidos y resultados"], ["users", "Usuarios"], ["whitelist", "Lista blanca"], ["invites", "Invitaciones"], ["people", "Participantes"], ["pulse", "Engagement 24h"], ["ligas", "Ligas e invitados"]].map(([k, l]) => (
          <button key={k} className={tab === k ? "on" : ""} onClick={() => setTab(k)}>{l}</button>
        ))}
      </div>

      {/* ---------- MATCHES ---------- */}
      {tab === "matches" && (
        <div className="qm-panel">
          <h3>Resultados</h3>
          <p className="ph">El estado <b>En juego</b> se calcula automáticamente por la hora del partido (120 min desde el inicio). Ingresa el <b>resultado final</b> para recalcular puntos y tomar la foto del ranking · corrígelo cuando haga falta. Los partidos de eliminación se completan solos a medida que cargas resultados; los cupos de <b>mejores terceros</b> y cualquier indefinido los asignas con <b>Editar datos</b>.</p>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 12 }}>
            <button className="qm-btn qm-btn-sm qm-btn-dark" onClick={() => { Q.actions.recalcBracket(); toast("Llave recalculada desde los resultados", "ok"); }}>
              <Icon name="trophy" size={14} />Recalcular llave eliminatoria
            </button>
            <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => setShowAdd(true)}>
              <Icon name="plus" size={14} />Agregar partido
            </button>
          </div>
          {matches.map((m) => <AdminMatchRow key={m.id} match={m} />)}
          {showAdd && <AddMatchModal onClose={() => setShowAdd(false)} />}
        </div>
      )}

      {/* ---------- WHITELIST ---------- */}
      {tab === "whitelist" && (
        <div className="qm-panel">
          <h3>Dominios autorizados</h3>
          <p className="ph">Cualquier correo de un dominio autorizado se aprueba automáticamente al registrarse.</p>
          <div className="qm-row2" style={{ marginBottom: 12 }}>
            <input className="qm-input" placeholder="quantia.pe" value={newDom} onChange={(e) => setNewDom(e.target.value)} />
            <button className="qm-btn qm-btn-dark" onClick={() => { if (newDom.trim()) { Q.actions.addWhitelistDomain(newDom); setNewDom(""); toast("Dominio agregado", "ok"); } }}><Icon name="plus" size={16} /></button>
          </div>
          <div className="qm-list">
            {Q.db.whitelist.domains.map((d) => (
              <div className="qm-listitem" key={d}><span className="qm-tagdom">dominio</span><b>@{d}</b><button className="x" onClick={() => Q.actions.removeWhitelist("domains", d)}>✕</button></div>
            ))}
          </div>

          <div className="qm-divider" />
          <h3>Correos individuales</h3>
          <p className="ph">Autoriza invitados puntuales fuera del dominio corporativo.</p>
          <div className="qm-row2" style={{ marginBottom: 12 }}>
            <input className="qm-input" placeholder="persona@correo.com" value={newEmail} onChange={(e) => setNewEmail(e.target.value)} />
            <button className="qm-btn qm-btn-dark" onClick={() => { if (newEmail.trim()) { Q.actions.addWhitelistEmail(newEmail); setNewEmail(""); toast("Correo agregado", "ok"); } }}><Icon name="plus" size={16} /></button>
          </div>
          <div className="qm-list">
            {Q.db.whitelist.emails.map((e) => (
              <div className="qm-listitem" key={e}><Icon name="mail" size={16} /><span>{e}</span><button className="x" onClick={() => Q.actions.removeWhitelist("emails", e)}>✕</button></div>
            ))}
            {Q.db.whitelist.emails.length === 0 && <div style={{ color: "var(--fg-3)", fontSize: 13 }}>Sin correos individuales.</div>}
          </div>
        </div>
      )}

      {/* ---------- INVITES ---------- */}
      {tab === "invites" && (
        <div className="qm-panel">
          <h3>Generador de invitaciones</h3>
          <p className="ph">Pega una lista de correos y obtén enlaces con el correo pre-cargado (campo de solo lectura al registrarse).</p>
          <textarea className="qm-input" rows="3" placeholder="ana.perez@quantia.pe, luis.gomez@quantia.pe" value={inviteInput} onChange={(e) => setInviteInput(e.target.value)} style={{ resize: "vertical" }} />
          <div style={{ display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap" }}>
            <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={sendInvites} disabled={sending}><Icon name="mail" size={14} />{sending ? "Enviando…" : "Enviar por correo"}</button>
            <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={genLinks}><Icon name="bolt" size={14} />Generar enlaces</button>
            <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => copy(INVITE_BASE)}><Icon name="copy" size={14} />Enlace para todo el dominio</button>
          </div>
          {links.length > 0 && (
            <div className="qm-list" style={{ marginTop: 14 }}>
              {links.map((l, i) => (
                <div className="qm-listitem" key={i} style={{ wordBreak: "break-all" }}>
                  <span style={{ flex: 1, fontSize: 12 }}>{l}</span>
                  <button className="x" onClick={() => copy(l)}><Icon name="copy" size={14} /></button>
                </div>
              ))}
            </div>
          )}
          <div className="qm-invite-out">
            <b style={{ color: "#fff" }}>Enlace para todo el dominio</b><br />
            <span className="lnk">{INVITE_BASE}</span><br />
            <span style={{ opacity: .7 }}>Cualquier @quantia.pe puede registrarse.</span>
          </div>

          <div className="qm-divider" />
          <h3>Enviar correo de activación</h3>
          <p className="ph">Envía la plantilla de marca <b>Email de activación</b> a los correos de arriba. Cada destinatario recibe su <b>enlace mágico</b> (generado en el servidor) para entrar directo. Solo correos autorizados en la <b>Lista blanca</b>.</p>
          <div style={{ marginTop: 4 }}>
            <button className="qm-btn qm-btn-dark" disabled={sending} onClick={sendActivation}><Icon name="mail" size={16} />{sending ? "Enviando…" : "Enviar activación"}</button>
          </div>
        </div>
      )}

      {/* ---------- PEOPLE ---------- */}
      {tab === "people" && <PeoplePanel />}
      {/* ---------- ENGAGEMENT 24h ---------- */}
      {tab === "pulse" && <EngagementPanel />}
      {/* ---------- LIGAS E INVITADOS ---------- */}
      {tab === "ligas" && <LeaguesAdminPanel />}
      {/* ---------- USERS ---------- */}
      {tab === "users" && <UsersPanel />}
    </div>
  );
}

/* ============================================================
   PeoplePanel — participantes por puntaje + pronósticos pendientes
   de cada uno en la ventana de 24h (Historia 2)
   ============================================================ */
function PeoplePanel() {
  const Q = useStore();
  const pend = qmPendingStats(Q);
  return (
    <div className="qm-panel">
      <h3>Participantes <span style={{ color: "var(--fg-3)", fontWeight: 400 }}>({Q.db.users.filter((u) => !u.guest).length})</span></h3>
      <p className="ph">Usuarios registrados, ordenados por puntaje. La etiqueta <b style={{ color: "var(--accent)" }}>pendientes</b> indica cuántos de los partidos que inician en las próximas 24h le faltan pronosticar a cada uno.</p>
      <div className="qm-list">
        {Q.standings().map((r) => {
          const miss = pend.perUser[r.user.userId] || 0;
          return (
            <div className="qm-listitem" key={r.user.userId}>
              <Avatar name={r.user.name} size={30} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontWeight: 700 }}>{r.user.name}</div>
                <div style={{ fontSize: 12, color: "var(--fg-3)" }}>{r.user.email} · {r.user.area}</div>
              </div>
              <div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 12 }}>
                {pend.matches.length > 0 && (
                  miss > 0
                    ? <span title={`Le faltan ${miss} de ${pend.matches.length} partidos que inician en <24h`} style={{ fontSize: 12, fontWeight: 700, color: "var(--accent)", background: "var(--bg-3)", padding: "2px 8px", borderRadius: 999, whiteSpace: "nowrap" }}>{miss} pend.</span>
                    : <span title="Está al día con los partidos de las próximas 24h" style={{ fontSize: 12, fontWeight: 700, color: "var(--ok)", whiteSpace: "nowrap" }}>✓ al día</span>
                )}
                <div style={{ textAlign: "right" }}>
                  <b style={{ fontFamily: "var(--font-head)", fontSize: 16 }}>{r.pts}</b>
                  <div style={{ fontSize: 11, color: "var(--fg-3)" }}>#{r.rank}</div>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ============================================================
   EngagementPanel — pronósticos pendientes que vencen en 24h
   (Historia 2). Todo client-side; sin backend nuevo.
   ============================================================ */
function EngagementPanel() {
  const Q = useStore();
  const s = qmPendingStats(Q);
  const pct = Math.round(s.coverage * 100);
  const barColor = pct >= 80 ? "var(--ok)" : pct >= 50 ? "var(--accent)" : "var(--bad, #e5484d)";

  if (s.matches.length === 0) {
    return (
      <div className="qm-panel">
        <h3>Engagement · próximas 24h</h3>
        <p className="ph">No hay partidos pronosticables que inicien en las próximas 24 horas. Nada por vencer ahora mismo.</p>
      </div>
    );
  }

  return (
    <div className="qm-panel">
      <h3>Engagement · próximas 24h</h3>
      <p className="ph">Pronósticos que <b>vencen</b> en las próximas 24h sobre <b>{s.matches.length}</b> partido(s) pronosticable(s) y <b>{s.participants}</b> participante(s) de la quiniela global. Mide qué tan lista está la gente.</p>

      {/* KPIs */}
      <div style={{ display: "flex", gap: 12, flexWrap: "wrap", margin: "8px 0 16px" }}>
        <div style={{ flex: "1 1 160px", background: "var(--bg-3)", borderRadius: 12, padding: "14px 16px" }}>
          <div style={{ fontFamily: "var(--font-head)", fontSize: 34, lineHeight: 1, color: s.totalPending ? "var(--accent)" : "var(--ok)" }}>{s.totalPending}</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", marginTop: 4 }}>pronósticos pendientes</div>
        </div>
        <div style={{ flex: "1 1 160px", background: "var(--bg-3)", borderRadius: 12, padding: "14px 16px" }}>
          <div style={{ fontFamily: "var(--font-head)", fontSize: 34, lineHeight: 1, color: barColor }}>{pct}%</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", marginTop: 4 }}>cobertura ({s.totalPredicted}/{s.totalExpected})</div>
          <div style={{ height: 6, borderRadius: 999, background: "var(--bg-1, rgba(0,0,0,.15))", marginTop: 8, overflow: "hidden" }}>
            <div style={{ height: "100%", width: `${pct}%`, background: barColor }} />
          </div>
        </div>
      </div>

      {/* Desglose por partido */}
      <h3 style={{ marginTop: 4 }}>Por partido</h3>
      <p className="ph">Qué juego inminente está más flojo.</p>
      <div className="qm-list">
        {s.perMatch.slice().sort((a, b) => b.pending - a.pending).map(({ match, predicted, expected, pending }) => {
          const home = Q.team(match.home), away = Q.team(match.away);
          const cov = expected ? Math.round((predicted / expected) * 100) : 100;
          return (
            <div className="qm-listitem" key={match.id}>
              <div style={{ minWidth: 0, flex: 1 }}>
                <div style={{ fontWeight: 700 }}>{home ? `${home.flag} ${home.name}` : match.home} <span style={{ color: "var(--fg-3)" }}>vs</span> {away ? `${away.name} ${away.flag}` : match.away}</div>
                <div style={{ fontSize: 12, color: "var(--fg-3)" }}>{fmtWhen(match.kickoff)} · {predicted}/{expected} pronosticaron ({cov}%)</div>
              </div>
              <div style={{ marginLeft: "auto", textAlign: "right" }}>
                <b style={{ fontFamily: "var(--font-head)", fontSize: 16, color: pending ? "var(--accent)" : "var(--ok)" }}>{pending}</b>
                <div style={{ fontSize: 11, color: "var(--fg-3)" }}>faltan</div>
              </div>
            </div>
          );
        })}
      </div>

      {/* Top usuarios con más pendientes */}
      <div className="qm-divider" />
      <h3>A quién empujar <span style={{ color: "var(--fg-3)", fontWeight: 400 }}>({s.topUsers.length})</span></h3>
      <p className="ph">Participantes con más pronósticos pendientes en la ventana de 24h.</p>
      {s.topUsers.length === 0 ? (
        <div style={{ color: "var(--ok)", fontSize: 13, fontWeight: 600 }}>✓ Todos están al día con los partidos de las próximas 24h.</div>
      ) : (
        <div className="qm-list">
          {s.topUsers.map(({ user, missing }) => (
            <div className="qm-listitem" key={user.userId}>
              <Avatar name={user.name} size={30} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontWeight: 700 }}>{user.name}</div>
                <div style={{ fontSize: 12, color: "var(--fg-3)" }}>{user.email}{user.area ? ` · ${user.area}` : ""}</div>
              </div>
              <div style={{ marginLeft: "auto", textAlign: "right" }}>
                <b style={{ fontFamily: "var(--font-head)", fontSize: 16, color: "var(--accent)" }}>{missing}</b>
                <div style={{ fontSize: 11, color: "var(--fg-3)" }}>de {s.matches.length}</div>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   LeaguesAdminPanel — ligas creadas e invitados externos por
   colaborador (Historia 1). Lee TODAS las ligas (one-time) y agrega.
   ============================================================ */
function LeaguesAdminPanel() {
  const Q = useStore();
  const [data, setData] = useState(null);   // null = cargando, [] vacío posible
  const [error, setError] = useState(false);
  const [busy, setBusy] = useState(false);

  // las ligas guardan el uid (email saneado), no el email real → resolverlo desde users
  const emailByUid = {};
  const knownUid = {};
  Q.db.users.forEach((u) => { emailByUid[u.userId] = u.email; knownUid[u.userId] = true; });

  const load = () => {
    const QL = window.QLeagues;
    if (!QL || !QL.actions.allLeagues) { setError(true); return Promise.resolve(); }
    return QL.actions.allLeagues().then((arr) => {
      if (arr == null) setError(true); else { setError(false); setData(arr); }
    });
  };
  useEffect(() => { let alive = true; load().then(() => { if (!alive) {} }); return () => { alive = false; }; }, []);

  // liga huérfana = su creador no existe en la colección de usuarios (data de prueba / cuenta borrada)
  const orphans = (data || []).filter((l) => !knownUid[l.commissionerUid]);
  const deleteOrphan = async (l) => {
    if (!confirm(`¿Eliminar la liga "${l.name}" (${l.code || "sin código"})?\nCreador sin registro: ${l.commissionerUid}\nMiembros: ${(l.members || []).length}\n\nAcción irreversible.`)) return;
    setBusy(true);
    const r = await window.QLeagues.actions.adminDeleteLeague(l.id, l.code);
    if (r.ok) { await load(); toast(`Liga "${l.name}" eliminada`, "ok"); }
    else toast(r.error || "No se pudo eliminar", "warn");
    setBusy(false);
  };

  if (error) {
    return (
      <div className="qm-panel">
        <h3>Ligas e invitados externos</h3>
        <p className="ph" style={{ color: "var(--bad, #e5484d)" }}>No se pudieron leer las ligas. Requiere permiso de lectura de la colección <code>leagues</code> para administradores en las reglas de Firestore.</p>
      </div>
    );
  }
  if (data == null) {
    return (
      <div className="qm-panel">
        <h3>Ligas e invitados externos</h3>
        <p className="ph">Cargando ligas…</p>
      </div>
    );
  }

  const s = qmLeagueStats(data);

  return (
    <div className="qm-panel">
      <h3>Ligas e invitados externos</h3>
      <p className="ph">Quién creó cuántas ligas y a cuánta <b>gente externa</b> (invitados que se unieron) sumó. Una liga puede mezclar colaboradores internos e invitados externos — acá se separan. Solo ligas activas.</p>

      <div style={{ display: "flex", gap: 12, flexWrap: "wrap", margin: "8px 0 16px" }}>
        <div style={{ flex: "1 1 120px", background: "var(--bg-3)", borderRadius: 12, padding: "14px 16px" }}>
          <div style={{ fontFamily: "var(--font-head)", fontSize: 30, lineHeight: 1 }}>{s.totalLeagues}</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", marginTop: 4 }}>ligas activas</div>
        </div>
        <div style={{ flex: "1 1 120px", background: "var(--bg-3)", borderRadius: 12, padding: "14px 16px" }}>
          <div style={{ fontFamily: "var(--font-head)", fontSize: 30, lineHeight: 1 }}>{s.totalCreators}</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", marginTop: 4 }}>creadores</div>
        </div>
        <div style={{ flex: "1 1 120px", background: "var(--bg-3)", borderRadius: 12, padding: "14px 16px" }}>
          <div style={{ fontFamily: "var(--font-head)", fontSize: 30, lineHeight: 1, color: "var(--accent)" }}>{s.totalExternalUnique}</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", marginTop: 4 }}>externos únicos (global)</div>
        </div>
      </div>

      {s.rows.length === 0 ? (
        <div style={{ color: "var(--fg-3)", fontSize: 13 }}>Todavía no hay ligas creadas.</div>
      ) : (
        <div className="qm-list">
          {/* encabezado de columnas */}
          <div className="qm-listitem" style={{ fontSize: 11, color: "var(--fg-3)", fontWeight: 700, textTransform: "uppercase", letterSpacing: ".04em" }}>
            <div style={{ flex: 1 }}>Colaborador</div>
            <div style={{ width: 64, textAlign: "center" }}>Ligas</div>
            <div style={{ width: 80, textAlign: "center" }}>Internos</div>
            <div style={{ width: 100, textAlign: "center" }} title="Personas externas distintas que se unieron a sus ligas">Externos</div>
          </div>
          {s.rows.map((r) => (
            <div className="qm-listitem" key={r.uid}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontWeight: 700 }}>{r.name}</div>
                <div style={{ fontSize: 12, color: "var(--fg-3)" }}>{emailByUid[r.uid] || r.uid}</div>
              </div>
              <div style={{ width: 64, textAlign: "center", fontWeight: 700 }}>{r.leagues}</div>
              <div style={{ width: 80, textAlign: "center" }} title="Colaboradores internos distintos en sus ligas (sin contarse a sí mismo)">{r.internalUnique}</div>
              <div style={{ width: 100, textAlign: "center" }}>
                <b style={{ fontFamily: "var(--font-head)", fontSize: 16, color: "var(--accent)" }} title="Personas externas distintas">{r.externalUnique}</b>
                {r.externalSeats > r.externalUnique && (
                  <div style={{ fontSize: 11, color: "var(--fg-3)" }} title={`${r.externalSeats} cupos externos en total: ${r.externalSeats - r.externalUnique} persona(s) están en más de una de sus ligas`}>{r.externalSeats - r.externalUnique} en ≥2 ligas</div>
                )}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* ---------- Ligas huérfanas (data de prueba / cuentas borradas) ---------- */}
      <div className="qm-divider" />
      <h3>Ligas huérfanas <span style={{ color: "var(--fg-3)", fontWeight: 400 }}>({orphans.length})</span></h3>
      <p className="ph">Ligas cuyo <b>creador no existe</b> en la lista de usuarios (cuenta nunca registrada o eliminada). Suelen ser data de prueba. Eliminarlas borra la liga y su invitación; sus miembros pierden esa liga.</p>
      {orphans.length === 0 ? (
        <div style={{ color: "var(--ok)", fontSize: 13, fontWeight: 600 }}>✓ No hay ligas huérfanas.</div>
      ) : (
        <div className="qm-list">
          {orphans.map((l) => (
            <div className="qm-listitem" key={l.id} style={{ alignItems: "center", gap: 8, flexWrap: "wrap" }}>
              <div style={{ flex: 1, minWidth: 140 }}>
                <div style={{ fontWeight: 700 }}>{l.name || "(sin nombre)"} {l.active === false && <span className="qm-tagdom" style={{ marginLeft: 4 }}>inactiva</span>}</div>
                <div style={{ fontSize: 12, color: "var(--fg-3)" }}>
                  código {l.code || "—"} · creador sin registro: {l.commissionerUid} · {(l.members || []).length} miembro(s)
                </div>
                {(l.members || []).some((m) => !knownUid[m.uid]) && (
                  <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>
                    miembros sin registro: {(l.members || []).filter((m) => !knownUid[m.uid]).map((m) => m.uid).join(", ")}
                  </div>
                )}
              </div>
              <button className="qm-btn qm-btn-sm qm-btn-ghost" disabled={busy} onClick={() => deleteOrphan(l)} title="Eliminar liga e invitación" style={{ color: "var(--bad, #e5484d)" }}>
                <Icon name="trash" size={14} />Eliminar
              </button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   UsersPanel — admin user management (create / edit / role / delete)
   ============================================================ */
function UsersPanel() {
  const Q = useStore();
  const areas = Q.AREAS;
  const users = Q.db.users.slice().sort((a, b) => a.name.localeCompare(b.name, "es"));

  const [nName, setNName] = useState("");
  const [nEmail, setNEmail] = useState("");
  const [nArea, setNArea] = useState(areas[0]);
  const [nRole, setNRole] = useState("regular");
  const [editId, setEditId] = useState(null);
  const [eName, setEName] = useState("");
  const [eArea, setEArea] = useState("");

  const create = () => {
    if (!nEmail.trim()) { toast("Ingresa un correo"); return; }
    const r = Q.actions.saveUser({ name: nName, email: nEmail, area: nArea, role: nRole });
    if (!r.ok) { toast(r.error); return; }
    toast("Usuario creado", "ok");
    setNName(""); setNEmail(""); setNRole("regular");
  };
  const startEdit = (u) => { setEditId(u.userId); setEName(u.name); setEArea(u.area || ""); };
  const saveEdit = (u) => { Q.actions.saveUser({ userId: u.userId, name: eName, email: u.email, area: eArea, role: u.role }); toast("Usuario actualizado", "ok"); setEditId(null); };
  const del = (u) => { if (confirm(`¿Eliminar a ${u.name}? Se borrarán también sus pronósticos.`)) { Q.actions.deleteUser(u.userId); toast("Usuario eliminado", "ok"); } };

  return (
    <div className="qm-panel">
      <h3>Crear usuario</h3>
      <p className="ph">Crea un participante manualmente y asígnale rol. El correo debe estar autorizado en la <b>Lista blanca</b> para que pueda ingresar.</p>
      <div className="qm-row-inline" style={{ flexWrap: "wrap", gap: 6, marginBottom: 8, alignItems: "flex-end" }}>
        <div><span className="qm-mini">Nombre</span><input className="qm-sel" value={nName} onChange={(e) => setNName(e.target.value)} placeholder="Ej. Ana Pérez" /></div>
        <div><span className="qm-mini">Correo</span><input className="qm-sel" type="email" value={nEmail} onChange={(e) => setNEmail(e.target.value)} placeholder="ana@quantia.pe" /></div>
        <div><span className="qm-mini">Área</span><select className="qm-sel" value={nArea} onChange={(e) => setNArea(e.target.value)}>{areas.map((a) => <option key={a} value={a}>{a}</option>)}</select></div>
        <div><span className="qm-mini">Rol</span><select className="qm-sel" value={nRole} onChange={(e) => setNRole(e.target.value)}><option value="regular">Regular</option><option value="admin">Admin</option></select></div>
        <button className="qm-btn qm-btn-sm qm-btn-dark" onClick={create}><Icon name="plus" size={15} />Crear</button>
      </div>

      <div className="qm-divider" />
      <h3>Usuarios <span style={{ color: "var(--fg-3)", fontWeight: 400 }}>({users.length})</span></h3>
      <p className="ph">Edita nombre/área, cambia el rol o elimina. Los <b>admin</b> entran al panel automáticamente al ingresar (sin la clave en la URL).</p>
      <div className="qm-list">
        {users.map((u) => { const sa = Q.isSuperadmin(u.userId); return (
          <div className="qm-listitem" key={u.userId} style={{ alignItems: "center", gap: 8, flexWrap: "wrap" }}>
            <Avatar name={u.name} size={30} gold={u.role === "admin"} />
            <div style={{ minWidth: 140, flex: 1 }}>
              {editId === u.userId ? (
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  <input className="qm-sel" value={eName} onChange={(e) => setEName(e.target.value)} style={{ maxWidth: 170 }} />
                  <select className="qm-sel" value={eArea} onChange={(e) => setEArea(e.target.value)}><option value="">— área —</option>{areas.map((a) => <option key={a} value={a}>{a}</option>)}</select>
                </div>
              ) : (
                <>
                  <div style={{ fontWeight: 700 }}>{u.name}{u.role === "admin" && <span className="qm-tagdom" style={{ marginLeft: 6 }}>{sa ? "admin principal" : "admin"}</span>}</div>
                  <div style={{ fontSize: 12, color: "var(--fg-3)" }}>{u.email}{u.area ? ` · ${u.area}` : ""}</div>
                </>
              )}
            </div>
            <select className="qm-sel" value={u.role} disabled={sa} title={sa ? "El administrador principal no se puede modificar" : ""} onChange={(e) => Q.actions.setUserRole(u.userId, e.target.value)} style={{ width: 120 }}>
              <option value="regular">Regular</option>
              <option value="admin">Admin</option>
            </select>
            {editId === u.userId ? (
              <>
                <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={() => saveEdit(u)}>Guardar</button>
                <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => setEditId(null)}>Cancelar</button>
              </>
            ) : (
              <>
                <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={() => startEdit(u)}><Icon name="edit" size={14} /></button>
                {sa ? <span title="Administrador principal — protegido" style={{ color: "var(--fg-3)", display: "inline-flex", padding: "0 6px" }}><Icon name="lock" size={14} /></span> : <button className="x" onClick={() => del(u)} title="Eliminar">✕</button>}
              </>
            )}
          </div>
        ); })}
      </div>
    </div>
  );
}

/* ============================================================
   AddMatchModal — popup to add a match (rarely used)
   ============================================================ */
function AddMatchModal({ onClose }) {
  const Q = useStore();
  const codes = Object.keys(Q.TEAMS).filter((c) => c !== "TBD");
  const [mh, setMh] = useState(codes[0] || "");
  const [ma, setMa] = useState(codes[1] || "");
  const [mg, setMg] = useState("A");
  const [mdate, setMdate] = useState("2026-06-21T18:00");

  const add = () => {
    if (mh === ma) { toast("Elige equipos distintos"); return; }
    Q.actions.addMatch({ home: mh, away: ma, group: mg, kickoff: limaInputToTs(mdate) });
    toast("Partido agregado", "ok");
    onClose();
  };

  return (
    <div className="qm-modal-backdrop" onClick={onClose}>
      <div className="qm-modal" onClick={(e) => e.stopPropagation()}>
        <div className="qm-modal-head">
          <b>Agregar partido</b>
          <button className="close" onClick={onClose}><Icon name="x" size={16} /></button>
        </div>
        <p className="ph">Local, visitante, grupo/llave y fecha/hora de inicio en hora de Lima (define el cierre de pronósticos).</p>
        <div className="qm-row-inline" style={{ flexWrap: "wrap", gap: 8, alignItems: "flex-end" }}>
          <div><span className="qm-mini">Local</span><select className="qm-sel" value={mh} onChange={(e) => setMh(e.target.value)}>{codes.map((c) => <option key={c} value={c}>{(Q.TEAMS[c] || {}).flag} {(Q.TEAMS[c] || {}).name}</option>)}</select></div>
          <div><span className="qm-mini">Visitante</span><select className="qm-sel" value={ma} onChange={(e) => setMa(e.target.value)}>{codes.map((c) => <option key={c} value={c}>{(Q.TEAMS[c] || {}).flag} {(Q.TEAMS[c] || {}).name}</option>)}</select></div>
          <div><span className="qm-mini">Grupo/Llave</span><input className="qm-sel" value={mg} onChange={(e) => setMg(e.target.value)} style={{ width: 90 }} /></div>
          <div><span className="qm-mini">Inicio (Lima)</span><input className="qm-sel" type="datetime-local" value={mdate} onChange={(e) => setMdate(e.target.value)} /></div>
        </div>
        <div style={{ display: "flex", gap: 8, marginTop: 16 }}>
          <button className="qm-btn qm-btn-sm qm-btn-primary" onClick={add}><Icon name="plus" size={15} />Agregar</button>
          <button className="qm-btn qm-btn-sm qm-btn-ghost" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { AdminScreen, AdminMatchRow, UsersPanel, AddMatchModal, PeoplePanel, EngagementPanel, LeaguesAdminPanel });
