/**
 * Function/Storage/storage-health.js
 * ---------------------------------------------------------------------------
 * STORAGE SMART CHECK
 *
 * Diagnostic only: drive model, type, capacity, serial, SMART predicted
 * failure, health status and per-volume usage. Nothing is repaired, formatted
 * or written - this module never modifies a disk.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

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 MEDIA_TYPES = { 0: 'Unspecified', 3: 'HDD', 4: 'SSD', 5: 'SCM' };

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '[ordered]@{',
  '  drives = @(Get-CimInstance Win32_DiskDrive | Select-Object Index, Model, SerialNumber, InterfaceType, MediaType, Size, Partitions, FirmwareRevision, Status, PNPDeviceID);',
  '  physical = @(Get-CimInstance -Namespace root\\microsoft\\windows\\storage -ClassName MSFT_PhysicalDisk | Select-Object DeviceId, FriendlyName, SerialNumber, MediaType, BusType, Size, HealthStatus, OperationalStatus, SpindleSpeed, FirmwareVersion, Usage);',
  '  reliability = @(Get-CimInstance -Namespace root\\microsoft\\windows\\storage -ClassName MSFT_PhysicalDisk | ForEach-Object { $d = $_; $r = Get-CimInstance -Namespace root\\microsoft\\windows\\storage -ClassName MSFT_StorageReliabilityCounter -Filter "DeviceId=\'$($d.DeviceId)\'"; if ($r) { [ordered]@{ DeviceId = $d.DeviceId; Temperature = $r.Temperature; PowerOnHours = $r.PowerOnHours; Wear = $r.Wear; ReadErrorsTotal = $r.ReadErrorsTotal; WriteErrorsTotal = $r.WriteErrorsTotal } } });',
  '  smart = @(Get-CimInstance -Namespace root\\wmi -ClassName MSStorageDriver_FailurePredictStatus | Select-Object InstanceName, PredictFailure, Reason);',
  '  volumes = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType = 3" | Select-Object DeviceID, VolumeName, FileSystem, Size, FreeSpace, VolumeSerialNumber)',
  '} | ConvertTo-Json -Depth 5 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 90000 });
  if (!result.ok || !result.data) return null;
  return {
    drives: system.asArray(result.data.drives),
    physical: system.asArray(result.data.physical),
    reliability: system.asArray(result.data.reliability),
    smart: system.asArray(result.data.smart),
    volumes: system.asArray(result.data.volumes),
  };
}

/** Pair a Win32_DiskDrive with its storage-subsystem twin. */
function matchPhysical(drive, physical, index) {
  const serial = system.clean(drive.SerialNumber);
  return (
    (serial && physical.find((p) => system.clean(p.SerialNumber) === serial)) ||
    physical.find((p) => String(p.DeviceId) === String(drive.Index)) ||
    physical[index] ||
    {}
  );
}

/** SMART "predict failure" flag for this drive, when the driver reports it. */
function smartStatus(drive, smartList, index) {
  const entry = smartList[index];
  if (!entry) return null;
  return entry.PredictFailure === true ? 'FAILURE PREDICTED' : 'OK (no failure predicted)';
}

function buildFields(drive, match, reliability, smart) {
  const temperature = Number(reliability && reliability.Temperature);
  const hours = Number(reliability && reliability.PowerOnHours);
  const wear = Number(reliability && reliability.Wear);

  return [
    ['Drive Model', system.firstOf(drive, ['Model']) || system.clean(match.FriendlyName)],
    ['Drive Type', MEDIA_TYPES[Number(match.MediaType)] || system.clean(drive.MediaType)],
    ['Bus / Interface', BUS_TYPES[Number(match.BusType)] || system.clean(drive.InterfaceType)],
    ['Capacity', drive.Size ? system.formatBytes(drive.Size) : system.formatBytes(match.Size)],
    ['Serial Number', system.firstOf(drive, ['SerialNumber']) || system.clean(match.SerialNumber)],
    ['Firmware', system.firstOf(drive, ['FirmwareRevision']) || system.clean(match.FirmwareVersion)],
    ['Partitions', system.clean(drive.Partitions)],
    ['SMART Status', smart],
    ['Health Status', system.clean(match.HealthStatus)],
    ['Operational Status', system.clean(match.OperationalStatus)],
    ['WMI Status', system.firstOf(drive, ['Status'])],
    [
      'Spindle Speed',
      Number(match.SpindleSpeed) > 0 && Number(match.SpindleSpeed) < 4294967295
        ? `${system.formatNumber(match.SpindleSpeed)} RPM`
        : null,
    ],
    ['Temperature', Number.isFinite(temperature) && temperature > 0 ? `${temperature} °C` : null],
    ['Power-On Hours', Number.isFinite(hours) && hours > 0 ? `${system.formatNumber(hours)} h` : null],
    ['Wear Level', Number.isFinite(wear) && wear > 0 ? `${wear} %` : null],
    [
      'Errors (read/write)',
      reliability && (reliability.ReadErrorsTotal || reliability.WriteErrorsTotal)
        ? `${reliability.ReadErrorsTotal || 0} / ${reliability.WriteErrorsTotal || 0}`
        : null,
    ],
  ];
}

function healthColor(value) {
  const text = String(value || '').toLowerCase();
  if (text.includes('failure')) return 'brightRed';
  if (text.includes('healthy') || text.includes('ok')) return 'brightGreen';
  if (text.includes('warning')) return 'brightYellow';
  return 'brightWhite';
}

