/**
 * Function/System/live-monitor.js
 * ---------------------------------------------------------------------------
 * LIVE SYSTEM MONITOR
 *
 * A terminal dashboard that refreshes until the user stops it: CPU, memory,
 * disk activity, network throughput and uptime. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

const os = require('os');
const kit = require('../../core/kit');
const system = require('../../core/system');

/** One sample of everything the dashboard shows. */
const SAMPLE_QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '$cpu = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average',
  '$osi = Get-CimInstance Win32_OperatingSystem',
  '$disk = Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk | Where-Object { $_.Name -eq "_Total" } | Select-Object -First 1 PercentDiskTime, DiskReadBytesPerSec, DiskWriteBytesPerSec',
  '$net = Get-CimInstance Win32_PerfFormattedData_Tcpip_NetworkInterface | Measure-Object -Property BytesReceivedPersec, BytesSentPersec -Sum',
  '$vol = Get-CimInstance Win32_LogicalDisk -Filter "DriveType = 3" | Select-Object DeviceID, Size, FreeSpace',
  '[ordered]@{',
  '  cpu = $cpu;',
  '  memTotal = $osi.TotalVisibleMemorySize;',
  '  memFree = $osi.FreePhysicalMemory;',
  '  processes = $osi.NumberOfProcesses;',
  '  diskBusy = $disk.PercentDiskTime;',
  '  diskRead = $disk.DiskReadBytesPerSec;',
  '  diskWrite = $disk.DiskWriteBytesPerSec;',
  '  netIn = (($net | Where-Object { $_.Property -eq "BytesReceivedPersec" }).Sum);',
  '  netOut = (($net | Where-Object { $_.Property -eq "BytesSentPersec" }).Sum);',
  '  volumes = @($vol)',
  '} | ConvertTo-Json -Depth 4 -Compress',
].join('\n');

async function sample() {
  const result = await system.runPowerShellJson(SAMPLE_QUERY, { timeoutMs: 25000 });
  if (!result.ok || !result.data) return null;
  const data = result.data;
  return {
    cpu: Number(data.cpu),
    memTotal: Number(data.memTotal) * 1024,
    memFree: Number(data.memFree) * 1024,
    processes: Number(data.processes),
    diskBusy: Number(data.diskBusy),
    diskRead: Number(data.diskRead),
    diskWrite: Number(data.diskWrite),
    netIn: Number(data.netIn),
    netOut: Number(data.netOut),
    volumes: system.asArray(data.volumes),
  };
}

/** Fallback sample built from Node itself when WMI counters are unavailable. */
function fallbackSample() {
  return {
    cpu: NaN,
    memTotal: os.totalmem(),
    memFree: os.freemem(),
    processes: NaN,
    diskBusy: NaN,
    diskRead: NaN,
    diskWrite: NaN,
    netIn: NaN,
    netOut: NaN,
    volumes: [],
  };
}

function rate(value) {
  return Number.isFinite(value) ? `${system.formatBytes(value)}/s` : null;
}

function render(ui, data, tick, intervalMs) {
  const memUsed = Math.max(0, data.memTotal - data.memFree);

  ui.clear();
  ui.blank();
  ui.box({
    title: 'LIVE SYSTEM MONITOR',
    lines: [
      ui.c.gray(`${os.hostname()}  -  refresh every ${Math.round(intervalMs / 1000)}s  -  sample ${tick}`),
      ui.c.gray('Press ENTER to stop.'),
    ],
  });
  ui.blank();

  ui.section('PERFORMANCE');
  ui.blank();
  ui.println(
    '  ' + ui.c.brightWhite(ui.padEnd('CPU', 12)) + ui.progressBar(Number.isFinite(data.cpu) ? data.cpu : 0, 100, 26)
  );
  ui.println('  ' + ui.c.brightWhite(ui.padEnd('Memory', 12)) + ui.progressBar(memUsed, data.memTotal || 1, 26));
  if (Number.isFinite(data.diskBusy)) {
    ui.println('  ' + ui.c.brightWhite(ui.padEnd('Disk', 12)) + ui.progressBar(Math.min(100, data.diskBusy), 100, 26));
  }

  ui.blank();
  kit.kv('Memory Used', `${system.formatBytes(memUsed)} of ${system.formatBytes(data.memTotal)}`);
  kit.kv('Memory Free', system.formatBytes(data.memFree));
  kit.kv('Processes', Number.isFinite(data.processes) ? system.formatNumber(data.processes) : null);
  kit.kv('Disk Read', rate(data.diskRead));
  kit.kv('Disk Write', rate(data.diskWrite));
  kit.kv('Network In', rate(data.netIn));
  kit.kv('Network Out', rate(data.netOut));
  kit.kv('Uptime', system.formatDuration(os.uptime()));

  if (data.volumes.length) {
    ui.blank();
    ui.section('DRIVES');
    ui.blank();
    for (const volume of data.volumes.slice(0, 6)) {
      const size = Number(volume.Size) || 0;
      const free = Number(volume.FreeSpace) || 0;
      ui.println(
        '  ' + ui.c.brightWhite(ui.padEnd(String(volume.DeviceID), 6)) +
          ui.progressBar(size - free, size || 1, 20) +
          ui.c.gray(`  ${system.formatBytes(free)} free`)
      );
    }
  }
}

module.exports = {
  name: 'Live System Monitor',
  menuName: 'Live System Monitor',
  description: 'Auto-refreshing dashboard: CPU, memory, disk, network and uptime.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 100,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query'],
  tags: ['monitor', 'live', 'dashboard', 'cpu', 'memory', 'network'],
  config: { refreshSeconds: 2 },
  documentation:
    'Shows a live dashboard of CPU load, memory usage, disk activity, network throughput, running ' +
    'processes, uptime and free space per drive. It refreshes on a fixed interval until ENTER is ' +
    'pressed. Values come from the Windows performance counters; if those are unavailable the ' +
    'monitor falls back to what Node.js itself can report.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await sample();
    return data ? { ok: true, message: 'performance counters available' } : { ok: false, message: 'counters unavailable' };
  },

  async execute(ctx) {
    const { ui, prompt, config } = ctx;
    kit.header('Live System Monitor', ['Live CPU, memory, disk and network usage.']);
    if (!kit.requireWindows('The live monitor')) return { ok: false, reason: 'unsupported-platform' };

    const intervalMs = Math.max(1, Number(config.refreshSeconds) || 2) * 1000;
    let stop = false;
    let tick = 0;

    ui.info('Starting live view - press ENTER at any time to stop.');
    // The prompt resolves as soon as the user presses ENTER; the loop watches it.
    prompt.ask('').then(() => {
      stop = true;
    });

    let warned = false;
    while (!stop) {
      tick += 1;
      let data = await sample();
      if (!data) {
        data = fallbackSample();
        if (!warned) {
          warned = true;
          ctx.logger.warn('live-monitor', 'Performance counters unavailable - using the Node.js fallback');
        }
      }
      if (stop) break;
      render(ui, data, tick, intervalMs);
      if (warned) {
        ui.blank();
        ui.warning('Windows performance counters are unavailable - showing reduced information.');
      }

      // Sleep in small slices so ENTER stops the view quickly.
      const until = Date.now() + intervalMs;
      while (!stop && Date.now() < until) {
        await new Promise((resolve) => setTimeout(resolve, 100));
      }
    }

    ui.blank();
    return kit.done(`Live view stopped after ${tick} sample(s).`);
  },
};
