/**
 * Function/Windows/device-summary.js
 * ---------------------------------------------------------------------------
 * DEVICE MANAGER SUMMARY
 *
 * The hardware Windows knows about, grouped into the familiar Device Manager
 * categories, with devices reporting an error highlighted. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

/** Device Manager categories mapped to PnP class names. */
const CATEGORIES = [
  { label: 'Display Adapters', classes: ['Display'] },
  { label: 'Network Adapters', classes: ['Net'] },
  { label: 'Audio Devices', classes: ['MEDIA', 'AudioEndpoint'] },
  { label: 'Storage Controllers', classes: ['SCSIAdapter', 'HDC'] },
  { label: 'Disk Drives', classes: ['DiskDrive'] },
  { label: 'USB Devices', classes: ['USB', 'USBDevice'] },
  { label: 'Bluetooth', classes: ['Bluetooth'] },
  { label: 'Keyboards', classes: ['Keyboard'] },
  { label: 'Mice & Pointing Devices', classes: ['Mouse', 'HIDClass'] },
  { label: 'Monitors', classes: ['Monitor'] },
  { label: 'Printers', classes: ['Printer', 'PrintQueue'] },
  { label: 'Processors', classes: ['Processor'] },
  { label: 'System Devices', classes: ['System'] },
  { label: 'Other Hardware', classes: null },
];

const ERROR_CODES = {
  1: 'not configured correctly',
  3: 'driver corrupted or low memory',
  10: 'cannot start',
  12: 'not enough resources',
  14: 'restart required',
  18: 'drivers must be reinstalled',
  22: 'disabled',
  24: 'not present or not working',
  28: 'drivers not installed',
  31: 'Windows cannot load the drivers',
  43: 'the device reported a problem and was stopped',
  45: 'not connected',
};

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '@(Get-CimInstance Win32_PnPEntity | Select-Object Name, Manufacturer, PNPClass, Status, ConfigManagerErrorCode, DeviceID, Present) | ConvertTo-Json -Depth 4 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 120000 });
  return result.ok ? system.asArray(result.data) : null;
}

/** Put each device into exactly one Device Manager category. */
function group(devices) {
  const used = new Set();
  const groups = [];

  for (const category of CATEGORIES) {
    if (!category.classes) continue;
    const members = devices.filter(
      (device) => category.classes.includes(String(device.PNPClass)) && !used.has(device)
    );
    members.forEach((device) => used.add(device));
    groups.push({ label: category.label, devices: members });
  }

  groups.push({ label: 'Other Hardware', devices: devices.filter((device) => !used.has(device)) });
  return groups;
}

