/**
 * Function/System/quick-summary.js
 * ---------------------------------------------------------------------------
 * QUICK PC SUMMARY
 *
 * One compact screen with the essentials: CPU, GPU, RAM, motherboard,
 * storage, Windows, IP and uptime - without running a full hardware scan.
 * Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '[ordered]@{',
  '  cs = (Get-CimInstance Win32_ComputerSystem | Select-Object -First 1 Name, Manufacturer, Model, TotalPhysicalMemory, NumberOfLogicalProcessors);',
  '  osi = (Get-CimInstance Win32_OperatingSystem | Select-Object -First 1 Caption, Version, BuildNumber, OSArchitecture, LastBootUpTime, FreePhysicalMemory, TotalVisibleMemorySize);',
  '  cpu = (Get-CimInstance Win32_Processor | Select-Object -First 1 Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed, LoadPercentage);',
  '  gpu = @(Get-CimInstance Win32_VideoController | Select-Object Name, DriverVersion, CurrentHorizontalResolution, CurrentVerticalResolution, CurrentRefreshRate);',
  '  board = (Get-CimInstance Win32_BaseBoard | Select-Object -First 1 Manufacturer, Product, Version);',
  '  bios = (Get-CimInstance Win32_BIOS | Select-Object -First 1 Manufacturer, SMBIOSBIOSVersion, ReleaseDate);',
  '  mem = @(Get-CimInstance Win32_PhysicalMemory | Select-Object Capacity, Speed, Manufacturer);',
  '  disks = @(Get-CimInstance Win32_DiskDrive | Select-Object Model, Size, MediaType);',
  '  volumes = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType = 3" | Select-Object DeviceID, Size, FreeSpace);',
  '  net = @(Get-CimInstance Win32_NetworkAdapterConfiguration -Filter "IPEnabled = True" | Select-Object Description, IPAddress, DefaultIPGateway)',
  '} | ConvertTo-Json -Depth 5 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 60000 });
  if (!result.ok || !result.data) return null;
  const data = result.data;
  return {
    cs: data.cs || {},
    osi: data.osi || {},
    cpu: data.cpu || {},
    gpu: system.asArray(data.gpu),
    board: data.board || {},
    bios: data.bios || {},
    mem: system.asArray(data.mem),
    disks: system.asArray(data.disks),
    volumes: system.asArray(data.volumes),
    net: system.asArray(data.net),
  };
}

/** Virtual display drivers should not be reported as "the" GPU. */
const VIRTUAL_GPU = /virtual|parsec|remote|meta|basic display|idd|citrix|vmware|oray|sunshine/i;

/** The adapter a user would call their graphics card. */
function primaryGpu(list) {
  return list.find((card) => !VIRTUAL_GPU.test(String(card.Name || ''))) || list[0] || {};
}

/** VPN and virtual adapters should not hide the real network connection. */
const VIRTUAL_NIC = /virtual|vpn|radmin|hamachi|tap-|tun|loopback|vethernet|hyper-v|vmware|virtualbox|zerotier|tailscale|wintun/i;

/**
 * First usable IPv4 address, ranked: physical adapter with a default gateway,
 * then any adapter with a gateway, then anything else.
 */
function primaryIp(data) {
  const hasGateway = (config) => system.asArray(config.DefaultIPGateway).filter(Boolean).length > 0;
  const isPhysical = (config) => !VIRTUAL_NIC.test(String(config.Description || ''));

  const ranked = [
    ...data.net.filter((c) => hasGateway(c) && isPhysical(c)),
    ...data.net.filter((c) => hasGateway(c)),
    ...data.net.filter(isPhysical),
    ...data.net,
  ];

  for (const config of ranked) {
    const ipv4 = system.asArray(config.IPAddress).find((ip) => /^\d+\.\d+\.\d+\.\d+$/.test(String(ip)));
    if (ipv4) return { ip: ipv4, adapter: system.clean(config.Description) };
  }
  // Fallback: Node's own interface list, physical adapters first.
  const candidates = [];
  for (const [name, entries] of Object.entries(os.networkInterfaces())) {
    for (const entry of entries || []) {
      if (entry.internal || entry.family !== 'IPv4') continue;
      candidates.push({ ip: entry.address, adapter: name, physical: !VIRTUAL_NIC.test(name) });
    }
  }
  const best = candidates.find((c) => c.physical) || candidates[0];
  return best ? { ip: best.ip, adapter: best.adapter } : { ip: null, adapter: null };
}

