// Subnet economics tile + sector coverage heatmap.
// Both components fetch on mount, render gracefully when central is
// unreachable (hide the tile vs. show stub).

const CENTRAL = (window.__ENV && window.__ENV.CENTRAL_API_URL) || '';

function fmtBig(n) {
  if (n === null || n === undefined || Number.isNaN(n)) return '—';
  const v = Number(n);
  if (v >= 1e9) return (v / 1e9).toFixed(2) + 'B';
  if (v >= 1e6) return (v / 1e6).toFixed(2) + 'M';
  if (v >= 1e3) return (v / 1e3).toFixed(1) + 'K';
  return v.toLocaleString();
}

function fmtInt(n) {
  if (n === null || n === undefined || Number.isNaN(n)) return '—';
  return Number(n).toLocaleString();
}

// ─── Subnet economics + graph counts, combined into one 6-card row ───
window.SubnetEconomics = function SubnetEconomics() {
  const [subnet, setSubnet] = React.useState(null);
  const [graph, setGraph] = React.useState(null);
  const [loaded, setLoaded] = React.useState(false);

  React.useEffect(() => {
    if (!CENTRAL) { setLoaded(true); return; }
    let alive = true;
    Promise.allSettled([
      fetch(CENTRAL.replace(/\/$/, '') + '/api/v1/stats/subnet').then((r) => r.ok ? r.json() : null),
      fetch(CENTRAL.replace(/\/$/, '') + '/health').then((r) => r.ok ? r.json() : null),
    ]).then(([s, h]) => {
      if (!alive) return;
      setSubnet(s.status === 'fulfilled' ? s.value : null);
      setGraph(h.status === 'fulfilled' ? h.value : null);
      setLoaded(true);
    });
    return () => { alive = false; };
  }, []);

  const subnetOk = subnet && subnet.available !== false;
  const validators = subnetOk ? subnet.validators : null;
  const miners = subnetOk ? subnet.miners : null;
  const alphaStake = subnetOk ? subnet.total_alpha_stake : null;
  const entities = graph && typeof graph.entities === 'number' ? graph.entities : null;
  const relationships = graph && typeof graph.relationships === 'number' ? graph.relationships : null;
  const facts = graph && typeof graph.facts === 'number' ? graph.facts : null;

  return (
    <section className="section" style={{paddingTop:96,paddingBottom:0,borderTop:0}}>
      <div className="container">
        <h2>Subnet 43 · Live state</h2>
        <p style={{color:'var(--fg-dim)',marginTop:10,maxWidth:620,fontSize:15,lineHeight:1.55}}>
          On-chain validators and miners, plus the canonical graph counts they're contributing to. Refreshed on every page load.
        </p>

        <div className="tile-row">
          <div className="tile">
            <div className="tile-label">Validators</div>
            <div className="tile-value">{loaded ? fmtInt(validators) : '…'}</div>
            <div className="tile-sub">on netuid 43</div>
          </div>
          <div className="tile">
            <div className="tile-label">Miners</div>
            <div className="tile-value">{loaded ? fmtInt(miners) : '…'}</div>
            <div className="tile-sub">registered UIDs</div>
          </div>
          <div className="tile">
            <div className="tile-label">Alpha stake</div>
            <div className="tile-value">{loaded ? fmtBig(alphaStake) : '…'}</div>
            <div className="tile-sub">total bonded</div>
          </div>
          <div className="tile">
            <div className="tile-label">Entities</div>
            <div className="tile-value">{loaded ? fmtBig(entities) : '…'}</div>
            <div className="tile-sub">in the graph</div>
          </div>
          <div className="tile">
            <div className="tile-label">Relationships</div>
            <div className="tile-value">{loaded ? fmtBig(relationships) : '…'}</div>
            <div className="tile-sub">typed edges</div>
          </div>
          <div className="tile">
            <div className="tile-label">Facts</div>
            <div className="tile-value">{loaded ? fmtBig(facts) : '…'}</div>
            <div className="tile-sub">sourced claims</div>
          </div>
        </div>
      </div>
    </section>
  );
};

