/**
 * Function/hardware-info.js
 * ---------------------------------------------------------------------------
 * COMPUTER HARDWARE / HWID INFORMATION
 *
 * Performs a read-only scan of the machine and writes a sectioned .txt report
 * to the reports/ folder.
 *
 * READ-ONLY GUARANTEE
 *   This module only queries information that Windows already exposes through
 *   WMI/CIM, the registry and the Node.js os module. It never writes, patches
 *   or spoofs any hardware identifier.
 *
 * Report file name:  HH-MM_DD-MM-YYYY_CunputerHWID.txt
 *                    e.g. 19-56_24-08-2026_CunputerHWID.txt
 * ---------------------------------------------------------------------------
 */

'use strict';

const os = require('os');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { execFile } = require('child_process');

const system = require('../../core/system');
const { createReport } = require('../../core/report');
const paths = require('../../core/paths');
const logger = require('../../core/logger');

const SCRIPT = path.join(paths.scripts, 'hardware-query.ps1');

/* --------------------------------------------------------------------------
 * WMI enumeration maps
 * ------------------------------------------------------------------------ */

const CHASSIS_TYPES = {
  1: 'Other', 2: 'Unknown', 3: 'Desktop', 4: 'Low Profile Desktop', 5: 'Pizza Box',
  6: 'Mini Tower', 7: 'Tower', 8: 'Portable', 9: 'Laptop', 10: 'Notebook',
  11: 'Hand Held', 12: 'Docking Station', 13: 'All in One', 14: 'Sub Notebook',
  15: 'Space-Saving', 16: 'Lunch Box', 17: 'Main System Chassis', 18: 'Expansion Chassis',
  19: 'Sub Chassis', 20: 'Bus Expansion Chassis', 21: 'Peripheral Chassis',
  22: 'Storage Chassis', 23: 'Rack Mount Chassis', 24: 'Sealed-Case PC',
  30: 'Tablet', 31: 'Convertible', 32: 'Detachable', 35: 'Mini PC',
};

const PC_SYSTEM_TYPES = {
  0: 'Unspecified', 1: 'Desktop', 2: 'Mobile / Laptop', 3: 'Workstation',
  4: 'Enterprise Server', 5: 'SOHO Server', 6: 'Appliance PC',
  7: 'Performance Server', 8: 'Maximum',
};

const CPU_ARCHITECTURE = {
  0: 'x86', 1: 'MIPS', 2: 'Alpha', 3: 'PowerPC', 5: 'ARM', 6: 'Itanium', 9: 'x64', 12: 'ARM64',
};

const MEMORY_TYPES = {
  0: 'Unknown', 1: 'Other', 2: 'DRAM', 3: 'Synchronous DRAM', 4: 'Cache DRAM',
  17: 'SDRAM', 18: 'SGRAM', 20: 'DDR', 21: 'DDR2', 22: 'DDR2 FB-DIMM',
  24: 'DDR3', 25: 'FBD2', 26: 'DDR4', 27: 'LPDDR', 28: 'LPDDR2', 29: 'LPDDR3',
  30: 'LPDDR4', 31: 'Logical non-volatile device', 32: 'HBM', 33: 'HBM2',
  34: 'DDR5', 35: 'LPDDR5',
};

const FORM_FACTORS = {
  0: 'Unknown', 1: 'Other', 2: 'SIP', 3: 'DIP', 4: 'ZIP', 5: 'SOJ', 6: 'Proprietary',
  7: 'SIMM', 8: 'DIMM', 9: 'TSOP', 10: 'PGA', 11: 'RIMM', 12: 'SODIMM',
  13: 'SRIMM', 14: 'SMD', 15: 'SSMP', 16: 'QFP', 17: 'TQFP', 18: 'SOIC',
  19: 'LCC', 20: 'PLCC', 21: 'BGA', 22: 'FPBGA', 23: 'LGA',
};

const NET_STATUS = {
  0: 'Disconnected', 1: 'Connecting', 2: 'Connected', 3: 'Disconnecting',
  4: 'Hardware not present', 5: 'Hardware disabled', 6: 'Hardware malfunction',
  7: 'Media disconnected', 8: 'Authenticating', 9: 'Authentication succeeded',
  10: 'Authentication failed', 11: 'Invalid address', 12: 'Credentials required',
};

const BUS_TYPES = {
  0: 'Unknown', 1: 'SCSI', 2: 'ATAPI', 3: 'ATA', 4: 'IEEE 1394', 5: 'SSA', 6: 'Fibre Channel',
  7: 'USB', 8: 'RAID', 9: 'iSCSI', 10: 'SAS', 11: 'SATA', 12: 'SD', 13: 'MMC',
  15: 'File Backed Virtual', 16: 'Storage Spaces', 17: 'NVMe',
};

const PHYSICAL_MEDIA = { 0: 'Unspecified', 3: 'HDD', 4: 'SSD', 5: 'SCM' };

const BATTERY_STATUS = {
  1: 'Discharging', 2: 'On AC power', 3: 'Fully charged', 4: 'Low', 5: 'Critical',
  6: 'Charging', 7: 'Charging (high)', 8: 'Charging (low)', 9: 'Charging (critical)',
  10: 'Undefined', 11: 'Partially charged',
};

const LICENSE_STATUS = {
  0: 'Unlicensed', 1: 'Licensed (activated)', 2: 'Out-of-box grace period',
  3: 'Out-of-tolerance grace period', 4: 'Non-genuine grace period',
  5: 'Notification mode', 6: 'Extended grace period',
};

