/**
 * Function/Diagnostics/system-health.js
 * ---------------------------------------------------------------------------
 * SYSTEM HEALTH CHECK
 *
 * Combines the framework diagnostics with a set of PC health checks - disk
 * space, memory pressure, disk SMART, pending reboot, uptime, critical
 * services and recent system errors - into one score. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '$pendingKeys = @(',
  '  "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending",',
  '  "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired"',
  ')',
  '$pending = $false',
  'foreach ($key in $pendingKeys) { if (Test-Path $key) { $pending = $true } }',
  '[ordered]@{',
  '  volumes = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType = 3" | Select-Object DeviceID, Size, FreeSpace);',
  '  memory = (Get-CimInstance Win32_OperatingSystem | Select-Object -First 1 TotalVisibleMemorySize, FreePhysicalMemory, LastBootUpTime);',
  '  smart = @(Get-CimInstance -Namespace root\\wmi -ClassName MSStorageDriver_FailurePredictStatus | Select-Object InstanceName, PredictFailure);',
  '  services = @(Get-CimInstance Win32_Service -Filter "Name = \'wuauserv\' OR Name = \'WinDefend\' OR Name = \'MpsSvc\' OR Name = \'EventLog\' OR Name = \'Dhcp\' OR Name = \'Dnscache\'" | Select-Object Name, DisplayName, State, StartMode);',
  '  errors = @(Get-WinEvent -FilterHashtable @{ LogName = "System"; Level = 1,2; StartTime = (Get-Date).AddDays(-7) } -MaxEvents 200 | Select-Object TimeCreated, Id, ProviderName, LevelDisplayName);',
  '  defender = (Get-MpComputerStatus | Select-Object -First 1 AntivirusEnabled, RealTimeProtectionEnabled, AntivirusSignatureAge);',
  '  pendingReboot = $pending',
  '} | ConvertTo-Json -Depth 5 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 120000 });
  if (!result.ok || !result.data) return null;
  return {
    volumes: system.asArray(result.data.volumes),
    memory: result.data.memory || {},
    smart: system.asArray(result.data.smart),
    services: system.asArray(result.data.services),
    errors: system.asArray(result.data.errors),
    defender: result.data.defender || {},
    pendingReboot: result.data.pendingReboot === true,
  };
}

/** Build the PC-side checks (the framework checks come from core/diagnostics). */
function pcChecks(data) {
  const checks = [];

  for (const volume of data.volumes) {
    const size = Number(volume.Size) || 0;
    const free = Number(volume.FreeSpace) || 0;
    const percent = size ? Math.round((free / size) * 100) : 0;
    checks.push(
      diagnostics.check(
        `Free space on ${volume.DeviceID}`,
        percent >= 10,
        `${system.formatBytes(free)} (${percent}%)`,
        percent >= 5 ? 'warning' : 'error'
      )
    );
  }

  const totalMemory = Number(data.memory.TotalVisibleMemorySize) * 1024 || os.totalmem();
  const freeMemory = Number(data.memory.FreePhysicalMemory) * 1024 || os.freemem();
  const freePercent = totalMemory ? Math.round((freeMemory / totalMemory) * 100) : 0;
  checks.push(
    diagnostics.check('Available memory', freePercent >= 10, `${system.formatBytes(freeMemory)} (${freePercent}%)`, 'warning')
  );

  const failing = data.smart.filter((entry) => entry.PredictFailure === true);
  checks.push(
    diagnostics.check(
      'Disk SMART status',
      failing.length === 0,
      failing.length ? `${failing.length} drive(s) predict failure` : `${data.smart.length || 'no'} drive(s) reporting, all healthy`,
      'error'
    )
  );

  checks.push(
    diagnostics.check('Pending reboot', !data.pendingReboot, data.pendingReboot ? 'a restart is pending' : 'none', 'warning')
  );

  const uptimeDays = Math.floor(os.uptime() / 86400);
  checks.push(
    diagnostics.check('Uptime', uptimeDays < 14, system.formatDuration(os.uptime()), 'warning')
  );

  for (const service of data.services) {
    const running = String(service.State) === 'Running';
    const shouldRun = String(service.StartMode) !== 'Disabled';
    checks.push(
      diagnostics.check(
        `Service: ${system.clean(service.Name)}`,
        running || !shouldRun,
        `${service.State} (${service.StartMode})`,
        'warning'
      )
    );
  }

  const critical = data.errors.filter((entry) => String(entry.LevelDisplayName).toLowerCase() === 'critical');
  checks.push(
    diagnostics.check(
      'System errors (7 days)',
      data.errors.length < 25,
      `${data.errors.length} error(s), ${critical.length} critical`,
      data.errors.length > 100 ? 'error' : 'warning'
    )
  );

  if (Object.keys(data.defender).length) {
    checks.push(
      diagnostics.check(
        'Real-time protection',
        data.defender.RealTimeProtectionEnabled === true,
        data.defender.RealTimeProtectionEnabled === true ? 'enabled' : 'disabled',
        'warning'
      )
    );
    const age = Number(data.defender.AntivirusSignatureAge);
    if (Number.isFinite(age)) {
      checks.push(
        diagnostics.check('Antivirus signatures', age <= 7, `${age} day(s) old`, 'warning')
      );
    }
  }

  return checks;
}

