const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');

const ROOT = path.resolve(__dirname, '..');
const NODE = process.execPath;
const SERVER = path.join(ROOT, 'src', 'server.js');
const SAMPLE_DIR = path.join(ROOT, 'data', 'priority-relay-sample');
const PORT_CORE = 3500;
const PORT_CLIENT = 3501;
const PORT_STALE = 3502;

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function jsonFetch(url, options = {}) {
  const response = await fetch(url, {
    headers: { 'Content-Type': 'application/json' },
    ...options,
  });
  const data = await response.json().catch(() => ({}));
  if (!response.ok || data.error) throw new Error(`${url} failed: ${data.error || data.message || response.status}`);
  return data;
}

async function waitForHealth(port) {
  for (let index = 0; index < 50; index += 1) {
    try {
      return await jsonFetch(`http://localhost:${port}/api/health`);
    } catch {
      await sleep(100);
    }
  }
  throw new Error(`Node ${port} did not become healthy`);
}

function assertInsideWorkspace(target) {
  const resolved = path.resolve(target);
  const workspace = path.resolve(ROOT);
  if (!resolved.startsWith(workspace + path.sep)) {
    throw new Error(`Refusing to remove path outside workspace: ${resolved}`);
  }
}

function startNode(port, name) {
  const dataDir = path.join(SAMPLE_DIR, name);
  fs.mkdirSync(dataDir, { recursive: true });
  return spawn(NODE, [SERVER], {
    cwd: ROOT,
    env: {
      ...process.env,
      PORT: String(port),
      MAZEX_DATA_DIR: dataDir,
      MAZEX_AUTO_BROADCAST: '0',
      MAZEX_SYNC_INTERVAL_MS: '0',
      MAZEX_PRIORITY_RELAYS: '',
      MAZEX_SEED_PEERS: '',
    },
    stdio: ['ignore', 'pipe', 'pipe'],
    windowsHide: true,
  });
}

async function main() {
  assertInsideWorkspace(SAMPLE_DIR);
  fs.rmSync(SAMPLE_DIR, { recursive: true, force: true });
  const children = [
    startNode(PORT_CORE, 'core-relay'),
    startNode(PORT_CLIENT, 'client'),
    startNode(PORT_STALE, 'stale-peer'),
  ];
  try {
    await Promise.all([waitForHealth(PORT_CORE), waitForHealth(PORT_CLIENT), waitForHealth(PORT_STALE)]);
    await jsonFetch(`http://localhost:${PORT_CORE}/api/base1/reset`, { method: 'POST', body: '{}' });
    await jsonFetch(`http://localhost:${PORT_CLIENT}/api/base1/reset`, { method: 'POST', body: '{}' });
    await jsonFetch(`http://localhost:${PORT_STALE}/api/base1/reset`, { method: 'POST', body: '{}' });

    await jsonFetch(`http://localhost:${PORT_CLIENT}/api/base1/node/peers`, {
      method: 'POST',
      body: JSON.stringify({ url: `http://localhost:${PORT_STALE}`, name: 'stale-peer' }),
    });
    await jsonFetch(`http://localhost:${PORT_CLIENT}/api/base1/node/peers`, {
      method: 'POST',
      body: JSON.stringify({
        url: `http://localhost:${PORT_CORE}`,
        name: 'core-relay',
        priorityRelay: true,
        relayTier: 'core',
      }),
    });

    const solve = await jsonFetch(`http://localhost:${PORT_CORE}/api/base1/demo-solve`, {
      method: 'POST',
      body: JSON.stringify({ solver: 'priority-relay-solver' }),
    });
    if (!solve.correct) throw new Error(`core relay solve failed: ${solve.message}`);

    const preflight = await jsonFetch(`http://localhost:${PORT_CLIENT}/api/base1/preflight`, {
      method: 'POST',
      body: JSON.stringify({ ttl: 0, maxPeers: 1, timeoutMs: 3000 }),
    });
    if (!preflight.changed) throw new Error('Expected client to sync through priority relay with maxPeers=1');
    if (!String(preflight.discovery.best?.source || '').includes(String(PORT_CORE))) {
      throw new Error(`Expected priority relay to be queried first, got ${preflight.discovery.best?.source}`);
    }

    const network = await jsonFetch(`http://localhost:${PORT_CLIENT}/api/base1/node/peers`);
    const firstPeer = network.peers[0];
    if (!firstPeer.priority_relay || !String(firstPeer.url).includes(String(PORT_CORE))) {
      throw new Error('Expected priority relay to be first in propagation order');
    }

    const current = await jsonFetch(`http://localhost:${PORT_CLIENT}/api/base1/current`);
    console.table([
      {
        node: 'client',
        phase: 'priority-first-preflight',
        priority_relays: network.propagation.priority_relay_count,
        queried: preflight.discovery.best.source,
        layer: current.current.layer_no,
        step: current.current.step_no,
      },
      {
        node: 'first-peer',
        phase: firstPeer.role,
        priority_relays: '-',
        queried: firstPeer.url,
        layer: '-',
        step: '-',
      },
    ]);
  } finally {
    for (const child of children) child.kill();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