function mapValue(map, value, fallback = null) {
  if (value === null || value === undefined || value === '') return fallback;
  const key = Number(Array.isArray(value) ? value[0] : value);
  if (!Number.isFinite(key)) return String(value);
  return map[key] !== undefined ? `${map[key]} (${key})` : `Code ${key}`;
}

/* --------------------------------------------------------------------------
 * Helpers
 * ------------------------------------------------------------------------ */

const { clean, firstOf, asArray, formatBytes, formatNumber, formatDuration, formatWmiDate, mask } = system;

function mhz(value) {
  const n = Number(value);
  return Number.isFinite(n) && n > 0 ? `${formatNumber(n)} MHz` : null;
}

function kb(value) {
  const n = Number(value);
  return Number.isFinite(n) && n > 0 ? formatBytes(n * 1024) : null;
}

function bytes(value) {
  const n = Number(value);
  return Number.isFinite(n) && n > 0 ? `${formatBytes(n)}  (${formatNumber(n)} bytes)` : null;
}

/** Track every identifier we surface so the fingerprint stays reproducible. */
function makeIdentifierCollector() {
  const list = [];
  return {
    push(label, value, opts = {}) {
      const cleaned = clean(value);
      if (cleaned) list.push({ label, value: cleaned, stable: opts.stable !== false, sensitive: opts.sensitive === true });
      return cleaned;
    },
    get all() {
      return list;
    },
    /** SHA-256 over the stable identifiers - computed locally, never sent anywhere. */
    fingerprint() {
      const stable = list.filter((i) => i.stable).map((i) => `${i.label}=${i.value}`).sort();
      if (!stable.length) return null;
      const hash = crypto.createHash('sha256').update(stable.join('|'), 'utf8').digest('hex').toUpperCase();
      return { hash, short: hash.match(/.{1,8}/g).slice(0, 4).join('-'), inputs: stable.length };
    },
  };
}

/* --------------------------------------------------------------------------
 * Report construction
 * ------------------------------------------------------------------------ */

