const express = require('express');
const router = express.Router();

/**
 * Infrastructure Module — Trinity Console
 * 
 * Live infrastructure topology and server health monitoring.
 * Data sourced from Trinity Core MCP gateway via HTTP API.
 * 
 * GET  /admin/infrastructure          — Main topology view
 * GET  /admin/infrastructure/refresh  — Force cache refresh (HTMX)
 * GET  /admin/infrastructure/server/:id — Server detail partial (HTMX)
 * 
 * Chronicler #78 | April 11, 2026
 */

const TRINITY_CORE_URL = 'https://mcp.firefrostgaming.com';
const TRINITY_CORE_TOKEN = 'FFG-Trinity-2026-Core-Access';
const CACHE_TTL = 60000; // 60 seconds

// Server definitions (matches Trinity Core's SERVERS config)
const SERVERS = {
  'command-center': { label: 'Command Center', role: 'Management Hub', color: '#A855F7' },
  'tx1-dallas':     { label: 'TX1 Dallas', role: 'Game Node (Primary)', color: '#FF6B35' },
  'nc1-charlotte':  { label: 'NC1 Charlotte', role: 'Game Node (Secondary)', color: '#4ECDC4' },
  'panel-vps':      { label: 'Panel VPS', role: 'Pterodactyl Panel', color: '#3b82f6' },
  'dev-panel':      { label: 'Dev Panel', role: 'Development & Testing', color: '#6b7280' },
  'wiki-vps':       { label: 'Wiki VPS', role: 'Knowledge Base', color: '#06b6d4' },
  'services-vps':   { label: 'Services VPS', role: 'Email & Services', color: '#f59e0b' },
  'trinity-core':   { label: 'Trinity Core', role: 'MCP Gateway (Pi)', color: '#A855F7' }
};

// In-memory cache
let auditCache = { data: null, lastFetch: 0 };

/**
 * Execute a command on a server via Trinity Core
 */