module.exports = {
  name: 'System Health Check',
  menuName: 'System Health Check',
  description: 'One score from disk space, memory, SMART, services, updates and recent system errors.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Diagnostics',
  order: 10,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'eventlog:read', 'filesystem:write(reports)'],
  tags: ['health', 'diagnostics', 'check', 'score', 'status'],
  documentation:
    'Runs both sets of checks: the framework itself (folders, configuration, modules, PowerShell ' +
    'access) and the PC (free space per volume, memory pressure, SMART failure prediction, pending ' +
    'reboot, uptime, critical Windows services, system errors of the last seven days and Defender ' +
    'real-time protection). Everything is combined into a 0-100 score with a per-check breakdown.',

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

  reportProvider: {
    id: 'health',
    label: 'System Health',
    order: 90,
    async build(report, ctx) {
      const data = await collect();
      const section = report.section('SYSTEM HEALTH');
      if (!data) {
        section.note('Health information was not available during export.');
        return;
      }
      const checks = [...(await diagnostics.environmentChecks(ctx.settings, ctx.modules)), ...pcChecks(data)];
      const score = diagnostics.score(checks);
      section.add('Health Score', `${score.value} / 100`);
      section.add('Status', score.label);
      const list = section.group('Checks');
      for (const check of checks) {
        list.add(check.name, `${check.ok ? 'OK' : check.severity.toUpperCase()}${check.detail ? ' - ' + check.detail : ''}`);
      }
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('System Health Check', ['Framework and PC health in one score.', 'Read-only diagnostics.']);
    if (!kit.requireWindows('The health check')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Running health checks...');
    const [data, frameworkChecks] = await Promise.all([
      collect(),
      diagnostics.environmentChecks(ctx.settings, ctx.modules),
    ]);
    spin.stop();

    if (!data) return kit.noData('health information');

    const checks = [...frameworkChecks, ...pcChecks(data)];
    const score = diagnostics.score(checks);
    const scoreColor = score.value >= 90 ? 'brightGreen' : score.value >= 70 ? 'brightYellow' : 'brightRed';

    ui.section('HEALTH SCORE');
    ui.blank();
    ui.println('  ' + ui.c.brightWhite(ui.padEnd('Overall', 14)) + ui.progressBar(score.value, 100, 26));
    ui.blank();
    kit.kv('Score', `${score.value} / 100`, scoreColor);
    kit.kv('Status', score.label, scoreColor);

    const failed = checks.filter((c) => !c.ok && c.severity !== 'info');
    kit.kv('Checks Run', String(checks.length));
    kit.kv('Problems Found', String(failed.length), failed.length ? 'brightYellow' : 'brightGreen');

    ui.blank();
    ui.section('CHECKS');
    ui.blank();
    for (const check of checks) {
      const mark = check.ok
        ? ui.c.brightGreen(ui.padEnd('[OK]', 11))
        : check.severity === 'error'
        ? ui.c.brightRed(ui.padEnd('[ERROR]', 11))
        : check.severity === 'info'
        ? ui.c.brightCyan(ui.padEnd('[INFO]', 11))
        : ui.c.yellow(ui.padEnd('[WARNING]', 11));
      ui.println('  ' + mark + ui.c.brightWhite(ui.padEnd(ui.truncate(check.name, 28), 29)) + ui.c.gray(check.detail));
    }

    if (failed.length) {
      ui.blank();
      ui.section('RECOMMENDATIONS', 'yellow');
      ui.blank();
      for (const check of failed.slice(0, 8)) {
        ui.println('  ' + ui.c.yellow(ui.glyphs().bullet + ' ') + ui.c.white(`${check.name}: ${check.detail}`));
      }
      if (data.pendingReboot) ui.println('  ' + ui.c.yellow(ui.glyphs().bullet + ' ') + ui.c.white('Restart Windows to finish pending updates.'));
    } else {
      ui.blank();
      ui.completed('Everything checked out - this system looks healthy.');
    }

    await kit.offerExport('System Health Report', 'SystemHealth', (report) => {
      const section = report.section('SYSTEM HEALTH');
      section.add('Health Score', `${score.value} / 100`);
      section.add('Status', score.label);
      section.add('Checks Run', String(checks.length));
      section.add('Problems Found', String(failed.length));

      const list = report.section('CHECKS');
      for (const check of checks) {
        list.add(check.name, `${check.ok ? 'OK' : check.severity.toUpperCase()}${check.detail ? ' - ' + check.detail : ''}`);
      }

      if (data.errors.length) {
        const events = report.section('RECENT SYSTEM ERRORS');
        data.errors.slice(0, 25).forEach((entry, index) => {
          events.add(
            `${index + 1}. ${system.clean(entry.ProviderName) || 'Unknown'}`,
            `${system.formatWmiDate(entry.TimeCreated) || ''} - Event ${entry.Id} (${entry.LevelDisplayName})`
          );
        });
      }
    });

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