function buildReport(data, ctx) {
  const cfg = ctx.settings.hardware;
  const report = createReport({
    title: 'COMPUTER HARDWARE / HWID INFORMATION REPORT',
    subtitle: 'Generated by SNIPERX HFX',
  });

  const now = new Date();
  report.headerNote(`Generated       : ${system.isoLocal(now)}`);
  report.headerNote(`Report type     : Read-only hardware identification scan`);
  report.headerNote(`Machine         : ${os.hostname()}`);
  report.headerNote(`Collected by    : ${os.userInfo().username}${ctx.isAdmin ? ' (administrator)' : ''}`);
  report.footerNote('This report was produced by reading values exposed by Windows.');
  report.footerNote('No hardware identifier was modified at any point.');

  const ids = makeIdentifierCollector();

  const d = data || {};
  const osInfo = d.os || {};
  const reg = d.registry || {};
  const cs = d.computerSystem || {};
  const product = d.computerProduct || {};
  const enclosure = d.enclosure || {};
  const board = d.baseBoard || {};
  const bios = d.bios || {};
  const licensing = d.licensing || {};
  const activation = d.activation || {};
  const cpus = asArray(d.processors);
  const memModules = asArray(d.memoryModules);
  const memArray = d.memoryArray || {};
  const gpus = asArray(d.videoControllers);
  const monitors = asArray(d.monitors);
  const disks = asArray(d.diskDrives);
  const physicalDisks = asArray(d.physicalDisks);
  const logicalDisks = asArray(d.logicalDisks);
  const adapters = asArray(d.networkAdapters);
  const netConfigs = asArray(d.networkConfigs);
  const sound = asArray(d.soundDevices);
  const battery = d.battery || {};

  /* ---------------------------------------------------------- SYSTEM ---- */
  const sys = report.section('SYSTEM');
  sys.add('Computer Name', firstOf(cs, ['Name']) || firstOf(reg, ['ComputerNameReg']) || os.hostname());
  sys.add('DNS Host Name', firstOf(cs, ['DNSHostName']));
  sys.add('Current User', firstOf(cs, ['UserName']) || `${os.userInfo().username}`);
  sys.add('Domain / Workgroup', firstOf(cs, ['Domain']) || firstOf(cs, ['Workgroup']));
  sys.add('Part of Domain', cs.PartOfDomain === undefined ? null : cs.PartOfDomain ? 'Yes' : 'No');
  sys.add('System Manufacturer', firstOf(cs, ['Manufacturer']) || firstOf(product, ['Vendor']));
  sys.add('System Model', firstOf(cs, ['Model']) || firstOf(product, ['Name']));
  sys.add('System Family', firstOf(cs, ['SystemFamily']));
  sys.add('System SKU', firstOf(cs, ['SystemSKUNumber']) || firstOf(product, ['SKUNumber']));
  sys.add('System Version', firstOf(product, ['Version']));
  sys.add('System Type', firstOf(cs, ['SystemType']));
  sys.add('PC System Type', mapValue(PC_SYSTEM_TYPES, cs.PCSystemType));
  sys.add('Chassis Type', mapValue(CHASSIS_TYPES, enclosure.ChassisTypes));
  sys.add('Chassis Manufacturer', firstOf(enclosure, ['Manufacturer']));
  sys.add('Asset Tag', firstOf(enclosure, ['SMBIOSAssetTag']));
  sys.add('Installed Memory', bytes(cs.TotalPhysicalMemory) || bytes(os.totalmem()));
  sys.add('Physical Processors', clean(cs.NumberOfProcessors));
  sys.add('Logical Processors', clean(cs.NumberOfLogicalProcessors) || String(os.cpus().length));
  sys.add('Hypervisor Present', cs.HypervisorPresent === undefined ? null : cs.HypervisorPresent ? 'Yes' : 'No');
  sys.add('Boot State', firstOf(cs, ['BootupState']));
  sys.add('Thermal State', firstOf(cs, ['ThermalState']));

  /* ------------------------------------------------ OPERATING SYSTEM ---- */
  const win = report.section('OPERATING SYSTEM');
  win.add('Windows Edition', firstOf(osInfo, ['Caption']) || firstOf(reg, ['ProductName']));
  win.add('Edition ID', firstOf(reg, ['EditionID']));
  win.add('Display Version', firstOf(reg, ['DisplayVersion']) || firstOf(reg, ['ReleaseId']));
  win.add('Version', firstOf(osInfo, ['Version']));
  win.add(
    'Build Number',
    reg.UBR !== undefined && reg.UBR !== null && clean(reg.CurrentBuild)
      ? `${clean(reg.CurrentBuild)}.${reg.UBR}`
      : firstOf(osInfo, ['BuildNumber'])
  );
  win.add('Build Lab', firstOf(reg, ['BuildLabEx']));
  win.add('Installation Type', firstOf(reg, ['InstallationType']));
  win.add('Architecture', firstOf(osInfo, ['OSArchitecture']) || process.arch);
  win.add('Install Date', formatWmiDate(osInfo.InstallDate) ||
    (Number(reg.InstallDateUnix) ? system.isoLocal(new Date(Number(reg.InstallDateUnix) * 1000)) : null));
  win.add('Last Boot', formatWmiDate(osInfo.LastBootUpTime));
  win.add('System Uptime', formatDuration(os.uptime()));
  win.add('Registered Owner', firstOf(reg, ['RegisteredOwner']) || firstOf(osInfo, ['RegisteredUser']));
  win.add('Registered Organization', firstOf(reg, ['RegisteredOrg']) || firstOf(osInfo, ['Organization']));
  win.add('System Drive', firstOf(osInfo, ['SystemDrive']));
  win.add('Windows Directory', firstOf(osInfo, ['WindowsDirectory']));
  win.add('Locale / Country', [clean(osInfo.Locale), clean(osInfo.CountryCode)].filter(Boolean).join(' / ') || null);
  win.add('Time Zone', firstOf(d.timeZone || {}, ['Caption', 'StandardName']));

  const productId = ids.push('WindowsProductId', firstOf(reg, ['ProductId']) || firstOf(osInfo, ['SerialNumber']));
  win.add('Windows Product ID', productId);
  win.add('Activation Status', mapValue(LICENSE_STATUS, activation.LicenseStatus));
  win.add('License Channel', firstOf(activation, ['Description']));
  win.add('Partial Product Key', firstOf(activation, ['PartialProductKey']));

  if (cfg.includeProductKey) {
    const key = clean(licensing.OA3xOriginalProductKey);
    win.add('OEM Product Key (BIOS)', key, { sensitive: true, masked: mask(key, 5, 5) });
    win.add('OEM Key Description', firstOf(licensing, ['OA3xOriginalProductKeyDescription']));
  } else {
    win.note('OEM product key collection is disabled in settings (hardware.includeProductKey).');
  }
  const clientMachineId = ids.push('ClientMachineID', firstOf(licensing, ['ClientMachineID']));
  win.add('Licensing Client Machine ID', clientMachineId);

  /* ------------------------------------------------------------- CPU ---- */
  const cpuSection = report.section('CPU');
  if (!cpus.length) {
    cpuSection.note('No processor information was returned by the operating system.');
  }
  cpus.forEach((cpu, index) => {
    const target = cpus.length > 1 ? cpuSection.group(`Processor ${index} (${clean(cpu.DeviceID) || 'CPU' + index})`) : cpuSection;
    target.add('CPU Name', firstOf(cpu, ['Name']) || firstOf(reg, ['ProcessorNameReg']));
    target.add('Manufacturer', firstOf(cpu, ['Manufacturer']));
    target.add('Description', firstOf(cpu, ['Description']) || firstOf(reg, ['ProcessorIdReg']));
    const procId = ids.push(`ProcessorId${index}`, cpu.ProcessorId);
    target.add('Processor ID', procId, { highlight: true });
    target.add('CPU Serial Number', ids.push(`CpuSerial${index}`, cpu.SerialNumber));
    target.add('Unique ID', ids.push(`CpuUniqueId${index}`, cpu.UniqueId));
    target.add('Part Number', firstOf(cpu, ['PartNumber']));
    target.add('Socket', firstOf(cpu, ['SocketDesignation']));
    target.add('Architecture', mapValue(CPU_ARCHITECTURE, cpu.Architecture));
    target.add('Family / Model / Stepping',
      [clean(cpu.Family), clean(cpu.Revision), clean(cpu.Stepping)].filter(Boolean).join(' / ') || null);
    target.add('Physical Cores', clean(cpu.NumberOfCores));
    target.add('Logical Processors', clean(cpu.NumberOfLogicalProcessors));
    target.add('Base Clock', mhz(cpu.MaxClockSpeed));
    target.add('Current Clock', mhz(cpu.CurrentClockSpeed));
    target.add('External Clock', mhz(cpu.ExtClock));
    target.add('L2 Cache', kb(cpu.L2CacheSize));
    target.add('L3 Cache', kb(cpu.L3CacheSize));
    target.add('Address Width', clean(cpu.AddressWidth) ? `${clean(cpu.AddressWidth)}-bit` : null);
    target.add('Data Width', clean(cpu.DataWidth) ? `${clean(cpu.DataWidth)}-bit` : null);
    target.add('Virtualization (Firmware)',
      cpu.VirtualizationFirmwareEnabled === undefined ? null : cpu.VirtualizationFirmwareEnabled ? 'Enabled' : 'Disabled');
    target.add('SLAT Support',
      cpu.SecondLevelAddressTranslationExtensions === undefined ? null
        : cpu.SecondLevelAddressTranslationExtensions ? 'Yes' : 'No');
    target.add('Current Load', clean(cpu.LoadPercentage) !== null ? `${clean(cpu.LoadPercentage)} %` : null);
    target.add('Status', firstOf(cpu, ['Status']));
  });

  /* ----------------------------------------------------- MOTHERBOARD ---- */
  const mb = report.section('MOTHERBOARD');
  mb.add('Manufacturer', firstOf(board, ['Manufacturer']), { highlight: true });
  mb.add('Model / Product', firstOf(board, ['Product', 'Model']), { highlight: true });
  mb.add('Version', firstOf(board, ['Version']));
  const boardSerial = ids.push('BaseBoardSerial', board.SerialNumber, { sensitive: true });
  mb.add('Serial Number', boardSerial, { sensitive: true, masked: mask(boardSerial) });
  mb.add('Part Number', firstOf(board, ['PartNumber']));
  mb.add('Tag', firstOf(board, ['Tag']));
  mb.add('Hosting Board', board.HostingBoard === undefined ? null : board.HostingBoard ? 'Yes' : 'No');
  mb.add('Status', firstOf(board, ['Status']));

  /* ------------------------------------------------------------ BIOS ---- */
  const biosSection = report.section('BIOS');
  biosSection.add('Manufacturer / Vendor', firstOf(bios, ['Manufacturer']));
  biosSection.add('BIOS Name', firstOf(bios, ['Name', 'Caption']));
  biosSection.add('SMBIOS BIOS Version', firstOf(bios, ['SMBIOSBIOSVersion']));
  biosSection.add('BIOS Version String', Array.isArray(bios.BIOSVersion) ? bios.BIOSVersion.filter(Boolean).join(' | ') : firstOf(bios, ['BIOSVersion']));
  biosSection.add('Registry BIOS Version', firstOf(reg, ['SystemBiosVersion']));
  biosSection.add('Version', firstOf(bios, ['Version']));
  biosSection.add('Release Date', formatWmiDate(bios.ReleaseDate));
  const biosSerial = ids.push('BiosSerial', bios.SerialNumber, { sensitive: true });
  biosSection.add('BIOS Serial Number', biosSerial, { sensitive: true, masked: mask(biosSerial) });
  biosSection.add('SMBIOS Version',
    clean(bios.SMBIOSMajorVersion) ? `${clean(bios.SMBIOSMajorVersion)}.${clean(bios.SMBIOSMinorVersion) || 0}` : null);
  biosSection.add('System BIOS Version',
    clean(bios.SystemBiosMajorVersion) ? `${clean(bios.SystemBiosMajorVersion)}.${clean(bios.SystemBiosMinorVersion) || 0}` : null);
  biosSection.add('Primary BIOS', bios.PrimaryBIOS === undefined ? null : bios.PrimaryBIOS ? 'Yes' : 'No');

  /* --------------------------------------------- SECURITY & FIRMWARE ---- */
  const sec = report.section('SECURITY & FIRMWARE');
  sec.add('Firmware Type', clean(d.firmwareType));
  sec.add('Secure Boot', d.secureBoot === undefined || d.secureBoot === null ? null : d.secureBoot ? 'Enabled' : 'Disabled');
  const tpm = d.tpm || {};
  const tpmPresent = Object.keys(tpm).length > 0;
  sec.add('TPM Present', tpmPresent ? 'Yes' : 'Not detected / access denied');
  if (tpmPresent) {
    sec.add('TPM Enabled', tpm.IsEnabled_InitialValue === undefined ? null : tpm.IsEnabled_InitialValue ? 'Yes' : 'No');
    sec.add('TPM Activated', tpm.IsActivated_InitialValue === undefined ? null : tpm.IsActivated_InitialValue ? 'Yes' : 'No');
    sec.add('TPM Owned', tpm.IsOwned_InitialValue === undefined ? null : tpm.IsOwned_InitialValue ? 'Yes' : 'No');
    sec.add('TPM Spec Version', firstOf(tpm, ['SpecVersion']));
    sec.add('TPM Manufacturer', firstOf(tpm, ['ManufacturerIdTxt']));
    sec.add('TPM Manufacturer Version', firstOf(tpm, ['ManufacturerVersion']));
  } else {
    sec.note('TPM details usually require administrator rights.');
  }

  /* ---------------------------------------------------------- MEMORY ---- */
  const mem = report.section('MEMORY');
  mem.add('Total Physical Memory', bytes(cs.TotalPhysicalMemory) || bytes(os.totalmem()));
  mem.add('Available Physical Memory', kb(osInfo.FreePhysicalMemory) || formatBytes(os.freemem()));
  mem.add('Total Visible Memory', kb(osInfo.TotalVisibleMemorySize));
  mem.add('Total Virtual Memory', kb(osInfo.TotalVirtualMemorySize));
  mem.add('Memory Slots (used/total)',
    memModules.length ? `${memModules.length} / ${clean(memArray.MemoryDevices) || memModules.length}` : null);
  mem.add('Max Supported Capacity', memArray.MaxCapacityEx ? formatBytes(Number(memArray.MaxCapacityEx) * 1024) : null);

  if (!memModules.length) {
    mem.note('No physical memory module details were returned.');
  }
  memModules.forEach((module, index) => {
    const label = clean(module.DeviceLocator) || clean(module.BankLabel) || `Module ${index + 1}`;
    const slot = mem.group(`Slot ${index + 1} - ${label}`);
    slot.add('Capacity', bytes(module.Capacity));
    slot.add('Manufacturer', firstOf(module, ['Manufacturer']));
    slot.add('Part Number', firstOf(module, ['PartNumber']));
    const memSerial = ids.push(`MemorySerial${index}`, module.SerialNumber, { sensitive: true });
    slot.add('Serial Number', memSerial, { sensitive: true, masked: mask(memSerial) });
    slot.add('Type', mapValue(MEMORY_TYPES, module.SMBIOSMemoryType ?? module.MemoryType));
    slot.add('Form Factor', mapValue(FORM_FACTORS, module.FormFactor));
    slot.add('Rated Speed', mhz(module.Speed));
    slot.add('Configured Speed', mhz(module.ConfiguredClockSpeed));
    slot.add('Configured Voltage', clean(module.ConfiguredVoltage) ? `${clean(module.ConfiguredVoltage)} mV` : null);
    slot.add('Bank Label', firstOf(module, ['BankLabel']));
    slot.add('Data / Total Width',
      [clean(module.DataWidth), clean(module.TotalWidth)].filter(Boolean).join(' / ') || null);
  });

  /* ------------------------------------------------------------- GPU ---- */
  const gpu = report.section('GPU');
  if (!gpus.length) gpu.note('No display adapter information was returned.');
  gpus.forEach((card, index) => {
    const target = gpus.length > 1 ? gpu.group(`Display Adapter ${index + 1}`) : gpu;
    target.add('GPU Name', firstOf(card, ['Name']), { highlight: true });
    target.add('Video Processor', firstOf(card, ['VideoProcessor']));
    target.add('Vendor', firstOf(card, ['AdapterCompatibility']));
    target.add('Dedicated Memory (reported)', bytes(card.AdapterRAM));
    target.add('Driver Version', firstOf(card, ['DriverVersion']));
    target.add('Driver Date', formatWmiDate(card.DriverDate));
    target.add('Current Mode', firstOf(card, ['VideoModeDescription']));
    target.add('Resolution',
      clean(card.CurrentHorizontalResolution)
        ? `${clean(card.CurrentHorizontalResolution)} x ${clean(card.CurrentVerticalResolution)} @ ${clean(card.CurrentRefreshRate) || '?'} Hz`
        : null);
    target.add('Colour Depth', clean(card.CurrentBitsPerPixel) ? `${clean(card.CurrentBitsPerPixel)}-bit` : null);
    target.add('PNP Device ID', ids.push(`GpuPnp${index}`, card.PNPDeviceID));
    target.add('Status', firstOf(card, ['Status']));
  });
  gpu.spacer();
  gpu.note('Note: "Dedicated Memory (reported)" comes from WMI and is capped at 4 GB for many adapters.');

  /* -------------------------------------------------------- MONITORS ---- */
  if (cfg.includeMonitors) {
    const mon = report.section('MONITORS');
    if (!monitors.length) mon.note('No monitor EDID information was returned.');
    monitors.forEach((screen, index) => {
      const target = mon.group(`Display ${index + 1}`);
      target.add('Manufacturer (EDID)', clean(screen.Manufacturer));
      target.add('Model', clean(screen.Model));
      const monSerial = ids.push(`MonitorSerial${index}`, screen.Serial, { sensitive: true, stable: false });
      target.add('Serial Number', monSerial, { sensitive: true, masked: mask(monSerial) });
      target.add('Product Code', clean(screen.ProductCode));
      target.add('Manufactured', clean(screen.YearOfManufacture)
        ? `Year ${clean(screen.YearOfManufacture)}${clean(screen.WeekOfManufacture) ? ', week ' + clean(screen.WeekOfManufacture) : ''}`
        : null);
      target.add('Instance Name', clean(screen.InstanceName));
    });
  }

  /* --------------------------------------------------------- STORAGE ---- */
  const storage = report.section('STORAGE');
  if (!disks.length) storage.note('No physical disk information was returned.');
  disks.forEach((disk, index) => {
    const target = storage.group(`Disk ${clean(disk.Index) ?? index} - ${clean(disk.Model) || 'Unknown model'}`);
    target.add('Disk Model', firstOf(disk, ['Model', 'Caption']), { highlight: true });
    target.add('Manufacturer', firstOf(disk, ['Manufacturer']));
    const diskSerial = ids.push(`DiskSerial${index}`, disk.SerialNumber, { sensitive: true });
    target.add('Serial Number', diskSerial, { sensitive: true, masked: mask(diskSerial) });
    target.add('Firmware Revision', firstOf(disk, ['FirmwareRevision']));
    target.add('Interface', firstOf(disk, ['InterfaceType']));
    target.add('Media Type', firstOf(disk, ['MediaType']));
    target.add('Capacity', bytes(disk.Size));
    target.add('Partitions', clean(disk.Partitions));
    target.add('Sector Size', clean(disk.BytesPerSector) ? `${clean(disk.BytesPerSector)} bytes` : null);
    target.add('Disk Signature', clean(disk.Signature));
    target.add('Device ID', clean(disk.DeviceID));
    target.add('PNP Device ID', ids.push(`DiskPnp${index}`, disk.PNPDeviceID));
    target.add('Status', firstOf(disk, ['Status']));

    // Match the storage-subsystem view (bus/media type, health) when available.
    const match = physicalDisks.find(
      (p) => clean(p.SerialNumber) && clean(p.SerialNumber) === clean(disk.SerialNumber)
    ) || physicalDisks[index];
    if (match) {
      target.add('Bus Type', mapValue(BUS_TYPES, match.BusType));
      target.add('Media Type (Storage API)', mapValue(PHYSICAL_MEDIA, match.MediaType));
      target.add('Health Status', clean(match.HealthStatus));
      target.add('Spindle Speed', clean(match.SpindleSpeed) && Number(match.SpindleSpeed) > 0 && Number(match.SpindleSpeed) < 4294967295
        ? `${formatNumber(match.SpindleSpeed)} RPM` : null);
      target.add('Unique ID', ids.push(`DiskUniqueId${index}`, match.UniqueId));
    }
  });

  storage.spacer();
  logicalDisks.forEach((vol) => {
    const target = storage.group(`Volume ${clean(vol.DeviceID) || '?'} ${clean(vol.VolumeName) ? '- ' + clean(vol.VolumeName) : ''}`.trim());
    target.add('File System', firstOf(vol, ['FileSystem']));
    target.add('Total Size', bytes(vol.Size));
    target.add('Free Space', bytes(vol.FreeSpace));
    const used = Number(vol.Size) - Number(vol.FreeSpace);
    target.add('Used Space', Number.isFinite(used) && used >= 0
      ? `${formatBytes(used)} (${Number(vol.Size) ? Math.round((used / Number(vol.Size)) * 100) : 0}%)` : null);
    target.add('Volume Serial Number', ids.push(`VolumeSerial${clean(vol.DeviceID)}`, vol.VolumeSerialNumber));
    target.add('Compressed', vol.Compressed === undefined ? null : vol.Compressed ? 'Yes' : 'No');
  });

  /* --------------------------------------------------------- NETWORK ---- */
  const net = report.section('NETWORK');
  const macs = [];
  if (!adapters.length) net.note('No physical network adapter information was returned.');
  adapters.forEach((adapter, index) => {
    const target = net.group(`Adapter ${index + 1} - ${clean(adapter.NetConnectionID) || clean(adapter.Name) || 'Unknown'}`);
    target.add('Name', firstOf(adapter, ['Name', 'ProductName']));
    target.add('Description', firstOf(adapter, ['Description']));
    target.add('Connection Name', firstOf(adapter, ['NetConnectionID']));
    target.add('Manufacturer', firstOf(adapter, ['Manufacturer']));
    target.add('Adapter Type', firstOf(adapter, ['AdapterType']));
    const mac = clean(adapter.MACAddress);
    if (mac) macs.push({ mac, name: clean(adapter.NetConnectionID) || clean(adapter.Name) });
    ids.push(`MAC${index}`, mac);
    target.add('MAC Address', mac, { highlight: true });
    target.add('Connection Status', mapValue(NET_STATUS, adapter.NetConnectionStatus));
    target.add('Link Speed', Number(adapter.Speed) > 0 ? `${formatNumber(Math.round(Number(adapter.Speed) / 1000000))} Mbps` : null);
    target.add('Adapter GUID', ids.push(`AdapterGuid${index}`, adapter.GUID));
    target.add('PNP Device ID', clean(adapter.PNPDeviceID));

    const config = netConfigs.find((c) => clean(c.MACAddress) === mac);
    if (config) {
      const ipv4 = asArray(config.IPAddress).filter((ip) => /^\d+\.\d+\.\d+\.\d+$/.test(String(ip)));
      const ipv6 = asArray(config.IPAddress).filter((ip) => String(ip).includes(':'));
      target.add('IPv4 Address', ipv4.join(', ') || null);
      target.add('IPv6 Address', ipv6.join(', ') || null);
      target.add('Subnet Mask', asArray(config.IPSubnet).filter((s) => /^\d+\.\d+\.\d+\.\d+$/.test(String(s))).join(', ') || null);
      target.add('Default Gateway', asArray(config.DefaultIPGateway).join(', ') || null);
      target.add('DNS Servers', asArray(config.DNSServerSearchOrder).join(', ') || null);
      target.add('DHCP Enabled', config.DHCPEnabled === undefined ? null : config.DHCPEnabled ? 'Yes' : 'No');
      target.add('DHCP Server', clean(config.DHCPServer));
      target.add('Setting ID', clean(config.SettingID));
    }
  });

  /* ----------------------------------------------------------- AUDIO ---- */
  if (sound.length) {
    const audio = report.section('AUDIO');
    sound.forEach((device, index) => {
      audio.add(`Device ${index + 1}`, firstOf(device, ['Name']));
      audio.add(`  Manufacturer`, firstOf(device, ['Manufacturer']));
      audio.add(`  Status`, firstOf(device, ['Status']));
    });
  }

  /* ----------------------------------------------------------- POWER ---- */
  if (clean(battery.Name)) {
    const power = report.section('POWER');
    power.add('Battery', firstOf(battery, ['Name']));
    power.add('Device ID', clean(battery.DeviceID));
    power.add('Charge Remaining', clean(battery.EstimatedChargeRemaining) !== null ? `${clean(battery.EstimatedChargeRemaining)} %` : null);
    power.add('Battery Status', mapValue(BATTERY_STATUS, battery.BatteryStatus));
    power.add('Design Voltage', clean(battery.DesignVoltage) ? `${clean(battery.DesignVoltage)} mV` : null);
  }

  /* --------------------------------------------- HARDWARE IDENTIFIERS ---- */
  const hwid = report.section('HARDWARE IDENTIFIERS');
  hwid.note('Identifiers below are read directly from the operating system and were NOT modified.');
  hwid.spacer();

  const systemUuid = ids.push('SystemUUID', product.UUID);
  hwid.add('System UUID (SMBIOS)', systemUuid, { highlight: true });
  hwid.add('System Identifying Number', ids.push('IdentifyingNumber', product.IdentifyingNumber, { sensitive: true }));
  hwid.add('Machine GUID (Cryptography)', ids.push('MachineGuid', reg.MachineGuid), { highlight: true });
  hwid.add('SQM Machine ID', ids.push('SQMMachineId', reg.SQMMachineId));
  hwid.add('Hardware Profile GUID', ids.push('HwProfileGuid', reg.HwProfileGuid));
  hwid.add('Windows Product ID', productId);
  hwid.add('Licensing Client Machine ID', clientMachineId);
  hwid.add('Processor ID', clean(cpus[0] && cpus[0].ProcessorId));
  hwid.add('Motherboard Serial', boardSerial, { sensitive: true, masked: mask(boardSerial) });
  hwid.add('BIOS Serial', biosSerial, { sensitive: true, masked: mask(biosSerial) });
  const chassisSerial = ids.push('ChassisSerial', enclosure.SerialNumber, { sensitive: true });
  hwid.add('Chassis Serial', chassisSerial, { sensitive: true, masked: mask(chassisSerial) });

  const primaryDiskSerial = clean(disks[0] && disks[0].SerialNumber);
  hwid.add('Primary Disk Serial', primaryDiskSerial, { sensitive: true, masked: mask(primaryDiskSerial) });
  const systemVolume = logicalDisks.find((v) => clean(v.DeviceID) === clean(osInfo.SystemDrive)) || logicalDisks[0];
  hwid.add('System Volume Serial', clean(systemVolume && systemVolume.VolumeSerialNumber));
  hwid.add('Primary MAC Address', macs.length ? macs[0].mac : null);
  hwid.add('All MAC Addresses', macs.length ? macs.map((m) => m.mac).join(', ') : null);

  hwid.spacer();
  const fingerprint = ids.fingerprint();
  if (fingerprint) {
    hwid.add('Composite Fingerprint (SHA-256)', fingerprint.hash);
    hwid.add('Short Fingerprint', fingerprint.short, { highlight: true });
    hwid.add('Fingerprint Inputs', `${fingerprint.inputs} identifiers`);
    hwid.note('The composite fingerprint is calculated locally by this tool from the identifiers above.');
    hwid.note('It is a convenience hash for support purposes - it is not a value stored by Windows.');
  }

  /* ------------------------------------------------ SCAN ENVIRONMENT ---- */
  const env = report.section('SCAN ENVIRONMENT');
  env.add('Scan Started', system.isoLocal(now));
  env.add('WMI Collection Time', clean(d.collectedAt));
  env.add('PowerShell Version', clean(d.psVersion));
  env.add('Elevated Scan', ctx.isAdmin ? 'Yes (administrator)' : 'No (some values may be unavailable)');
  env.add('Node.js Version', process.version);
  env.add('Platform', `${process.platform} ${process.arch}`);
  env.add('Utility Version', ctx.appVersion || '1.0.0');
  env.add('Data Points Collected', String(report.countFields()));

  return { report, fingerprint, identifierCount: ids.all.length };
}