module.exports = {
  name: 'Storage Health Check',
  menuName: 'Storage Health / SMART Check',
  description: 'Drive model, type, capacity, serial, SMART status, health and per-volume usage.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Storage',
  order: 10,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'filesystem:write(reports)'],
  tags: ['disk', 'smart', 'health', 'storage', 'ssd', 'hdd'],
  documentation:
    'Diagnostic only. Reads the drive inventory, the storage subsystem health status, the SMART ' +
    'failure prediction flag and - where the driver exposes them - temperature, power-on hours and ' +
    'wear level. Some values require administrator rights. This function never repairs, formats or ' +
    'writes to a disk.',

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

  reportProvider: {
    id: 'storage',
    label: 'Storage',
    order: 80,
    async build(report) {
      const data = await collect();
      const section = report.section('STORAGE');
      if (!data) {
        section.note('Storage information was not available during export.');
        return;
      }
      data.drives.forEach((drive, index) => {
        const match = matchPhysical(drive, data.physical, index);
        const reliability = data.reliability.find((r) => String(r.DeviceId) === String(match.DeviceId));
        const group = section.group(`Drive ${index} - ${system.clean(drive.Model) || 'Unknown'}`);
        for (const [label, value] of buildFields(drive, match, reliability, smartStatus(drive, data.smart, index))) {
          group.add(label, value);
        }
      });
      const volumes = section.group('Volumes');
      for (const volume of data.volumes) {
        volumes.add(
          `${volume.DeviceID} ${system.clean(volume.VolumeName) || ''}`.trim(),
          `${system.formatBytes(volume.FreeSpace)} free of ${system.formatBytes(volume.Size)} (${system.clean(volume.FileSystem) || '?'})`
        );
      }
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Storage Health / SMART Check', [
      'Diagnostic only - no disk is ever repaired or written to.',
    ]);
    if (!kit.requireWindows('The storage health check')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Reading drive information and SMART status...');
    const data = await collect();
    spin.stop();
    if (!data || !data.drives.length) return kit.noData('drive information');

    if (!ctx.isAdmin) {
      ui.warning('Some SMART values require administrator rights and may show as N/A.');
    }

    ui.info(`Physical drives detected: ${ui.c.brightYellow(String(data.drives.length))}`);

    data.drives.forEach((drive, index) => {
      const match = matchPhysical(drive, data.physical, index);
      const reliability = data.reliability.find((r) => String(r.DeviceId) === String(match.DeviceId));
      const smart = smartStatus(drive, data.smart, index);

      ui.blank();
      ui.section(`DRIVE ${index} - ${system.clean(drive.Model) || 'Unknown'}`);
      ui.blank();
      for (const [label, value] of buildFields(drive, match, reliability, smart)) {
        const color = label === 'SMART Status' || label === 'Health Status' ? healthColor(value) : 'brightWhite';
        kit.kv(label, value, color);
      }
    });

    ui.blank();
    ui.section('VOLUMES');
    ui.blank();
    kit.table(
      ['DRIVE', 'LABEL', 'FS', 'SIZE', 'FREE', 'USED'],
      data.volumes.map((volume) => {
        const size = Number(volume.Size) || 0;
        const free = Number(volume.FreeSpace) || 0;
        const usedPercent = size ? Math.round(((size - free) / size) * 100) : 0;
        return [
          ui.c.brightWhite(String(volume.DeviceID)),
          ui.c.gray(ui.truncate(system.clean(volume.VolumeName) || '-', 14)),
          ui.c.gray(system.clean(volume.FileSystem) || '-'),
          ui.c.brightCyan(system.formatBytes(size)),
          ui.c.brightGreen(system.formatBytes(free)),
          usedPercent >= 90 ? ui.c.brightRed(`${usedPercent}%`) : ui.c.brightWhite(`${usedPercent}%`),
        ];
      }),
      { align: ['left', 'left', 'left', 'right', 'right', 'right'], limit: 20 }
    );

    const failing = data.smart.filter((entry) => entry.PredictFailure === true);
    ui.blank();
    if (failing.length) {
      ui.error(`${failing.length} drive(s) report a predicted failure - back up your data now.`);
    } else {
      ui.ok('No drive reports a predicted failure.');
    }

    const full = data.volumes.filter((v) => Number(v.Size) && Number(v.FreeSpace) / Number(v.Size) < 0.1);
    if (full.length) {
      ui.warning(`${full.length} volume(s) have less than 10% free space.`);
    }

    await kit.offerExport('Storage Health Report', 'StorageHealth', (report) => {
      const section = report.section('STORAGE');
      data.drives.forEach((drive, index) => {
        const match = matchPhysical(drive, data.physical, index);
        const reliability = data.reliability.find((r) => String(r.DeviceId) === String(match.DeviceId));
        const group = section.group(`Drive ${index} - ${system.clean(drive.Model) || 'Unknown'}`);
        for (const [label, value] of buildFields(drive, match, reliability, smartStatus(drive, data.smart, index))) {
          group.add(label, value);
        }
      });
      const volumes = report.section('VOLUMES');
      for (const volume of data.volumes) {
        const group = volumes.group(`${volume.DeviceID} ${system.clean(volume.VolumeName) || ''}`.trim());
        group.add('File System', system.clean(volume.FileSystem));
        group.add('Total Size', system.formatBytes(volume.Size));
        group.add('Free Space', system.formatBytes(volume.FreeSpace));
        group.add('Volume Serial', system.clean(volume.VolumeSerialNumber));
      }
    });

    return kit.done('Storage health check finished.');
  },
};
