/* AcquiCup — AcquiBoard: internal (@acquisit.io-only) leaderboards.
 *
 * Four cuts of the same contest: Individual (staff only), and group boards by
 * Office, Department, and Country. The data is produced server-side in
 * bootstrap.routes.js and attached to window.ACQ ONLY for staff (OFFICE_RANK,
 * DEPARTMENT_RANK, COUNTRY_RANK, ACQUISIT_USERS) — a non-staff session never
 * receives it, and app.jsx also gates the route. The whole roster is shown:
 * rostered staff who haven't signed up arrive as `notJoined` rows (0 points,
 * muted + a "Not joined" tag). Each ranking has a rank-over-time bump chart below it,
 * reusing RankChartCard (screens-rank.jsx); these scopes have no STORED rank_history, so
 * the server re-derives the series live (ACQUI_RANK_DATES + ACQUI_*_SERIES, see
 * src/acquiSeries.js). Reads globals from components.jsx (PageHead, Avatar, Movement, Icon,
 * AiBadge, useNarrow) and RankChartCard/BumpChart from screens-rank.jsx. */
const { useState } = React;

// Office keys are short codes server-side; show friendly labels on the board.
const OFFICE_LABEL = { UAE: "United Arab Emirates", KSA: "Saudi Arabia", LEB: "Lebanon", INT: "International" };
// Flag for each office bucket (flagcdn iso); INT has no country → a globe.
const OFFICE_ISO = { UAE: "ae", KSA: "sa", LEB: "lb", INT: null };

// Country flag (flagcdn SVG, same CDN as TeamBadge; CSP allows it). Falls back to a neutral
// plate when the iso is missing or the image 404s — a globe 🌐 when `globe` is set (the
// "International" office bucket), else the first letter of the name.
function CountryFlag({ iso, name, size = 26, globe = false }) {
  const [err, setErr] = useState(false);
  const w = size;
  const h = Math.round(size * 0.72);
  const r = Math.max(3, Math.round(size * 0.16));
  if (err || !iso) {
    return (
      <span className="team-flag fallback" title={name || ""} style={{ width: w, height: h, borderRadius: r,
        background: "var(--surface-2)", border: "1px solid var(--line)", color: "var(--ink-soft)",
        fontSize: Math.round(size * (globe ? 0.52 : 0.42)), display: "inline-flex", alignItems: "center", justifyContent: "center", flex: "none" }}>
        {globe ? "🌐" : (name ? String(name).trim().charAt(0).toUpperCase() : "🌐")}
      </span>
    );
  }
  return <img className="team-flag" src={"https://flagcdn.com/" + iso + ".svg"} alt={name || ""} title={name || ""}
    onError={() => setErr(true)} style={{ width: w, height: h, borderRadius: r, objectFit: "cover", flex: "none",
      boxShadow: "inset 0 0 0 1px rgba(15,45,82,.12)" }} />;
}

// Small muted "Not joined" pill for rostered staff who haven't created an account yet.
function NotJoinedTag() {
  return (
    <span style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: ".02em", textTransform: "uppercase",
      color: "var(--ink-soft)", background: "var(--surface-2)", border: "1px solid var(--line)",
      borderRadius: 999, padding: "2px 8px", whiteSpace: "nowrap" }}>
      Not joined
    </span>
  );
}

// Stable accent colour per group key (for the rank chip + meter), so each office /
// department / nationality reads consistently. Pure hash → hue, like companyColor.
function keyColor(key) {
  const h = String(key || "x").split("").reduce((a, c) => a + c.charCodeAt(0), 0) % 360;
  return `oklch(0.62 0.13 ${h})`;
}

/* ------------------------- time-series (rank over matchdays) -------------------------
 * The AcquiBoard rank-over-time charts reuse the global RankSeriesCard (screens-rank.jsx),
 * the same Top 10 / Top 20 / Me / All / Select selector the main boards use. Server ships
 * ACQUI_RANK_DATES + ACQUI_*_SERIES (rank per matchday); the boards below map their rows to
 * the { id, rank, name } entities + buildEntry that RankSeriesCard consumes. */

