// 铸渊自动同步验证Agent v1.0 · ICE-GL-ZY001

const http = require('http');
const fs = require('fs');
const path = require('path');
const REPO = '/opt/guanghulab-repo';

function apiGet(p) {
  return new Promise((ok, no) => {
    http.get({ hostname: '127.0.0.1', port: 3920, path: p, timeout: 15000 }, r => {
      let b = '';
      r.on('data', c => b += c);
      r.on('end', () => { try { ok(JSON.parse(b)) } catch (e) { no(e) } });
    }).on('error', no).on('timeout', () => no(new Error('timeout')));
  });
}

function gkExec(srv, cmd, to = 10000) {
  return new Promise(ok => {
    const d = JSON.stringify({ cmd });
    const o = {
      hostname: srv.ip, port: srv.port || 3910, path: '/exec', method: 'POST',
      headers: {
        'Authorization': 'Bearer ' + srv.key,
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(d)
      }, timeout: to
    };
    const r = http.request(o, res => {
      let b = '';
      res.on('data', c => b += c);
      res.on('end', () => { try { ok({ ok: res.statusCode === 200, body: JSON.parse(b) }) } catch (e) { ok({ ok: false, body: b, error: e.message }) } });
    });
    r.on('error', e => ok({ ok: false, error: e.message }));
    r.on('timeout', () => { r.destroy(); ok({ ok: false, error: 'timeout' }) });
    r.write(d); r.end();
  });
}

function load(f) {
  try { return JSON.parse(fs.readFileSync(path.join(REPO, f), 'utf-8')) } catch (e) { return null }
}

async function main() {
  const t0 = Date.now();
  console.log('铸渊自动同步Agent v1.0 · 启动验证...');

  console.log('[1/5] 获取真实服务器状态...');
  let real;
  try { real = await apiGet('/api/servers?hw=0'); console.log('  OK ' + real.servers.length + '台') }
  catch (e) { console.log('  FAIL ' + e.message); process.exit(1) }

  console.log('[2/5] 读取声明配置...');
  const topo = load('brain/pool-topology.json');
  const gk = load('brain/gatekeeper-deployment.json');
  console.log('  topo v' + (topo?.version || '?') + ' · gk v' + (gk?.version || '?'));

  console.log('[3/5] 逐台对比声明vs实际...');
  let alive = 0, off = 0;
  const issues = [];
  const codes = new Set();
  gk?.servers?.forEach(s => codes.add(s.code));
  if (topo?.pools) for (const p of Object.values(topo.pools)) if (p.nodes) Object.keys(p.nodes).forEach(c => codes.add(c));

  for (const code of codes) {
    const r = real.servers.find(s => s.code === code);
    const g = gk?.servers?.find(s => s.code === code);
    let t = null;
    for (const [k, p] of Object.entries(topo?.pools || {})) { if (p.nodes?.[code]) { t = p.nodes[code]; break } }

    const diffs = [];
    if (t && t.online !== undefined && t.online !== (r?.alive || false)) {
      diffs.push({ f: 'online', d: t.online, a: r?.alive || false, s: t.online === false ? 'info' : 'critical' });
    }
    if (!g?.key && r) diffs.push({ f: 'gk_key', d: 'present', a: 'missing', s: 'critical' });

    if (r?.alive) alive++; else if (r) off++;
    if (diffs.length > 0) issues.push({ code, name: g?.name || code, diffs });
  }

  console.log('[4/5] 深度验证 (仓库/密钥/gatekeeper进程)...');
  const deep = [];
  const ps = gk?.servers?.filter(s => s.persona) || [];
  for (const s of ps) {
    const r = real.servers.find(x => x.code === s.code);
    if (!r?.alive) { deep.push({ code: s.code, name: s.name, status: 'offline' }); continue }
    const ck = {};
    try {
      const x = await gkExec(s, 'ls ~/ 2>/dev/null && echo FOUND || echo NF', 5000);
      ck.home = x.ok ? 'ok' : '?';
    } catch (e) { ck.home = 'err' }
    try {
      const x = await gkExec(s, 'ls ~/repos/ 2>/dev/null && echo FOUND || echo NF', 5000);
      ck.repo = x.ok ? (x.body?.stdout?.includes('FOUND') ? 'found' : 'none') : '?';
    } catch (e) { ck.repo = 'err' }
    try {
      const x = await gkExec(s, 'test -f ~/.ssh/authorized_keys && wc -l < ~/.ssh/authorized_keys || echo 0', 5000);
      ck.keys = x.ok ? (parseInt(x.body?.stdout?.trim()) || 0) : '?';
    } catch (e) { ck.keys = 'err' }
    try {
      const x = await gkExec(s, 'pm2 list 2>/dev/null | grep -c gatekeeper || echo 0', 5000);
      ck.gk_pm2 = x.ok ? (parseInt(x.body?.stdout?.trim()) || 0) : '?';
    } catch (e) { ck.gk_pm2 = 'err' }
    deep.push({ code: s.code, name: s.name, persona: s.persona, status: 'online', checks: ck });
    console.log('  ' + s.code + ': home=' + ck.home + ' repo=' + ck.repo + ' keys=' + ck.keys + ' gk=' + ck.gk_pm2);
  }

  console.log('[5/5] 生成报告...');
  const dur = Date.now() - t0;
  console.log('');
  console.log('═══ 铸渊验证报告 ═══');
  console.log('时间: ' + new Date().toISOString());
  console.log(alive + '/' + codes.size + ' 在线 · ' + issues.length + ' 差异 · ' + deep.length + ' 深度检查');
  if (issues.length === 0) console.log('所有节点声明与实际状态一致');
  else for (const i of issues) for (const d of i.diffs) {
    console.log('  ' + (d.s === 'critical' ? '!' : '~') + ' ' + i.code + ' [' + d.f + ']: ' + d.d + ' → ' + d.a);
  }
  console.log('深度检查:');
  for (const d of deep) {
    console.log('  ' + d.code + '(' + (d.persona || '?') + '): home=' + d.checks.home + ' repo=' + d.checks.repo + ' keys=' + d.checks.keys + ' gk_pm2=' + d.checks.gk_pm2);
  }
  console.log(dur + 'ms · 报告完毕');

  const rep = { ts: new Date().toISOString(), summary: { total: codes.size, alive, off, diffs: issues.length, deep: deep.length, ms: dur }, issues, deep };
  fs.writeFileSync(path.join(REPO, 'brain/auto-sync-report.json'), JSON.stringify(rep, null, 2));
  console.log('brain/auto-sync-report.json');
}

main().catch(e => { console.error('FAIL: ' + e.message); process.exit(1) });