function buildFields(data) {
  const memoryTotal = Number(data.cs.TotalPhysicalMemory) || os.totalmem();
  const memoryFree = Number(data.osi.FreePhysicalMemory) ? Number(data.osi.FreePhysicalMemory) * 1024 : os.freemem();
  const speeds = [...new Set(data.mem.map((m) => Number(m.Speed)).filter(Boolean))];
  const gpu = primaryGpu(data.gpu);
  const network = primaryIp(data);
  const totalStorage = data.disks.reduce((sum, disk) => sum + (Number(disk.Size) || 0), 0);
  const freeStorage = data.volumes.reduce((sum, volume) => sum + (Number(volume.FreeSpace) || 0), 0);

  return [
    ['Computer', system.firstOf(data.cs, ['Name']) || os.hostname()],
    ['System', [system.clean(data.cs.Manufacturer), system.clean(data.cs.Model)].filter(Boolean).join(' ') || null],
    [
      'CPU',
      system.clean(data.cpu.Name)
        ? `${system.clean(data.cpu.Name)}  (${data.cpu.NumberOfCores || '?'}C/${data.cpu.NumberOfLogicalProcessors || '?'}T @ ${data.cpu.MaxClockSpeed || '?'} MHz)`
        : null,
    ],
    ['CPU Load', data.cpu.LoadPercentage !== undefined && data.cpu.LoadPercentage !== null ? `${data.cpu.LoadPercentage} %` : null],
    ['GPU', system.clean(gpu.Name)],
    [
      'Display',
      gpu.CurrentHorizontalResolution
        ? `${gpu.CurrentHorizontalResolution} x ${gpu.CurrentVerticalResolution} @ ${gpu.CurrentRefreshRate || '?'} Hz`
        : null,
    ],
    [
      'RAM',
      `${system.formatBytes(memoryTotal)}${data.mem.length ? ` (${data.mem.length} module${data.mem.length === 1 ? '' : 's'}` : ''}${speeds.length ? `, ${speeds.join('/')} MHz)` : data.mem.length ? ')' : ''}`,
    ],
    ['RAM Free', `${system.formatBytes(memoryFree)} of ${system.formatBytes(memoryTotal)}`],
    [
      'Motherboard',
      [system.clean(data.board.Manufacturer), system.clean(data.board.Product)].filter(Boolean).join(' ') || null,
    ],
    [
      'BIOS',
      [system.clean(data.bios.Manufacturer), system.clean(data.bios.SMBIOSBIOSVersion)].filter(Boolean).join(' ') || null,
    ],
    ['Storage', totalStorage ? `${data.disks.length} drive(s), ${system.formatBytes(totalStorage)} total` : null],
    ['Storage Free', freeStorage ? system.formatBytes(freeStorage) : null],
    [
      'Windows',
      system.clean(data.osi.Caption)
        ? `${system.clean(data.osi.Caption)} ${system.clean(data.osi.OSArchitecture) || ''} (build ${system.clean(data.osi.BuildNumber) || '?'})`
        : null,
    ],
    ['IP Address', network.ip ? `${network.ip}${network.adapter ? `  (${network.adapter})` : ''}` : null],
    ['Last Boot', system.formatWmiDate(data.osi.LastBootUpTime)],
    ['Uptime', system.formatDuration(os.uptime())],
  ];
}

module.exports = {
  name: 'Quick PC Summary',
  menuName: 'Quick PC Summary',
  description: 'Compact overview: CPU, GPU, RAM, motherboard, storage, Windows, IP and uptime.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 5,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'filesystem:write(reports)'],
  tags: ['summary', 'overview', 'quick', 'specs'],
  documentation:
    'A single screen with the information people usually ask for - processor, graphics card, memory, ' +
    'motherboard, storage, Windows version, local IP address and uptime - without running the full ' +
    'hardware scan. Ideal when someone just needs the specs of the machine.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await collect();
    return data && system.clean(data.cpu.Name)
      ? { ok: true, message: 'summary data available' }
      : { ok: false, message: 'summary query failed' };
  },

  reportProvider: {
    id: 'summary',
    label: 'Quick Summary',
    order: 5,
    async build(report) {
      const data = await collect();
      const section = report.section('QUICK SUMMARY');
      if (!data) {
        section.note('The quick summary was not available during export.');
        return;
      }
      for (const [label, value] of buildFields(data)) section.add(label, value);
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Quick PC Summary', ['The essentials of this machine at a glance.']);
    if (!kit.requireWindows('The quick summary')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Collecting the essentials...');
    const data = await collect();
    spin.stop();
    if (!data) return kit.noData('system information');

    const fields = buildFields(data);
    ui.section('SYSTEM');
    ui.blank();
    for (const [label, value] of fields) kit.kv(label, value);

    ui.blank();
    ui.section('DRIVES');
    ui.blank();
    for (const volume of data.volumes) {
      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 of ${system.formatBytes(size)}`)
      );
    }

    await kit.offerExport('Quick PC Summary', 'QuickSummary', (report) => {
      const section = report.section('QUICK SUMMARY');
      for (const [label, value] of fields) section.add(label, value);
      const drives = section.group('Drives');
      for (const volume of data.volumes) {
        drives.add(
          String(volume.DeviceID),
          `${system.formatBytes(volume.FreeSpace)} free of ${system.formatBytes(volume.Size)}`
        );
      }
    });

    return kit.done('Summary complete.');
  },
};
