// Consistency (ACCoRD) scatter. Wraps window.ScatterChart (from dtbench-chart.jsx)
// with two toggles:
//   X axis  — Release date | Text Arena
//   Metric  — Linear | Linear (norm.) | RMS | RMS (norm.)
// All metrics are inconsistency measures: LOWER = more consistent.
const { useState: useConsState } = React;

const CONS_METRICS = [
  { key: "lin",  label: "Linear",        yl: "Mean linear margin" },
  { key: "linN", label: "Linear (norm.)", yl: "Average constraint violation (normalized)" },
  { key: "rms",  label: "RMS",           yl: "RMS violation (rdms)" },
  { key: "rmsN", label: "RMS (norm.)",   yl: "RMS violation (normalized)" },
];
const CONS_XMODES = [
  { key: "date", label: "Release date" },
  { key: "elo",  label: "Text Arena" },
];
const CONS_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

function consFrac(dateStr) {
  const [y, m, d] = dateStr.split("-").map(Number);
  return y + (m - 1) / 12 + ((d || 1) - 1) / 365;
}
function consFracLabel(v) {
  const y = Math.floor(v + 1e-6);
  let m = Math.round((v - y) * 12);
  if (m > 11) m = 11;
  if (m < 0) m = 0;
  return `${CONS_MONTHS[m]} ${y}`;
}
function consNiceMax(max) {
  const step = max <= 0.3 ? 0.05 : max <= 0.6 ? 0.1 : 0.15;
  return { step, top: Math.ceil((max * 1.06) / step) * step };
}

function ConsistencyChart() {
  const [metric, setMetric] = useConsState("linN");
  const [xmode, setXmode] = useConsState("elo");

  const rows = window.CONS_RESULTS || [];
  const met = CONS_METRICS.find((m) => m.key === metric);

  const data = rows
    .map((r) => ({
      name: r.name,
      org: r.org,
      y: r[metric],
      x: xmode === "date" ? consFrac(r.released) : r.elo,
    }))
    .filter((p) => p.x != null && p.y != null && !Number.isNaN(p.x) && !Number.isNaN(p.y));

  // y domain. The normalized-linear metric has a meaningful "random (uniform)
  // guessing" reference at 0.73 (worse than every real model); when it's shown,
  // make sure the axis reaches it.
  const showRandom = metric === "linN";
  const RANDOM_Y = 0.73;
  const ys = data.map((p) => p.y);
  const ymaxData = Math.max(ys.length ? Math.max(...ys) : 1, showRandom ? RANDOM_Y : 0);
  const { step: yStep, top: yTop } = consNiceMax(ymaxData);
  const yTicks = [];
  for (let v = 0; v <= yTop + 1e-9; v += yStep) yTicks.push(Number(v.toFixed(4)));

  // x domain + ticks
  let xMin, xMax, xTicks, xFmt, xTipFmt, xLabel;
  if (xmode === "date") {
    const xs = data.map((p) => p.x);
    xMin = Math.floor(Math.min(...xs) * 4) / 4 - 0.05;
    xMax = Math.ceil(Math.max(...xs) * 4) / 4 + 0.05;
    xTicks = [];
    for (let y = Math.ceil(xMin); y <= Math.floor(xMax); y++) xTicks.push(y);
    xFmt = (v) => String(v);
    xTipFmt = (v) => consFracLabel(v);
    xLabel = "Release date";
  } else {
    xMin = 1000; xMax = 1520;
    xTicks = [1050, 1150, 1250, 1350, 1450];
    xFmt = (v) => v;
    xTipFmt = (v) => Math.round(v);
    xLabel = "Text Arena";
  }

  const seg = (active) => ({
    fontFamily: "var(--font-mono)",
    fontSize: "12px",
    letterSpacing: "0.03em",
    padding: "6px 12px",
    cursor: "pointer",
    border: "1px solid " + (active ? "var(--teal)" : "var(--rule-strong)"),
    background: active ? "var(--teal)" : "transparent",
    color: active ? "#fff" : "var(--ink-2)",
    borderRadius: "6px",
    transition: "all 0.14s",
  });
  const groupLabel = {
    fontFamily: "var(--font-mono)",
    fontSize: "11px",
    letterSpacing: "0.1em",
    textTransform: "uppercase",
    color: "var(--ink-3)",
    marginRight: "4px",
    alignSelf: "center",
  };

  return (
    <div>
      <window.ScatterChart
        id="cons-chart"
        title={xmode === "date" ? "Inconsistency vs. release date" : "Inconsistency vs. Text Arena"}
        data={data}
        orgOrder={window.CONS_ORGS || []}
        xMin={xMin} xMax={xMax} yMin={0} yMax={yTop}
        xTicks={xTicks} yTicks={yTicks}
        xLabel={xLabel} yLabel={met.yl}
        xFmt={xFmt} yFmt={(v) => v.toFixed(2)}
        xTipFmt={xTipFmt} yTipFmt={(v) => v.toFixed(3)}
        showR rPos="tr"
        refLine={showRandom ? [{ y: RANDOM_Y, label: "Random (uniform) guessing", dash: "2 6", labelBelow: true }] : undefined}
        caption={<>
          {xmode === "date"
            ? "More recent models are more consistent. "
            : "More capable models are more consistent. "}
          The y-axis shows mean absolute constraint violation for each model, normalized by average absolute difference between unrelated probabilities.{xmode === "date" ? " " : " The x-axis is the model's Text Arena Score. "}
          <a className="text-link" href="https://arena.ai/leaderboard/text" target="_blank" rel="noopener">Text Arena</a> ranks AI models based on blind human preference votes. The shaded band is the trend line's 95% Confidence Interval.
        </>}
      />
    </div>
  );
}