async function trinityExec(server, command) {
  try {
    const res = await fetch(`${TRINITY_CORE_URL}/exec`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${TRINITY_CORE_TOKEN}`
      },
      body: JSON.stringify({ server, command })
    });
    if (!res.ok) throw new Error(`Trinity Core error: ${res.status}`);
    const data = await res.json();
    return data.output || data.stdout || '';
  } catch (err) {
    console.error(`[INFRA] trinityExec failed for ${server}:`, err.message);
    return null;
  }
}

/**
 * Parse the standard audit command output
 */
function parseAuditOutput(output) {
  if (!output) return null;
  const lines = output.split('\n');
  const get = (marker) => {
    const idx = lines.findIndex(l => l.includes(marker));
    return idx >= 0 && idx + 1 < lines.length ? lines[idx + 1]?.trim() : '';
  };

  // Parse RAM line: "Mem:  3.8Gi  1.4Gi  803Mi ..."
  const memLine = get('=== RAM ===');
  const memParts = memLine.split(/\s+/);
  const totalRam = memParts[1] || '?';
  const usedRam = memParts[2] || '?';

  // Parse disk line: "/dev/xxx  38G  17G  21G  45%  /"
  const diskLine = get('=== DISK ===');
  const diskParts = diskLine.split(/\s+/);
  const totalDisk = diskParts[1] || '?';
  const usedDisk = diskParts[2] || '?';
  const diskPct = parseInt(diskParts[4]) || 0;

  // Parse uptime: "05:03:05 up 61 days, ..."
  const uptimeLine = get('=== UPTIME ===');
  const uptimeMatch = uptimeLine.match(/up\s+(.+?),\s+\d+\s+user/);
  const uptime = uptimeMatch ? uptimeMatch[1].trim() : uptimeLine;

  // Parse load averages
  const loadMatch = uptimeLine.match(/load average:\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
  const load = loadMatch ? [parseFloat(loadMatch[1]), parseFloat(loadMatch[2]), parseFloat(loadMatch[3])] : [0, 0, 0];

  // Parse restart required
  const restartLine = get('=== RESTART ===');
  const restart = restartLine.includes('restart required');

  // Parse hostname, OS, kernel, CPU
  const hostname = get('=== HOSTNAME ===');
  const osLines = output.split('\n').filter(l => l.startsWith('NAME=') || l.startsWith('VERSION='));
  const osName = osLines.map(l => l.split('=')[1]?.replace(/"/g, '')).join(' ');
  const kernel = get('=== KERNEL ===');
  const cpuModel = lines.find(l => l.includes('Model name'))?.split(':')[1]?.trim() || '?';
  const cpuCores = lines.find(l => l.match(/^CPU\(s\)/))?.split(':')[1]?.trim() || '?';

  // Parse RAM percentage (handle Gi vs Mi units)
  function toGi(val) {
    if (!val) return 0;
    const num = parseFloat(val);
    if (val.includes('Mi')) return num / 1024;
    if (val.includes('Gi')) return num;
    return num;
  }
  const ramPct = toGi(totalRam) > 0
    ? Math.round((toGi(usedRam) / toGi(totalRam)) * 100)
    : 0;

  return {
    hostname,
    os: osName,
    kernel,
    cpu: `${cpuModel} (${cpuCores} cores)`,
    ram: { total: totalRam, used: usedRam, pct: ramPct },
    disk: { total: totalDisk, used: usedDisk, pct: diskPct },
    uptime,
    load,
    restart,
    diskWarning: diskPct >= 70,
    status: diskPct >= 85 ? 'critical' : diskPct >= 70 ? 'warning' : restart ? 'warning' : 'healthy'
  };
}

/**
 * Fetch audit data from all servers in parallel
 */
async function fetchFleetAudit() {
  const now = Date.now();
  if (auditCache.data && (now - auditCache.lastFetch < CACHE_TTL)) {
    return auditCache.data;
  }

  console.log('[INFRA] Fetching fleet audit from Trinity Core...');

  const auditCommand = [
    'echo "=== HOSTNAME ===" && hostname',
    'echo "=== OS ===" && cat /etc/os-release | grep -E "^(NAME|VERSION)="',
    'echo "=== CPU ===" && lscpu | grep -E "^(Architecture|CPU\\(s\\)|Model name|Thread)"',
    'echo "=== RAM ===" && free -h | grep Mem',
    'echo "=== DISK ===" && df -h / | tail -1',
    'echo "=== KERNEL ===" && uname -r',
    'echo "=== RESTART ===" && [ -f /var/run/reboot-required ] && cat /var/run/reboot-required || echo "No restart required"',
    'echo "=== UPTIME ===" && uptime'
  ].join(' && ');

  // Run all server audits in parallel
  const serverIds = Object.keys(SERVERS);
  const results = await Promise.allSettled(
    serverIds.map(id => trinityExec(id, auditCommand))
  );

  const audit = {};
  serverIds.forEach((id, i) => {
    const output = results[i].status === 'fulfilled' ? results[i].value : null;
    const parsed = parseAuditOutput(output);
    audit[id] = {
      ...SERVERS[id],
      ...(parsed || { status: 'offline', error: 'Could not reach server' }),
      online: !!parsed
    };
  });

  // Fetch game servers from Pterodactyl
  let gameServers = [];
  try {
    const panelRes = await fetch(`${process.env.PANEL_URL}/api/application/servers?per_page=50`, {
      headers: {
        'Authorization': `Bearer ${process.env.PANEL_APPLICATION_KEY}`,
        'Accept': 'application/json'
      }
    });
    if (panelRes.ok) {
      const panelData = await panelRes.json();
      const nodeMap = { 2: 'NC1', 3: 'TX1' };
      gameServers = panelData.data.map(s => ({
        name: s.attributes.name,
        uuid: s.attributes.uuid,
        node: nodeMap[s.attributes.node] || `Node ${s.attributes.node}`,
        nodeId: s.attributes.node,
        ram: s.attributes.limits.memory,
        disk: s.attributes.limits.disk,
        suspended: s.attributes.suspended
      }));
    }
  } catch (err) {
    console.error('[INFRA] Pterodactyl API error:', err.message);
  }

  const fleetData = {
    servers: audit,
    gameServers,
    fetchedAt: new Date().toISOString(),
    summary: {
      totalServers: serverIds.length,
      online: Object.values(audit).filter(s => s.online).length,
      needRestart: Object.values(audit).filter(s => s.restart).length,
      totalGameServers: gameServers.length,
      tx1Games: gameServers.filter(g => g.node === 'TX1').length,
      nc1Games: gameServers.filter(g => g.node === 'NC1').length
    }
  };

  auditCache = { data: fleetData, lastFetch: now };
  console.log(`[INFRA] Fleet audit complete: ${fleetData.summary.online}/${fleetData.summary.totalServers} online`);

  return fleetData;
}

// GET /admin/infrastructure — Main page
router.get('/', async (req, res) => {
  try {
    const fleet = await fetchFleetAudit();
    res.render('admin/infrastructure/index', {
      title: 'Infrastructure',
      currentPath: '/infrastructure',
      fleet,
      adminUser: req.user,
      layout: 'layout'
    });
  } catch (err) {
    console.error('[INFRA] Route error:', err);
    res.render('admin/infrastructure/index', {
      title: 'Infrastructure',
      currentPath: '/infrastructure',
      fleet: null,
      error: err.message,
      adminUser: req.user,
      layout: 'layout'
    });
  }
});

// GET /admin/infrastructure/refresh — Force refresh (HTMX)
router.get('/refresh', async (req, res) => {
  auditCache = { data: null, lastFetch: 0 };
  try {
    const fleet = await fetchFleetAudit();
    res.json({ success: true, fleet, fetchedAt: fleet.fetchedAt });
  } catch (err) {
    res.status(500).json({ success: false, error: err.message });
  }
});

// GET /admin/infrastructure/server/:id — Server detail (JSON for client)
router.get('/server/:id', async (req, res) => {
  const fleet = await fetchFleetAudit();
  const server = fleet.servers[req.params.id];
  if (!server) return res.status(404).json({ error: 'Server not found' });

  const games = fleet.gameServers.filter(g => {
    if (req.params.id === 'tx1-dallas') return g.node === 'TX1';
    if (req.params.id === 'nc1-charlotte') return g.node === 'NC1';
    return false;
  });

  res.json({ server, games });
});

module.exports = router;