module.exports = {
  name: 'Device Manager Summary',
  menuName: 'Device Summary',
  description: 'Detected hardware grouped into Device Manager categories, with errors highlighted.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Windows',
  order: 40,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'filesystem:write(reports)'],
  tags: ['devices', 'device manager', 'hardware', 'pnp'],
  documentation:
    'Enumerates every Plug and Play device Windows knows about and groups it the way Device Manager ' +
    'does - display adapters, network adapters, audio, storage controllers, disks, USB, Bluetooth, ' +
    'keyboards, mice, monitors, printers, processors, system devices and everything else. Devices ' +
    'reporting a Device Manager error code are listed first with the code translated. Read-only.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const devices = await collect();
    return devices && devices.length
      ? { ok: true, message: `${devices.length} device(s) enumerated` }
      : { ok: false, message: 'no device data' };
  },

  async execute(ctx) {
    const { ui, prompt } = ctx;
    kit.header('Device Summary', ['Hardware detected by Windows, by category.']);
    if (!kit.requireWindows('The device summary')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Enumerating devices...');
    const devices = await collect();
    spin.stop();
    if (!devices || !devices.length) return kit.noData('device information');

    const problems = devices.filter((device) => Number(device.ConfigManagerErrorCode) > 0);
    const groups = group(devices).filter((entry) => entry.devices.length);

    ui.section('SUMMARY');
    ui.blank();
    kit.kv('Devices Detected', system.formatNumber(devices.length));
    kit.kv('Categories', String(groups.length));
    kit.kv('Devices With Errors', system.formatNumber(problems.length), problems.length ? 'brightRed' : 'brightGreen');

    if (problems.length) {
      ui.blank();
      ui.section('DEVICES REPORTING AN ERROR', 'brightRed');
      ui.blank();
      for (const device of problems.slice(0, 12)) {
        const code = Number(device.ConfigManagerErrorCode);
        ui.println('  ' + ui.c.brightRed(ui.glyphs().cross + ' ') + ui.c.brightWhite(ui.truncate(system.clean(device.Name) || 'Unknown device', 42)));
        ui.println('      ' + ui.c.gray(`Code ${code}: ${ERROR_CODES[code] || 'see Device Manager'}`));
      }
      if (problems.length > 12) ui.println('  ' + ui.c.gray(`... and ${problems.length - 12} more`));
    }

    ui.blank();
    ui.section('CATEGORIES');
    ui.blank();
    kit.table(
      ['CATEGORY', 'DEVICES', 'WITH ERRORS'],
      groups.map((entry) => [
        ui.c.brightWhite(entry.label),
        ui.c.brightCyan(String(entry.devices.length)),
        entry.devices.some((d) => Number(d.ConfigManagerErrorCode) > 0)
          ? ui.c.brightRed(String(entry.devices.filter((d) => Number(d.ConfigManagerErrorCode) > 0).length))
          : ui.c.gray('0'),
      ]),
      { align: ['left', 'right', 'right'], limit: 20 }
    );

    for (;;) {
      ui.blank();
      ui.menu(
        [
          { key: '1', label: 'Show the devices of a category' },
          { key: '2', label: 'Search for a device' },
          { key: '3', label: 'Export the full device list' },
          { separator: true },
          { key: '0', label: 'Back', color: 'gray' },
        ],
        { title: 'DEVICE SUMMARY' }
      );
      ui.blank();

      const answer = await prompt.select();
      if (!answer && prompt.isClosed()) break;
      if (answer === '0') break;

      if (answer === '1') {
        const index = await prompt.choose(
          'Which category?',
          groups.map((entry) => ({ label: entry.label, hint: `${entry.devices.length} device(s)` }))
        );
        if (index === null) continue;
        const entry = groups[index];
        ui.blank();
        ui.section(entry.label.toUpperCase());
        ui.blank();
        kit.table(
          ['DEVICE', 'MANUFACTURER', 'STATUS'],
          entry.devices.map((device) => [
            ui.c.brightWhite(ui.truncate(system.clean(device.Name) || 'Unknown', 34)),
            ui.c.gray(ui.truncate(system.clean(device.Manufacturer) || '-', 20)),
            Number(device.ConfigManagerErrorCode) > 0
              ? ui.c.brightRed(`Error ${device.ConfigManagerErrorCode}`)
              : ui.c.brightGreen(system.clean(device.Status) || 'OK'),
          ]),
          { limit: 25 }
        );
        await prompt.pause();
      } else if (answer === '2') {
        const term = (await prompt.text('Device name contains')).trim().toLowerCase();
        if (!term) continue;
        const matches = devices.filter((device) => String(device.Name || '').toLowerCase().includes(term));
        ui.blank();
        ui.section(`SEARCH: ${term.toUpperCase()} (${matches.length})`);
        ui.blank();
        if (!matches.length) {
          ui.println('  ' + ui.c.gray('No device matches.'));
        } else {
          kit.table(
            ['DEVICE', 'CLASS', 'STATUS'],
            matches.map((device) => [
              ui.c.brightWhite(ui.truncate(system.clean(device.Name) || 'Unknown', 34)),
              ui.c.gray(system.clean(device.PNPClass) || '-'),
              Number(device.ConfigManagerErrorCode) > 0
                ? ui.c.brightRed(`Error ${device.ConfigManagerErrorCode}`)
                : ui.c.brightGreen(system.clean(device.Status) || 'OK'),
            ]),
            { limit: 25 }
          );
        }
        await prompt.pause();
      } else if (answer === '3') {
        const report = kit.report('Device Manager Summary');
        const section = report.section('SUMMARY');
        section.add('Devices Detected', system.formatNumber(devices.length));
        section.add('Devices With Errors', system.formatNumber(problems.length));

        for (const entry of groups) {
          const group2 = report.section(entry.label.toUpperCase());
          entry.devices.forEach((device, index) => {
            group2.add(
              `${index + 1}. ${system.clean(device.Name) || 'Unknown'}`,
              [system.clean(device.Manufacturer), system.clean(device.Status), Number(device.ConfigManagerErrorCode) > 0 ? `ERROR ${device.ConfigManagerErrorCode}` : null]
                .filter(Boolean)
                .join(' | ')
            );
          });
        }
        kit.saveReport(report, 'DeviceSummary');
        await prompt.pause();
      } else {
        ui.warning('Enter one of the numbers shown above, or 0 to go back.');
        await prompt.pause();
      }
    }

    return kit.done('Device summary closed.');
  },
};