window.ConsistencyChart = ConsistencyChart;

// 60-sample inconsistency vs. Text Arena. Uses window.CONS_ARENA_RESULTS (from
// consistency-arena-data.js / data/consistency_vs_arena.csv). Unlike the main
// scatter (a single sample per proposition), each model here is measured from 60
// samples per proposition, which removes most within-proposition stochasticity.
// Models were run at differing effort, so each point's hover label carries its
// effort level.
function ConsistencyArenaChart() {
  const rows = window.CONS_ARENA_RESULTS || [];
  // Label the effort level on hover, but skip budget-style notes (e.g. "budget-10k")
  // — those describe a token budget, not an effort level.
  const data = rows
    .map((r) => ({
      name: r.effort && !/^budget/i.test(r.effort) ? `${r.name} · ${r.effort}` : r.name,
      org: r.org,
      x: r.elo,
      y: r.incon,
    }))
    .filter((p) => p.x != null && p.y != null && !Number.isNaN(p.x) && !Number.isNaN(p.y));

  const ys = data.map((p) => p.y);
  const { step: yStep, top: yTop } = consNiceMax(ys.length ? Math.max(...ys) : 0.5);
  const yTicks = [];
  for (let v = 0; v <= yTop + 1e-9; v += yStep) yTicks.push(Number(v.toFixed(4)));

  return (
    <div>
      <window.ScatterChart
        id="cons-arena"
        title="Inconsistency measured with more samples"
        data={data}
        orgOrder={window.CONS_ARENA_ORGS || []}
        xMin={1050} xMax={1520} yMin={0} yMax={yTop}
        xTicks={[1100, 1200, 1300, 1400, 1500]} yTicks={yTicks}
        xLabel="Text Arena" yLabel="Average constraint violation (normalized)"
        xFmt={(v) => v} yFmt={(v) => v.toFixed(2)}
        xTipFmt={(v) => Math.round(v)} yTipFmt={(v) => v.toFixed(3)}
        showR rPos="tr"
        caption={<>
          Inconsistency measured using 60 samples for various models (note that many of them are <em>not</em> on maximum effort and so are not directly comparable with the CRI results, which use maximum effort). This removes most of the effects of within-proposition stochasticity. Each point's effort level is shown on hover. The x-axis is the model's <a className="text-link" href="https://arena.ai/leaderboard/text" target="_blank" rel="noopener">Text Arena</a> Score; the shaded band is the trend line's 95% Confidence Interval.
        </>}
      />
    </div>
  );
}

