#!/usr/bin/env node
// REVM Engine Benchmark CLI — v0.2.0 RwLock vs v0.3.0 Lock-Free
// Usage: node bench-cli.js

const TOTAL = 300;
const TICK_MS = 12;

// ── ANSI escape codes ──
const ESC = '\x1b[';
const RESET = `${ESC}0m`;
const BOLD = `${ESC}1m`;
const DIM = `${ESC}2m`;
const BLINK = `${ESC}5m`;
const BLACK = `${ESC}30m`;
const RED = `${ESC}31m`;
const GREEN = `${ESC}32m`;
const YELLOW = `${ESC}33m`;
const BLUE = `${ESC}34m`;
const MAGENTA = `${ESC}35m`;
const CYAN = `${ESC}36m`;
const WHITE = `${ESC}37m`;
const GRAY = `${ESC}90m`;
const BR_RED = `${ESC}91m`;
const BR_GREEN = `${ESC}92m`;
const BR_YELLOW = `${ESC}93m`;
const BR_CYAN = `${ESC}96m`;
const BR_WHITE = `${ESC}97m`;
const BG_BLACK = `${ESC}40m`;
const BG_RED = `${ESC}41m`;
const BG_GREEN = `${ESC}42m`;
const BG_YELLOW = `${ESC}43m`;
const BG_ORANGE = `${ESC}48;5;208m`;
const BG_DARK = `${ESC}48;5;233m`;
const BG_DARKER = `${ESC}48;5;232m`;
const BG_DARKGREEN = `${ESC}48;5;22m`;
const BG_DARKORANGE = `${ESC}48;5;94m`;
const FG_ORANGE = `${ESC}38;5;208m`;
const FG_DARKORANGE = `${ESC}38;5;130m`;
const FG_DARKGREEN = `${ESC}38;5;28m`;
const FG_GRAY = `${ESC}38;5;240m`;
const FG_DARKGRAY = `${ESC}38;5;236m`;
const FG_MIDGRAY = `${ESC}38;5;244m`;
const HIDE_CURSOR = `${ESC}?25l`;
const SHOW_CURSOR = `${ESC}?25h`;
const CLEAR = `${ESC}2J${ESC}H`;
const SAVE_POS = `${ESC}s`;
const RESTORE_POS = `${ESC}u`;

function moveTo(row, col) { return `${ESC}${row};${col}H`; }

// ── Box drawing ──
const BOX = {
  tl: '┌', tr: '┐', bl: '└', br: '┘',
  h: '─', v: '│', lj: '├', rj: '┤',
  tj: '┬', bj: '┴', cross: '┼',
  dtl: '╔', dtr: '╗', dbl: '╚', dbr: '╝',
  dh: '═', dv: '║', dlj: '╠', drj: '╣',
};

// ── Terminal size ──
const cols = Math.max(process.stdout.columns || 80, 80);
const rows = Math.max(process.stdout.rows || 40, 40);
const panelW = Math.floor((cols - 3) / 2);
const leftStart = 1;
const rightStart = panelW + 2;

// ── ACO Engine (same as web version) ──
class AcoEngine {
  constructor(opts) {
    this.alpha = opts.alpha || 1.2;
    this.beta = opts.beta || 3.0;
    this.evapRate = opts.evapRate || 0.25;
    this.antCount = opts.antCount || 64;
    this.maxIter = opts.maxIter || 50;
    this.Q = 100;
    this.contentionChance = opts.contentionChance || 0;
    this.contentionDelay = opts.contentionDelay || 0;
    this.adaptiveEvap = opts.adaptiveEvap || false;
    this.nodeCount = 0;
    this.pheromone = null;
    this.distance = null;
    this.adjacency = null;
    this.latencyWindow = [];
  }

  init(n) {
    this.nodeCount = n;
    this.pheromone = Array.from({length: n}, () => Array(n).fill(0.1));
    this.distance = Array.from({length: n}, () => Array(n).fill(0));
    this.adjacency = Array.from({length: n}, () => []);
    this.latencyWindow = [];
  }

  setEdge(i, j, w) {
    if (i < this.nodeCount && j < this.nodeCount) {
      this.distance[i][j] = w;
      if (!this.adjacency[i].includes(j)) this.adjacency[i].push(j);
    }
  }