/* --------------------------------------------------------------------------
 * Module entry point
 * ------------------------------------------------------------------------ */

async function execute(ctx) {
  const { ui, prompt, settings } = ctx;
  const cfg = settings.hardware;

  ui.blank();
  ui.box({
    title: 'COMPUTER HARDWARE / HWID INFORMATION',
    lines: [
      ui.c.gray('Read-only scan of this computer. No identifier is modified.'),
      ui.c.gray('A sectioned .txt report is written to the reports folder.'),
    ],
    color: 'brightCyan',
  });
  ui.blank();

  if (!system.IS_WINDOWS) {
    ui.error('This module requires Windows (WMI/CIM is unavailable on this platform).');
    return { ok: false, reason: 'unsupported-platform' };
  }

  ui.info(`Elevation      : ${ctx.isAdmin ? ui.c.brightGreen('Administrator') : ui.c.yellow('Standard user (some values may be hidden)')}`);
  ui.info(`Query script   : ${ui.c.white(paths.relative(SCRIPT))}`);
  ui.info(`Report folder  : ${ui.c.white(paths.relative(paths.reports))}`);
  ui.blank();

  logger.info('hardware-info', 'Hardware scan started', { elevated: ctx.isAdmin });

  const started = Date.now();
  const spin = ui.spinner('Querying Windows Management Instrumentation (this can take a moment)...');

  let result;
  try {
    result = await system.runPowerShellFileJson(SCRIPT, { timeoutMs: cfg.timeoutSeconds * 1000 });
  } catch (err) {
    spin.stop();
    ui.error(`The hardware query could not be started: ${err.message}`);
    logger.exception('hardware-info', 'Hardware scan failed to start', err);
    return { ok: false, reason: 'query-failed' };
  }

  if (!result.ok || !result.data) {
    spin.stop('ERROR', 'Hardware information could not be collected.');
    ui.blank();
    ui.warning('Windows did not return usable data for this scan.');
    if (result.stderr) ui.println('  ' + ui.c.gray(ui.truncate(result.stderr.split('\n')[0], ui.getWidth())));
    ui.println('  ' + ui.c.gray('Possible causes: the WMI service is stopped, PowerShell is restricted,'));
    ui.println('  ' + ui.c.gray('or security software is blocking WMI queries.'));
    logger.error('hardware-info', 'Hardware scan failed', result.stderr || 'unknown error');
    return { ok: false, reason: 'no-data' };
  }

  spin.stop('OK', `Hardware data collected in ${((Date.now() - started) / 1000).toFixed(1)}s`);

  // ---- build ------------------------------------------------------------
  let built;
  try {
    built = buildReport(result.data, ctx);
  } catch (err) {
    ui.error(`The report could not be assembled: ${err.message}`);
    logger.exception('hardware-info', 'Report assembly failed', err);
    return { ok: false, reason: 'build-failed' };
  }

  const { report, fingerprint } = built;

  ui.status('COMPLETED', ui.c.brightGreen(`${report.countFields()} data points collected`));
  report.print({ maskSensitive: cfg.maskSensitiveOnScreen });

  // ---- save -------------------------------------------------------------
  ui.blank();
  ui.section('REPORT');

  const fileName = `${logger.fileStamp()}_${cfg.fileNameSuffix}.txt`;
  const targetDir = path.isAbsolute(cfg.reportDirectory)
    ? cfg.reportDirectory
    : path.join(paths.root, cfg.reportDirectory);
  const target = path.join(targetDir, fileName);

  const saved = report.save(target);
  if (saved.ok) {
    ui.ok('Report saved successfully.');
    ui.blank();
    ui.println('  ' + ui.c.gray('File name  : ') + ui.c.brightWhite(fileName));
    ui.println('  ' + ui.c.gray('Location   : ') + ui.c.brightCyan(target));
    ui.println('  ' + ui.c.gray('Size       : ') + ui.c.brightWhite(system.formatBytes(saved.size)));
    if (cfg.maskSensitiveOnScreen) {
      ui.blank();
      ui.info(ui.c.gray('Serial numbers were masked on screen - the saved file contains the full values.'));
    }
    logger.info('hardware-info', 'Hardware report saved', target);
  } else {
    ui.error(`The report could not be saved: ${saved.error}`);
    ui.println('  ' + ui.c.gray('Attempted location: ') + ui.c.white(target));
    logger.error('hardware-info', 'Hardware report could not be saved', saved.error);
  }

  if (fingerprint) {
    ui.blank();
    ui.println('  ' + ui.c.gray('Short fingerprint: ') + ui.c.brightYellow(fingerprint.short));
  }

  // ---- optional: open the folder ----------------------------------------
  if (saved.ok) {
    ui.blank();
    const open = await prompt.confirm('Open the reports folder now?');
    if (open === true) {
      try {
        execFile('explorer.exe', [`/select,`, target], { windowsHide: false }, () => {});
        ui.ok('Explorer window requested.');
      } catch (err) {
        ui.warning(`The folder could not be opened: ${err.message}`);
      }
    }
  }

  logger.info('hardware-info', 'Hardware scan finished', {
    ms: Date.now() - started,
    saved: saved.ok,
  });

  return { ok: true, file: saved.ok ? target : null, fields: report.countFields() };
}