window.ConsistencyArenaChart = ConsistencyArenaChart;

// Linear (norm.) vs RMS (norm.) — shows how the linear estimator relates to the
// RMS estimator across models. Static scatter.
function ConsBiasChart() {
  const data = (window.CONS_RESULTS || [])
    .filter((r) => r.linN != null && r.rmsN != null)
    .map((r) => ({ name: r.name, org: r.org, x: r.rmsN, y: r.linN }));
  return (
    <window.ScatterChart
      id="cons-bias"
      title="Linear vs. RMS violation"
      data={data}
      orgOrder={window.CONS_ORGS || []}
      xMin={0.15} xMax={0.75} yMin={0.05} yMax={0.55}
      xTicks={[0.2, 0.3, 0.4, 0.5, 0.6, 0.7]} yTicks={[0.1, 0.2, 0.3, 0.4, 0.5]}
      xLabel="RMS violation (normalized)" yLabel="Average constraint violation (normalized)"
      xFmt={(v) => v.toFixed(1)} yFmt={(v) => v.toFixed(1)}
      xTipFmt={(v) => v.toFixed(3)} yTipFmt={(v) => v.toFixed(3)}
      showR
      caption="normalized absolute violation against normalized RMS violation"
    />
  );
}

window.ConsBiasChart = ConsBiasChart;

// Partial ACCoRD leaderboard — most consistent models (ranked by normalized linear
// margin, ascending). Shows both normalized metrics; lower = more consistent.
function ConsLeaderboard({ limit }) {
  const rows = (window.CONS_RESULTS || [])
    .filter((r) => r.linN != null)
    .slice()
    .sort((a, b) => a.linN - b.linN);
  const shown = rows.slice(0, limit || 15);
  const colorFor = (org) => (window.DT_ORG_COLORS_PUBLIC && window.DT_ORG_COLORS_PUBLIC[org]) || "#9a938a";

  const cell = { padding: "10px 14px", fontFamily: "var(--font-mono)", fontSize: "13px", color: "var(--ink)", borderBottom: "1px solid var(--rule)" };
  const head = { ...cell, fontSize: "11px", letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--ink-3)", fontWeight: 500, borderBottom: "1px solid var(--rule-strong)" };

  return (
    <div>
      <table style={{ width: "100%", borderCollapse: "collapse" }}>
        <thead>
          <tr>
            <th style={{ ...head, textAlign: "right", width: "48px" }}>#</th>
            <th style={{ ...head, textAlign: "left" }}>Model</th>
            <th style={{ ...head, textAlign: "right" }}>Linear (norm.)</th>
            <th style={{ ...head, textAlign: "right" }}>RMS (norm.)</th>
          </tr>
        </thead>
        <tbody>
          {shown.map((r, i) => (
            <tr key={r.name}>
              <td style={{ ...cell, textAlign: "right", color: "var(--ink-3)" }}>{i + 1}</td>
              <td style={{ ...cell, fontFamily: "var(--font-body)", fontSize: "16px" }}>
                <span style={{ display: "inline-block", width: "9px", height: "9px", borderRadius: "50%", background: colorFor(r.org), marginRight: "10px", verticalAlign: "middle" }} />
                {r.name}
              </td>
              <td style={{ ...cell, textAlign: "right", fontVariantNumeric: "tabular-nums" }}>{r.linN.toFixed(3)}</td>
              <td style={{ ...cell, textAlign: "right", fontVariantNumeric: "tabular-nums", color: "var(--ink-2)" }}>{r.rmsN != null ? r.rmsN.toFixed(3) : "—"}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

window.ConsLeaderboard = ConsLeaderboard;