  route(src, dst) {
    if (src >= this.nodeCount || dst >= this.nodeCount) return null;
    let bestPath = null, bestCost = Infinity, noImprove = 0;
    let mutexEvents = 0, lockWait = 0;
    let convergeIter = this.maxIter;
    const t0 = performance.now();

    for (let iter = 0; iter < this.maxIter; iter++) {
      let iterBest = null, iterBestCost = Infinity;

      if (this.contentionChance > 0 && Math.random() < this.contentionChance) {
        mutexEvents++;
        const spin = this.contentionDelay * (0.5 + Math.random());
        lockWait += spin;
        const spinEnd = performance.now() + spin;
        while (performance.now() < spinEnd) {}
      }

      for (let ant = 0; ant < this.antCount; ant++) {
        const result = this._antWalk(src, dst);
        if (result && result.cost < iterBestCost) {
          iterBestCost = result.cost;
          iterBest = result;
        }
      }

      let rate = this.evapRate;
      if (this.adaptiveEvap && this.latencyWindow.length >= 3) {
        const n = this.latencyWindow.length;
        const mean = this.latencyWindow.reduce((a, b) => a + b, 0) / n;
        const variance = this.latencyWindow.reduce((a, x) => a + (x - mean) ** 2, 0) / n;
        const cv = mean > 0 ? Math.sqrt(variance) / mean : 0;
        rate = Math.min(0.95, this.evapRate * (1 + 0.8 * cv));
      }

      for (let i = 0; i < this.nodeCount; i++) {
        for (let j = 0; j < this.nodeCount; j++) {
          this.pheromone[i][j] *= (1 - rate);
          if (this.pheromone[i][j] < 0.01) this.pheromone[i][j] = 0.01;
        }
      }

      if (iterBest) {
        const deposit = this.Q / iterBestCost;
        for (let k = 0; k < iterBest.path.length - 1; k++) {
          this.pheromone[iterBest.path[k]][iterBest.path[k + 1]] += deposit;
        }
        if (iterBestCost < bestCost) {
          bestCost = iterBestCost;
          bestPath = iterBest;
          noImprove = 0;
        } else {
          noImprove++;
        }
      } else {
        noImprove++;
      }

      if (noImprove >= 8) { convergeIter = iter + 1; break; }
    }

    const elapsed = performance.now() - t0;
    if (bestPath) {
      this.latencyWindow.push(elapsed);
      if (this.latencyWindow.length > 100) this.latencyWindow.shift();
    }

    return bestPath ? {
      path: bestPath.path, cost: bestPath.cost,
      hops: bestPath.path.length - 1,
      elapsed, mutexEvents, lockWait, convergeIter
    } : null;
  }

  _antWalk(src, dst) {
    const visited = new Set([src]);
    const path = [src];
    let cost = 0, current = src;
    const maxHops = Math.min(this.nodeCount, 6);

    while (current !== dst && path.length < maxHops) {
      const neighbors = this.adjacency[current].filter(
        nb => !visited.has(nb) && this.distance[current][nb] > 0
      );
      if (neighbors.length === 0) return null;

      let sum = 0;
      const probs = neighbors.map(nb => {
        const p = Math.pow(this.pheromone[current][nb], this.alpha) *
                  Math.pow(1.0 / this.distance[current][nb], this.beta);
        sum += p;
        return p;
      });

      let r = Math.random() * sum, acc = 0, chosen = neighbors[0];
      for (let k = 0; k < neighbors.length; k++) {
        acc += probs[k];
        if (acc >= r) { chosen = neighbors[k]; break; }
      }

      cost += this.distance[current][chosen];
      visited.add(chosen);
      path.push(chosen);
      current = chosen;
    }

    return current !== dst ? null : { path, cost };
  }
}

function buildTopology(engine, numValidators) {
  engine.init(numValidators + 1);
  for (let i = 0; i < numValidators; i++) {
    const stake = 0.3 - (i * 0.02);
    const lat = 3 + (1 - stake) * 12 + Math.random() * 2;
    engine.setEdge(0, i + 1, lat);
  }
  for (let i = 0; i < numValidators; i++) {
    for (let j = i + 1; j < numValidators; j++) {
      if (Math.random() < 0.5) {
        const lat = 1 + Math.random() * 10;
        engine.setEdge(i + 1, j + 1, lat);
        engine.setEdge(j + 1, i + 1, lat);
      }
    }
  }
}