module.exports = {
  name: 'Computer Hardware / HWID Information',
  menuName: 'Computer Hardware / HWID Information',
  description:
    'Scans this computer and saves a detailed, sectioned hardware & identifier report (read-only).',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 10,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'registry:read', 'filesystem:write(reports)'],
  tags: ['hardware', 'hwid', 'report', 'serial', 'identifier'],
  config: {
    reportDirectory: 'reports',
    fileNameSuffix: 'CunputerHWID',
  },
  documentation:
    'Collects system, CPU, motherboard, BIOS, memory, GPU, storage, network and identifier ' +
    'information, prints it on screen and writes it to reports/<time>_<date>_CunputerHWID.txt. ' +
    'Read-only: no identifier is ever modified. Running as administrator reveals additional ' +
    'values (TPM, Secure Boot, some serial numbers).',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    if (!fs.existsSync(SCRIPT)) return { ok: false, message: 'hardware-query.ps1 is missing' };
    const probe = await ctx.system.runPowerShellJson(
      'Get-CimInstance Win32_ComputerSystem | Select-Object -First 1 Name | ConvertTo-Json -Compress',
      { timeoutMs: 20000 }
    );
    return probe.ok ? { ok: true, message: 'WMI responded' } : { ok: false, message: 'WMI query failed' };
  },

  /** Contributes the hardware sections to the Export Center. */
  reportProvider: {
    id: 'hardware',
    label: 'Hardware & Identifiers',
    order: 10,
    async build(report, ctx) {
      const result = await system.runPowerShellFileJson(SCRIPT, {
        timeoutMs: (ctx.settings.hardware.timeoutSeconds || 90) * 1000,
      });
      if (!result.ok || !result.data) {
        report.section('HARDWARE').note('Hardware information was not available during export.');
        return;
      }
      const built = buildReport(result.data, ctx);
      for (const section of built.report.sections) {
        const target = report.section(section.title);
        for (const item of section.items) target.items.push(item);
      }
    },
  },

  execute,
};