// ─── Sector coverage — squarified treemap ───
// Tile area is proportional to entity count. Tiles are absolutely positioned;
// the squarify algorithm packs them into the container rectangle with no
// gaps. Tile orientation is chosen each step to minimise worst aspect ratio,
// so the tiles tend toward squares rather than long thin strips.
window.SectorHeatmap = function SectorHeatmap() {
  const [data, setData] = React.useState(null);
  const [loaded, setLoaded] = React.useState(false);
  const containerRef = React.useRef(null);
  const [size, setSize] = React.useState({ w: 0, h: 480 });

  // Fetch
  React.useEffect(() => {
    if (!CENTRAL) { setLoaded(true); return; }
    let alive = true;
    fetch(CENTRAL.replace(/\/$/, '') + '/api/v1/stats/coverage')
      .then((r) => (r.ok ? r.json() : null))
      .then((d) => {
        if (!alive) return;
        setData(d);
        setLoaded(true);
      })
      .catch(() => { if (alive) { setData(null); setLoaded(true); } });
    return () => { alive = false; };
  }, []);

  // Track container width so the treemap reflows on window resize.
  React.useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const measure = () => {
      const rect = el.getBoundingClientRect();
      setSize({ w: rect.width, h: rect.height || 480 });
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, [loaded]);

  const bySector = (data && data.entities_by_sector) || {};
  const entries = Object.entries(bySector)
    .map(([k, v]) => ({ name: prettySector(k), value: Number(v) || 0 }))
    .filter((e) => e.value > 0)
    .sort((a, b) => b.value - a.value);

  const max = entries.length ? entries[0].value : 0;
  const tiles = (size.w > 0 && entries.length > 0)
    ? squarifyTreemap(entries, 0, 0, size.w, size.h)
    : [];

  return (
    <section className="section" style={{paddingTop:16,paddingBottom:80,borderTop:0}}>
      <div className="container">
        <h2>Sector coverage</h2>
        <p style={{color:'var(--fg-dim)',marginTop:10,maxWidth:620,fontSize:15,lineHeight:1.55}}>
          Entities indexed per sector. Tile area scales with the count — bigger tile, deeper coverage. Underserved sectors show up as small tiles and are good targets for new miners.
        </p>

        {!loaded && (
          <div style={{padding:'48px 24px',textAlign:'center',color:'var(--fg-mute)'}}>Loading…</div>
        )}
        {loaded && entries.length === 0 && (
          <div style={{padding:'48px 24px',textAlign:'center',color:'var(--fg-mute)'}}>
            No sector data yet.
          </div>
        )}
        <div ref={containerRef} className="heatmap" style={{display: loaded && entries.length > 0 ? 'block' : 'none'}}>
          {tiles.map((t) => {
            const intensity = max ? Math.max(0.06, Math.min(0.35, t.value / max * 0.35)) : 0.06;
            return (
              <div
                key={t.name}
                className="heatmap-cell"
                style={{
                  left: t.x + 'px',
                  top: t.y + 'px',
                  width: t.w + 'px',
                  height: t.h + 'px',
                  background: 'rgba(124,124,255,' + intensity.toFixed(3) + ')',
                }}
                title={t.name + ' · ' + t.value.toLocaleString() + ' entities'}
              >
                <div className="sect-count">{fmtBig(t.value)}</div>
                <div className="sect-name">{t.name}</div>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
};

// Squarified treemap (Bruls, Huijing, van Wijk 2000). Lays out `items`
// (objects with .value, already sorted descending) into a rectangle
// [x, y, w, h] and returns a list of placed tiles { x, y, w, h, ...item }.
function squarifyTreemap(items, x, y, w, h) {
  const total = items.reduce((s, i) => s + i.value, 0);
  if (total <= 0 || w <= 0 || h <= 0) return [];
  const scale = (w * h) / total;
  const scaled = items.map((it) => ({ ...it, area: it.value * scale }));
  const out = [];
  layoutSquarify(scaled.slice(), [], { x, y, w, h }, out);
  return out;
}

function layoutSquarify(remaining, row, rect, out) {
  if (remaining.length === 0) {
    if (row.length) placeRow(row, rect, out);
    return;
  }
  const side = Math.min(rect.w, rect.h);
  const item = remaining[0];
  const newRow = row.concat([item]);
  const oldWorst = row.length ? worstAspect(row, side) : Infinity;
  const newWorst = worstAspect(newRow, side);
  if (row.length === 0 || newWorst <= oldWorst) {
    layoutSquarify(remaining.slice(1), newRow, rect, out);
  } else {
    const placed = placeRow(row, rect, out);
    layoutSquarify(remaining, [], placed, out);
  }
}

// Lay a finished row out along the short side of `rect`, then return the
// remaining rectangle for the next row.
function placeRow(row, rect, out) {
  const sum = row.reduce((s, i) => s + i.area, 0);
  if (sum <= 0) return rect;
  if (rect.w >= rect.h) {
    // Vertical strip on the left, tiles stacked top-to-bottom.
    const stripW = sum / rect.h;
    let dy = 0;
    for (const it of row) {
      const tileH = (it.area / sum) * rect.h;
      out.push({ ...it, x: rect.x, y: rect.y + dy, w: stripW, h: tileH });
      dy += tileH;
    }
    return { x: rect.x + stripW, y: rect.y, w: rect.w - stripW, h: rect.h };
  } else {
    // Horizontal strip on the top, tiles arranged left-to-right.
    const stripH = sum / rect.w;
    let dx = 0;
    for (const it of row) {
      const tileW = (it.area / sum) * rect.w;
      out.push({ ...it, x: rect.x + dx, y: rect.y, w: tileW, h: stripH });
      dx += tileW;
    }
    return { x: rect.x, y: rect.y + stripH, w: rect.w, h: rect.h - stripH };
  }
}

function worstAspect(row, side) {
  let sum = 0;
  let rmin = Infinity, rmax = -Infinity;
  for (const it of row) {
    sum += it.area;
    if (it.area < rmin) rmin = it.area;
    if (it.area > rmax) rmax = it.area;
  }
  if (sum <= 0) return Infinity;
  const s2 = side * side;
  const sum2 = sum * sum;
  return Math.max((s2 * rmax) / sum2, sum2 / (s2 * rmin));
}

// Convert snake_case_sector keys ("cloud_computing") into display labels
// ("Cloud Computing"). Untouched if already prettified.
function prettySector(s) {
  if (!s) return '—';
  return String(s)
    .replace(/_/g, ' ')
    .replace(/\b\w/g, (c) => c.toUpperCase());
}