function percentile(arr, p) {
  if (arr.length === 0) return 0;
  const sorted = [...arr].sort((a, b) => a - b);
  const idx = Math.ceil(sorted.length * p / 100) - 1;
  return sorted[Math.max(0, idx)];
}

// ── Drawing functions ──
function pad(str, len, align = 'left', ch = ' ') {
  const s = String(str);
  const need = len - stripAnsi(s).length;
  if (need <= 0) return s.slice(0, len);
  if (align === 'right') return ch.repeat(need) + s;
  if (align === 'center') return ch.repeat(Math.floor(need / 2)) + s + ch.repeat(Math.ceil(need / 2));
  return s + ch.repeat(need);
}

function stripAnsi(str) {
  return str.replace(/\x1b\[[0-9;]*m/g, '');
}

function drawBox(row, col, w, h, color) {
  let out = '';
  out += moveTo(row, col) + color + BOX.tl + BOX.h.repeat(w - 2) + BOX.tr + RESET;
  for (let i = 1; i < h - 1; i++) {
    out += moveTo(row + i, col) + color + BOX.v + RESET;
    out += moveTo(row + i, col + w - 1) + color + BOX.v + RESET;
  }
  out += moveTo(row + h - 1, col) + color + BOX.bl + BOX.h.repeat(w - 2) + BOX.br + RESET;
  return out;
}

function drawDoubleBox(row, col, w, h, color) {
  let out = '';
  out += moveTo(row, col) + color + BOX.dtl + BOX.dh.repeat(w - 2) + BOX.dtr + RESET;
  for (let i = 1; i < h - 1; i++) {
    out += moveTo(row + i, col) + color + BOX.dv + RESET;
    out += moveTo(row + i, col + w - 1) + color + BOX.dv + RESET;
  }
  out += moveTo(row + h - 1, col) + color + BOX.dbl + BOX.dh.repeat(w - 2) + BOX.dbr + RESET;
  return out;
}

function drawHLine(row, col, w, color) {
  return moveTo(row, col) + color + BOX.lj + BOX.h.repeat(w - 2) + BOX.rj + RESET;
}

function textAt(row, col, text) {
  return moveTo(row, col) + text;
}

function fillLine(row, col, w, ch = ' ', color = '') {
  return moveTo(row, col) + color + ch.repeat(w) + RESET;
}

// ── Ant ASCII art ──
const ANT_FRAMES = [
  [
    `    \\   /    `,
    `     \\ /     `,
    ` ──( 0 0 )── `,
    `     /█\\     `,
    `    / █ \\    `,
    `   /  █  \\   `,
  ],
  [
    `     \\ /     `,
    `    \\ . /    `,
    ` ──( · · )── `,
    `     /█\\     `,
    `    / █ \\    `,
    `      █      `,
  ]
];

const LOGO = [
  `${BR_GREEN}██████╗ ███████╗██╗   ██╗███╗   ███╗${RESET}`,
  `${BR_GREEN}██╔══██╗██╔════╝██║   ██║████╗ ████║${RESET}`,
  `${BR_GREEN}██████╔╝█████╗  ██║   ██║██╔████╔██║${RESET}`,
  `${BR_GREEN}██╔══██╗██╔══╝  ╚██╗ ██╔╝██║╚██╔╝██║${RESET}`,
  `${BR_GREEN}██║  ██║███████╗ ╚████╔╝ ██║ ╚═╝ ██║${RESET}`,
  `${BR_GREEN}╚═╝  ╚═╝╚══════╝  ╚═══╝  ╚═╝     ╚═╝${RESET}`,
];

// ── Main rendering ──
let buf = '';
function flush() {
  process.stdout.write(buf);
  buf = '';
}
function w(s) { buf += s; }

// State
let oldTimes = [], newTimes = [];
let oldMutex = 0, oldLockWait = 0;
let oldConverge = [], newConverge = [];
let oldLanded = 0, newLanded = 0;
let oldFailed = 0, newFailed = 0;
let oldLogs = [], newLogs = [];
let routeIdx = 0;
let phase = 'init'; // init, running, done
let frame = 0;

function addOldLog(msg, type = 'msg') {
  oldLogs.push({ msg, type });
  if (oldLogs.length > 6) oldLogs.shift();
}
function addNewLog(msg, type = 'msg') {
  newLogs.push({ msg, type });
  if (newLogs.length > 6) newLogs.shift();
}

function drawHeader() {
  const centerX = Math.floor((cols - 36) / 2);
  for (let i = 0; i < LOGO.length; i++) {
    w(moveTo(2 + i, centerX) + LOGO[i]);
  }
  const sub = `${FG_GRAY}ENGINE BENCHMARK${RESET}`;
  w(moveTo(8, Math.floor((cols - 16) / 2)) + sub);

  const vs = `${FG_ORANGE}v0.2.0 RwLock${RESET}  ${FG_DARKGRAY}━━━${RESET}  ${DIM}VS${RESET}  ${FG_DARKGRAY}━━━${RESET}  ${BR_GREEN}v0.3.0 Lock-Free${RESET}`;
  w(moveTo(10, Math.floor((cols - 47) / 2)) + vs);
}

function drawPanel(side, row, col, pw) {
  const isOld = side === 'old';
  const color = isOld ? FG_DARKORANGE : FG_DARKGREEN;
  const brightColor = isOld ? FG_ORANGE : BR_GREEN;
  const bgHighlight = isOld ? BG_DARKORANGE : BG_DARKGREEN;
  const label = isOld ? ' v0.2.0 RwLock ' : ' v0.3.0 Lock-Free ';
  const innerW = pw - 2;

  // Panel border
  w(drawBox(row, col, pw, 24, color));

  // Title bar
  w(moveTo(row, col + 2) + bgHighlight + BOLD + BLACK + label + RESET);

  // Engine type
  const engineType = isOld ?
    `${FG_DARKORANGE}parking_lot::RwLock<Vec<f64>>${RESET}` :
    `${FG_DARKGREEN}AtomicU64 CAS (lock-free)${RESET}`;
  w(moveTo(row + 1, col + 2) + engineType);

  // Separator
  w(drawHLine(row + 2, col, pw, color));

  // AVG Latency (big number)
  const times = isOld ? oldTimes : newTimes;
  const avg = times.length > 0 ?
    (times.reduce((a, b) => a + b, 0) / times.length).toFixed(1) : '---';
  w(moveTo(row + 3, col + 2) + `${FG_GRAY}AVG LATENCY${RESET}`);
  const avgStr = avg === '---' ? `${DIM}---${RESET}` :
    `${BOLD}${brightColor}${avg}${RESET} ${FG_GRAY}ms${RESET}`;
  w(moveTo(row + 4, col + 2) + avgStr);

  // Separator
  w(drawHLine(row + 5, col, pw, color));

  // P50 / P99
  const p50 = times.length > 0 ? percentile(times, 50).toFixed(2) : '---';
  const p99 = times.length > 0 ? percentile(times, 99).toFixed(2) : '---';
  w(moveTo(row + 6, col + 2) + `${FG_GRAY}P50${RESET}`);
  w(moveTo(row + 6, col + innerW - 12) +
    (p50 === '---' ? `${DIM}---${RESET}` : `${brightColor}${pad(p50, 8, 'right')}${RESET}${FG_GRAY}ms${RESET}`));
  w(moveTo(row + 7, col + 2) + `${FG_GRAY}P99${RESET}`);
  w(moveTo(row + 7, col + innerW - 12) +
    (p99 === '---' ? `${DIM}---${RESET}` : `${brightColor}${pad(p99, 8, 'right')}${RESET}${FG_GRAY}ms${RESET}`));

  // Separator
  w(drawHLine(row + 8, col, pw, color));

  // Contention
  const mutex = isOld ? oldMutex : 0;
  const lockW = isOld ? oldLockWait : 0;
  w(moveTo(row + 9, col + 2) + `${FG_GRAY}MUTEX EVENTS${RESET}`);
  if (mutex > 0) {
    w(moveTo(row + 9, col + innerW - 10) + `${BR_RED}${BOLD}${pad(String(mutex), 8, 'right')}${RESET}`);
  } else {
    w(moveTo(row + 9, col + innerW - 10) +
      (phase === 'init' ? `${DIM}${pad('---', 8, 'right')}${RESET}` : `${BR_GREEN}${pad('0', 8, 'right')}${RESET}`));
  }
  w(moveTo(row + 10, col + 2) + `${FG_GRAY}LOCK WAIT${RESET}`);
  if (lockW > 0) {
    w(moveTo(row + 10, col + innerW - 12) + `${BR_RED}${pad(lockW.toFixed(1), 8, 'right')}${RESET}${FG_GRAY}ms${RESET}`);
  } else {
    w(moveTo(row + 10, col + innerW - 12) +
      (phase === 'init' ? `${DIM}${pad('---', 8, 'right')}${RESET}   ` : `${BR_GREEN}${pad('0.0', 8, 'right')}${RESET}${FG_GRAY}ms${RESET}`));
  }

  // Contention bar
  const barW = innerW - 2;
  const barFill = Math.min(barW, Math.floor(mutex / (TOTAL * 0.18) * barW));
  w(moveTo(row + 11, col + 2));
  if (isOld && mutex > 0) {
    w(`${BG_RED}${' '.repeat(barFill)}${RESET}${FG_DARKGRAY}${'░'.repeat(barW - barFill)}${RESET}`);
  } else {
    w(`${FG_DARKGRAY}${'░'.repeat(barW)}${RESET}`);
  }

  // Separator
  w(drawHLine(row + 12, col, pw, color));

  // Routing stats
  const converge = isOld ? oldConverge : newConverge;
  const landed = isOld ? oldLanded : newLanded;
  const failed = isOld ? oldFailed : newFailed;
  const total = landed + failed;
  const landRate = total > 0 ? (landed / total * 100).toFixed(1) : '---';
  const avgConv = converge.length > 0 ?
    (converge.reduce((a, b) => a + b, 0) / converge.length).toFixed(1) : '---';

  w(moveTo(row + 13, col + 2) + `${FG_GRAY}CONVERGENCE${RESET}`);
  w(moveTo(row + 13, col + innerW - 14) +
    (avgConv === '---' ? `${DIM}${pad('---', 8, 'right')}${RESET}      ` : `${brightColor}${pad(avgConv, 8, 'right')}${RESET}${FG_GRAY} iters${RESET}`));
  w(moveTo(row + 14, col + 2) + `${FG_GRAY}ROUTES${RESET}`);
  w(moveTo(row + 14, col + innerW - 10) +
    (landed === 0 && phase === 'init' ? `${DIM}${pad('---', 8, 'right')}${RESET}` : `${brightColor}${pad(String(landed), 8, 'right')}${RESET}`));
  w(moveTo(row + 15, col + 2) + `${FG_GRAY}LANDED${RESET}`);
  w(moveTo(row + 15, col + innerW - 10) +
    (landRate === '---' ? `${DIM}${pad('---', 8, 'right')}${RESET}` : `${brightColor}${pad(landRate + '%', 8, 'right')}${RESET}`));

  // Separator
  w(drawHLine(row + 16, col, pw, color));

  // Logs
  w(moveTo(row + 17, col + 2) + `${FG_GRAY}LOG${RESET}`);
  const logs = isOld ? oldLogs : newLogs;
  for (let i = 0; i < 5; i++) {
    const line = logs[i] || null;
    w(moveTo(row + 18 + i, col + 2) + ' '.repeat(innerW - 2));
    if (line) {
      let logColor = FG_GRAY;
      if (line.type === 'warn') logColor = BR_RED;
      else if (line.type === 'ok') logColor = BR_GREEN;
      else logColor = isOld ? FG_DARKORANGE : FG_DARKGREEN;
      const trimmed = line.msg.slice(0, innerW - 4);
      w(moveTo(row + 18 + i, col + 2) + logColor + trimmed + RESET);
    }
  }
}

function drawProgressBar(row) {
  const barW = cols - 20;
  const pct = TOTAL > 0 ? routeIdx / TOTAL : 0;
  const filled = Math.floor(pct * barW);
  const empty = barW - filled;
  const pctStr = `${(pct * 100).toFixed(0)}%`;

  w(moveTo(row, 2) + `${FG_GRAY}[${RESET}`);
  w(`${BR_GREEN}${'█'.repeat(filled)}${RESET}`);
  if (empty > 0) w(`${FG_DARKGRAY}${'░'.repeat(empty)}${RESET}`);
  w(`${FG_GRAY}]${RESET} `);
  w(`${BR_WHITE}${pad(pctStr, 4, 'right')}${RESET}`);
  w(`  ${FG_GRAY}${routeIdx}/${TOTAL}${RESET}`);
}

function drawStatus(row) {
  let status = '';
  if (phase === 'init') {
    status = `${FG_GRAY}Press ${BR_WHITE}ENTER${RESET}${FG_GRAY} to start benchmark${RESET}`;
  } else if (phase === 'running') {
    const dots = '.'.repeat((frame % 3) + 1) + ' '.repeat(3 - (frame % 3) - 1);
    status = `${BR_GREEN}${BOLD}BENCHMARKING${dots}${RESET}  ${FG_GRAY}${routeIdx} routes computed${RESET}`;
  } else if (phase === 'done') {
    status = `${BR_GREEN}${BOLD}BENCHMARK COMPLETE${RESET}`;
  }
  w(moveTo(row, 2) + ' '.repeat(cols - 2));
  w(moveTo(row, 2) + status);
}

function drawResults() {
  if (phase !== 'done') return;

  const oldAvg = oldTimes.reduce((a, b) => a + b, 0) / oldTimes.length;
  const newAvg = newTimes.reduce((a, b) => a + b, 0) / newTimes.length;
  const oldP50 = percentile(oldTimes, 50);
  const newP50 = percentile(newTimes, 50);
  const oldP99 = percentile(oldTimes, 99);
  const newP99 = percentile(newTimes, 99);
  const oldConvAvg = oldConverge.reduce((a, b) => a + b, 0) / oldConverge.length;
  const newConvAvg = newConverge.reduce((a, b) => a + b, 0) / newConverge.length;

  const p50Imp = ((oldP50 - newP50) / oldP50 * 100).toFixed(0);
  const p99Imp = ((oldP99 - newP99) / oldP99 * 100).toFixed(0);
  const convImp = ((oldConvAvg - newConvAvg) / oldConvAvg * 100).toFixed(0);

  const resultRow = 37;
  const rw = Math.min(cols - 4, 70);
  const rx = Math.floor((cols - rw) / 2);

  w(drawDoubleBox(resultRow, rx, rw, 10, BR_GREEN));
  w(moveTo(resultRow, rx + 2) + `${BG_DARKGREEN}${BOLD}${BLACK} RESULTS ${RESET}`);

  const innerW = rw - 4;
  w(moveTo(resultRow + 2, rx + 2) + `${FG_GRAY}AVG LATENCY   ${FG_ORANGE}${pad(oldAvg.toFixed(1) + 'ms', 10, 'right')}${RESET}  ${FG_GRAY}→${RESET}  ${BR_GREEN}${pad(newAvg.toFixed(1) + 'ms', 10, 'right')}${RESET}`);
  w(moveTo(resultRow + 3, rx + 2) + `${FG_GRAY}P50           ${FG_ORANGE}${pad(oldP50.toFixed(2) + 'ms', 10, 'right')}${RESET}  ${FG_GRAY}→${RESET}  ${BR_GREEN}${pad(newP50.toFixed(2) + 'ms', 10, 'right')}${RESET}  ${BR_GREEN}▼ ${p50Imp}%${RESET}`);
  w(moveTo(resultRow + 4, rx + 2) + `${FG_GRAY}P99           ${FG_ORANGE}${pad(oldP99.toFixed(2) + 'ms', 10, 'right')}${RESET}  ${FG_GRAY}→${RESET}  ${BR_GREEN}${pad(newP99.toFixed(2) + 'ms', 10, 'right')}${RESET}  ${BR_GREEN}▼ ${p99Imp}%${RESET}`);
  w(moveTo(resultRow + 5, rx + 2) + `${FG_GRAY}CONVERGENCE   ${FG_ORANGE}${pad(oldConvAvg.toFixed(1) + ' it', 10, 'right')}${RESET}  ${FG_GRAY}→${RESET}  ${BR_GREEN}${pad(newConvAvg.toFixed(1) + ' it', 10, 'right')}${RESET}  ${BR_GREEN}▼ ${convImp}%${RESET}`);
  w(moveTo(resultRow + 6, rx + 2) + `${FG_GRAY}CONTENTION    ${BR_RED}${pad(String(oldMutex), 10, 'right')}${RESET}  ${FG_GRAY}→${RESET}  ${BR_GREEN}${pad('0', 10, 'right')}${RESET}  ${BR_GREEN}ELIMINATED${RESET}`);

  w(moveTo(resultRow + 8, rx + 2) + `${FG_GRAY}Lock-free AtomicU64 CAS engine. Zero contention. Adaptive evaporation.${RESET}`);
}

function render() {
  w(HIDE_CURSOR);

  // Header
  drawHeader();

  // Panels
  const panelRow = 12;
  drawPanel('old', panelRow, 1, panelW);
  drawPanel('new', panelRow, panelW + 2, panelW);

  // Center divider
  const vsRow = panelRow + 12;
  w(moveTo(vsRow, panelW + 1) + `${FG_GRAY}${BOLD}VS${RESET}`);

  // Progress
  drawProgressBar(panelRow + 25);

  // Status
  drawStatus(panelRow + 26);

  // Results
  drawResults();

  flush();
}

// ── Engines ──
const oldEngine = new AcoEngine({
  alpha: 1.2, beta: 3.0, evapRate: 0.25,
  antCount: 64, maxIter: 50,
  contentionChance: 0.18, contentionDelay: 0.08,
  adaptiveEvap: false
});

const newEngine = new AcoEngine({
  alpha: 1.2, beta: 3.0, evapRate: 0.25,
  antCount: 64, maxIter: 50,
  contentionChance: 0, contentionDelay: 0,
  adaptiveEvap: true
});

buildTopology(oldEngine, 12);
buildTopology(newEngine, 12);

// ── Benchmark loop ──
function runTick() {
  if (phase !== 'running') return;

  const batchSize = 3;
  for (let b = 0; b < batchSize && routeIdx < TOTAL; b++, routeIdx++) {
    const dst = 1 + Math.floor(Math.random() * 12);

    const oldRes = oldEngine.route(0, dst);
    if (oldRes) {
      oldTimes.push(oldRes.elapsed);
      oldMutex += oldRes.mutexEvents;
      oldLockWait += oldRes.lockWait;
      oldConverge.push(oldRes.convergeIter);
      oldLanded++;
    } else {
      oldFailed++;
    }

    const newRes = newEngine.route(0, dst);
    if (newRes) {
      newTimes.push(newRes.elapsed);
      newConverge.push(newRes.convergeIter);
      newLanded++;
    } else {
      newFailed++;
    }

    if (routeIdx > 0 && routeIdx % 30 === 0) {
      addOldLog(`Route #${routeIdx} p99=${percentile(oldTimes, 99).toFixed(2)}ms`);
      if (oldMutex > 0) addOldLog(`WARN: ${oldMutex} mutex contentions`, 'warn');
      addNewLog(`Route #${routeIdx} p99=${percentile(newTimes, 99).toFixed(2)}ms`);
      addNewLog(`0 mutex contentions`, 'ok');
    }
  }

  frame++;
  render();

  if (routeIdx >= TOTAL) {
    phase = 'done';
    addOldLog(`Complete: ${oldLanded} routes`);
    addOldLog(`Total contentions: ${oldMutex}`, 'warn');
    addNewLog(`Complete: ${newLanded} routes`, 'ok');
    addNewLog(`0 mutex contentions`, 'ok');
    render();
    setTimeout(() => {
      w(moveTo(rows - 1, 2) + `${FG_GRAY}Press ${BR_WHITE}q${RESET}${FG_GRAY} to exit${RESET}`);
      flush();
    }, 500);
    return;
  }

  setTimeout(runTick, TICK_MS);
}

// ── Init ──
process.stdout.write(CLEAR);

addOldLog('Engine: RwLock<Vec<f64>>');
addOldLog('parking_lot::RwLock');
addOldLog('Topology: 13 nodes');
addNewLog('Engine: AtomicU64 CAS');
addNewLog('lock-free atomics');
addNewLog('Topology: 13 nodes');

render();

// Handle input
const stdin = process.stdin;
stdin.setRawMode(true);
stdin.resume();
stdin.setEncoding('utf8');

stdin.on('data', (key) => {
  if (key === 'q' || key === '\x03') {
    process.stdout.write(SHOW_CURSOR + CLEAR + moveTo(1, 1));
    process.exit(0);
  }
  if (key === '\r' || key === '\n' || key === ' ') {
    if (phase === 'init') {
      phase = 'running';
      addOldLog('Benchmark started...');
      addNewLog('Benchmark started...');
      runTick();
    }
  }
});

// Cleanup on exit
process.on('exit', () => {
  process.stdout.write(SHOW_CURSOR + RESET);
});
process.on('SIGINT', () => {
  process.stdout.write(SHOW_CURSOR + CLEAR + RESET);
  process.exit(0);
});