/* ----------------------------- group board ----------------------------- */
// rows: [{ key, count, avgTop3, total, settled, correct, exacts, good, bestUser, rank, iso? }]
// badge(g, color) — optional left-chip renderer (Country uses a flag); defaults to the
// colored first-letter tile. Renders as a table (matching the Individual ranking), with a
// rank-over-time chart below it. seriesDict = ACQUI_*_SERIES, highlightKey = the viewer's own
// group (highlighted line), chartNode(g) = optional end-of-line marker (flag).
function GroupBoard({ rows, labelFor, unitSingular, badge, seriesDict, highlightKey, chartNode }) {
  const narrow = useNarrow(820);
  if (!rows || !rows.length) {
    return (
      <div className="card card-pad muted" style={{ padding: "26px 20px", textAlign: "center", fontSize: 13.5 }}>
        No {unitSingular} data yet. Once staff predictions are in (and HR metadata is loaded), the board fills in here.
      </div>
    );
  }
  const sorted = [...rows].sort((a, b) => a.rank - b.rank);
  const head = unitSingular.charAt(0).toUpperCase() + unitSingular.slice(1);

  // Chart: feed every group to RankSeriesCard (Top 10 default; "Me" plots the viewer's own
  // group). `_g` carries the original row so chartNode (flag) can read its iso/key.
  const A = window.ACQ;
  const dates = A.ACQUI_RANK_DATES || [];
  const entities = sorted.map((g) => ({ id: String(g.key), rank: g.rank, name: labelFor(g.key), _g: g }));
  const buildEntry = (e, { highlight }) => ({
    id: e.id,
    label: labelFor(e.id),
    color: highlight ? "var(--brand-navy)" : keyColor(e.id),
    ranks: seriesDict ? seriesDict[e.id] : null,
    highlight,
    node: chartNode ? chartNode(e._g) : undefined,
  });
  const meId = entities.some((e) => e.id === highlightKey) ? highlightKey : null;
  const hasSeries = !!seriesDict && dates.length > 0 &&
    entities.some((e) => Array.isArray(seriesDict[e.id]) && seriesDict[e.id].length);

  return (
    <div className="col gap-20">
      <span className="muted" style={{ fontSize: 13 }}>
        Ranked by the average score of each {unitSingular}'s top 3 players · {sorted.length} {sorted.length === 1 ? unitSingular : unitSingular + "s"} ranked
      </span>
      <StatLegend align="flex-start" />
      <div className="card" style={{ overflow: "hidden" }}>
        <div style={{ overflowX: "auto" }}>
          <table className="lb lb-center" style={{ width: "100%", tableLayout: narrow ? "fixed" : undefined }}>
            <thead><tr>
              <th style={{ width: narrow ? 46 : 54 }}>Rank</th>
              <th>{head}</th>
              <th style={{ width: narrow ? 44 : undefined }}>Players</th>
              <th style={{ width: narrow ? 56 : undefined }}>Avg top 3</th>
              {!narrow && <th>Total</th>}
              <th style={{ width: narrow ? 72 : undefined }}>Accuracy</th>
              {!narrow && <th>Safety</th>}
              {!narrow && <th>Best player</th>}
            </tr></thead>
            <tbody>
              {sorted.map((g) => {
                const color = keyColor(g.key);
                return (
                  <tr key={g.key} className="hov">
                    <td><span className="rank-num">{g.rank}</span></td>
                    <td>
                      <span className="row gap-10" style={{ alignItems: "center", justifyContent: "center", minWidth: 0 }}>
                        {badge ? badge(g, color) : (
                          <div style={{ width: 30, height: 30, borderRadius: 9, background: color, color: "#fff", flex: "none",
                            display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 800, fontSize: 13,
                            boxShadow: "inset 0 0 0 1px rgba(255,255,255,.14), 0 1px 2px rgba(15,45,82,.18)" }}>
                            {String(labelFor(g.key)).trim().charAt(0).toUpperCase()}
                          </div>
                        )}
                        <span style={{ fontWeight: 700, fontSize: 14, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{labelFor(g.key)}</span>
                      </span>
                    </td>
                    <td className="mono">{g.count}</td>
                    <td><span className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{g.avgTop3}</span></td>
                    {!narrow && <td className="mono">{g.total}</td>}
                    <td><AccuracyBar exacts={g.exacts} good={g.good} settled={g.settled} width={narrow ? 54 : 120} showPct={!narrow} align="center" /></td>
                    {!narrow && <td><SafetyBar high={g.riskLow} med={g.riskMed} low={g.riskHigh} width={120} align="center" /></td>}
                    {!narrow && <td><span style={{ fontSize: 13 }}>{g.bestUser ? `${userName(g.bestUser)} (${g.bestUser.points})` : "-"}</span></td>}
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
      {hasSeries && (
        <RankSeriesCard title={head + " ranking over time"}
          sub={"Daily rank of each " + unitSingular + " across the tournament" + (highlightKey ? ". Your " + unitSingular + " is highlighted." : ".")}
          dates={dates} entities={entities} buildEntry={buildEntry} meId={meId} kindLabel={unitSingular + "s"} />
      )}
    </div>
  );
}

/* ----------------------------- individual (staff) ----------------------------- */
function AcquiIndividual({ rows }) {
  const narrow = useNarrow(820);
  if (!rows || !rows.length) {
    return (
      <div className="card card-pad muted" style={{ padding: "26px 20px", textAlign: "center", fontSize: 13.5 }}>
        No Acquisit players yet. Staff who join the contest will show up here.
      </div>
    );
  }
  const sorted = [...rows].sort((a, b) => a.acquiRank - b.acquiRank);
  const joined = sorted.filter((u) => !u.notJoined).length;

  // Chart: feed every staff player to RankSeriesCard (Top 10 default; "Me" plots your own
  // line). `_u` carries the original row for the label/flag node.
  const A = window.ACQ;
  const series = A.ACQUI_INDIVIDUAL_SERIES || {};
  const dates = A.ACQUI_RANK_DATES || [];
  const youRow = sorted.find((u) => u.you);
  const entities = sorted.map((u) => ({ id: u.id, rank: u.acquiRank, name: userName(u), _u: u }));
  const buildEntry = (e, { highlight }) => ({
    id: e.id,
    label: e._u.you ? "You" : userName(e._u),
    color: e._u.you ? "var(--brand-navy)" : keyColor(e._u.countryName || e._u.id),
    ranks: series[e.id],
    highlight,
    node: <CountryFlag iso={e._u.countryIso} name={e._u.countryName} size={highlight ? 22 : 18} />,
  });
  const hasSeries = dates.length > 0 &&
    entities.some((e) => Array.isArray(series[e.id]) && series[e.id].length);

  return (
    <div className="col gap-20">
      <span className="muted" style={{ fontSize: 13 }}>{sorted.length} Acquisit staff · {joined} joined · ranked among staff only</span>
      <StatLegend align="flex-start" />
      <div className="card" style={{ overflow: "hidden" }}>
        <div style={{ overflowX: "auto" }}>
          <table className="lb lb-center" style={{ width: "100%", tableLayout: narrow ? "fixed" : undefined }}>
            <thead><tr>
              <th style={{ width: narrow ? 42 : 54 }}>Rank</th>
              <th>Player</th>
              <th style={{ width: narrow ? 40 : undefined }}>Country</th>
              {!narrow && <th>Office</th>}
              {!narrow && <th>Department</th>}
              <th style={{ width: narrow ? 50 : undefined }}>Points</th>
              <th style={{ width: narrow ? 72 : undefined }}>Accuracy</th>
              {!narrow && <th>Safety</th>}
              {!narrow && <th>Overall</th>}
            </tr></thead>
            <tbody>
              {sorted.map((u) => (
                <tr key={u.id} className={"hov " + (u.you ? "me-row" : "")} style={u.notJoined ? { opacity: 0.6 } : null}>
                  <td><span className="rank-num">{u.acquiRank}</span></td>
                  <td>
                    <span className="row gap-8" style={{ alignItems: "center", justifyContent: "center", minWidth: 0 }}>
                      <span style={{ fontWeight: 700, fontSize: 14, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{userName(u)}{u.you ? " · you" : ""}</span>
                      {u.isAi && <AiBadge />}
                      {u.notJoined && <NotJoinedTag />}
                    </span>
                  </td>
                  <td>
                    <span className="row" style={{ justifyContent: "center" }}>
                      <CountryFlag iso={u.countryIso} name={u.countryName} size={26} />
                    </span>
                  </td>
                  {!narrow && <td><span style={{ fontSize: 13.5 }}>{u.workLocation ? (OFFICE_LABEL[String(u.workLocation).toUpperCase()] || u.workLocation) : "-"}</span></td>}
                  {!narrow && <td><span style={{ fontSize: 13.5 }}>{u.department || "-"}</span></td>}
                  <td><span className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{u.points}</span></td>
                  <td><AccuracyBar exacts={u.exacts} good={u.good} settled={u.settled} width={narrow ? 54 : 120} showPct={!narrow} align="center" /></td>
                  {!narrow && <td><SafetyBar high={u.riskLow} med={u.riskMed} low={u.riskHigh} width={120} align="center" /></td>}
                  {!narrow && <td className="mono"><span className="muted">{u.rank ? "#" + u.rank : "-"}</span></td>}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      {hasSeries && (
        <RankSeriesCard title="Player ranking over time"
          sub="Daily rank among staff across the tournament. Your line is highlighted."
          dates={dates} entities={entities} buildEntry={buildEntry}
          meId={youRow ? youRow.id : null} kindLabel="players" />
      )}
    </div>
  );
}

/* ----------------------------- page ----------------------------- */
const ACQUIBOARD_TABS = [
  { id: "individual", label: "Individual" },
  { id: "office", label: "Office" },
  { id: "department", label: "Department" },
  { id: "country", label: "Country" },
];

function AcquiBoard({ store, go }) {
  const A = window.ACQ;
  const [tab, setTab] = useState("individual");
  const narrow = useNarrow(560);

  const offices = A.OFFICE_RANK || [];
  const departments = A.DEPARTMENT_RANK || [];
  const countries = A.COUNTRY_RANK || [];
  const individuals = A.ACQUISIT_USERS || [];
  const me = A.ACQUI_ME || {};

  let body;
  if (tab === "office") body = <GroupBoard rows={offices} labelFor={(k) => OFFICE_LABEL[k] || k} unitSingular="office"
    badge={(g) => <CountryFlag iso={OFFICE_ISO[g.key]} name={OFFICE_LABEL[g.key] || g.key} globe={g.key === "INT"} size={28} />}
    seriesDict={A.ACQUI_OFFICE_SERIES} highlightKey={me.office}
    chartNode={(g) => <CountryFlag iso={OFFICE_ISO[g.key]} name={OFFICE_LABEL[g.key] || g.key} globe={g.key === "INT"} size={18} />} />;
  else if (tab === "department") body = <GroupBoard rows={departments} labelFor={(k) => k} unitSingular="department"
    badge={() => null} seriesDict={A.ACQUI_DEPARTMENT_SERIES} highlightKey={me.department} />;
  else if (tab === "country") body = <GroupBoard rows={countries} labelFor={(k) => k} unitSingular="country"
    badge={(g) => <CountryFlag iso={g.iso} name={g.key} size={28} />}
    seriesDict={A.ACQUI_COUNTRY_SERIES} highlightKey={me.country}
    chartNode={(g) => <CountryFlag iso={g.iso} name={g.key} size={18} />} />;
  else body = <AcquiIndividual rows={individuals} />;

  return (
    <div className="col gap-24">
      <PageHead eyebrow="Acquisit only" title="AcquiBoard"
        sub="The internal Acquisit standings, ranked individually and by office, department, and country. The whole team is listed (players who haven't joined yet appear muted). Group score is each group's top-3 average, so a big team can't brute-force the table." />

      <div className="row gap-6" role="tablist" aria-label="AcquiBoard view" style={{
        background: "var(--surface-2)", padding: 4, borderRadius: 12, border: "1px solid var(--line)",
        width: narrow ? "100%" : "fit-content", maxWidth: "100%" }}>
        {ACQUIBOARD_TABS.map((t) => {
          const on = tab === t.id;
          return (
            <button key={t.id} role="tab" aria-selected={on} onClick={() => setTab(t.id)}
              className="pointer" style={{ border: "none", cursor: "pointer", borderRadius: 9,
                padding: narrow ? "8px 4px" : "8px 16px", flex: narrow ? "1 1 0" : undefined, minWidth: 0,
                fontFamily: "var(--ff)", fontSize: narrow ? 12.5 : 13.5, fontWeight: 700,
                whiteSpace: "nowrap", textOverflow: "ellipsis", overflow: "hidden",
                background: on ? "var(--surface)" : "transparent", color: on ? "var(--accent)" : "var(--ink-2)",
                boxShadow: on ? "0 1px 2px rgba(15,45,82,.14)" : "none" }}>
              {t.label}
            </button>
          );
        })}
      </div>

      {body}
    </div>
  );
}

Object.assign(window, { AcquiBoard });
