/**
 * Function/Windows/windows-update.js
 * ---------------------------------------------------------------------------
 * WINDOWS UPDATE STATUS
 *
 * Update service state, last search/install times, pending reboot and the
 * installed update history. Informational - nothing is downloaded, installed
 * or hidden by this module.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const AU_KEY = 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update';

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  'function SafeGet([scriptblock] $block) { try { & $block } catch { $null } }',
  `$results = SafeGet { Get-ItemProperty "${AU_KEY}\\Results\\Install" -ErrorAction Stop | Select-Object LastSuccessTime }`,
  `$detect = SafeGet { Get-ItemProperty "${AU_KEY}\\Results\\Detect" -ErrorAction Stop | Select-Object LastSuccessTime }`,
  '$service = SafeGet { Get-CimInstance Win32_Service -Filter "Name = \'wuauserv\'" -ErrorAction Stop | Select-Object -First 1 Name, DisplayName, State, StartMode }',
  '$hotfix = SafeGet { @(Get-CimInstance Win32_QuickFixEngineering -ErrorAction Stop | Select-Object HotFixID, Description, InstalledOn, InstalledBy) }',
  '$reboot = $false',
  'foreach ($key in @("HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired")) { if (Test-Path $key) { $reboot = $true } }',
  // The COM searcher needs no admin rights but can be slow, so it is optional.
  '$pending = SafeGet {',
  '  $session = New-Object -ComObject Microsoft.Update.Session',
  '  $searcher = $session.CreateUpdateSearcher()',
  '  $found = $searcher.Search("IsInstalled=0 and IsHidden=0")',
  '  @($found.Updates | Select-Object -First 25 Title, @{n="Severity";e={$_.MsrcSeverity}}, @{n="Size";e={$_.MaxDownloadSize}}, IsMandatory)',
  '}',
  '[ordered]@{',
  '  service = $service;',
  '  lastInstall = $results.LastSuccessTime;',
  '  lastDetect = $detect.LastSuccessTime;',
  '  rebootRequired = $reboot;',
  '  history = @($hotfix);',
  '  pending = @($pending)',
  '} | ConvertTo-Json -Depth 5 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 180000 });
  if (!result.ok || !result.data) return null;
  return {
    service: result.data.service || {},
    lastInstall: result.data.lastInstall,
    lastDetect: result.data.lastDetect,
    rebootRequired: result.data.rebootRequired === true,
    history: system.asArray(result.data.history),
    pending: system.asArray(result.data.pending),
  };
}

function sortHistory(history) {
  return [...history].sort((a, b) => {
    const left = system.formatWmiDate(a.InstalledOn) || '';
    const right = system.formatWmiDate(b.InstalledOn) || '';
    return String(right).localeCompare(String(left));
  });
}

module.exports = {
  name: 'Windows Update Status',
  menuName: 'Windows Update Status',
  description: 'Update service state, last check, pending updates, reboot state and update history.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Windows',
  order: 90,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'registry:read', 'filesystem:write(reports)'],
  tags: ['update', 'windows update', 'patch', 'hotfix', 'reboot'],
  documentation:
    'Reports the state of the Windows Update service, when updates were last searched for and last ' +
    'installed successfully, whether a restart is pending, which updates are currently offered ' +
    '(through the Windows Update COM searcher - this can take a minute) and the full list of ' +
    'installed updates with their date. Informational only: this module never downloads, installs, ' +
    'hides or removes an update.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const probe = await system.runPowerShellJson(
      '@(Get-CimInstance Win32_QuickFixEngineering | Select-Object -First 1 HotFixID) | ConvertTo-Json -Compress',
      { timeoutMs: 30000 }
    );
    return probe.ok ? { ok: true, message: 'update history readable' } : { ok: false, message: 'update data unavailable' };
  },

  async execute(ctx) {
    const { ui, prompt } = ctx;
    kit.header('Windows Update Status', ['Service state, pending updates and history.', 'Informational only.']);
    if (!kit.requireWindows('Windows Update status')) return { ok: false, reason: 'unsupported-platform' };

    ui.info('Searching for offered updates can take up to a minute.');
    const spin = ui.spinner('Reading Windows Update status...');
    const data = await collect();
    spin.stop();
    if (!data) return kit.noData('Windows Update information');

    const history = sortHistory(data.history);

    ui.blank();
    ui.section('UPDATE STATUS');
    ui.blank();
    kit.kv(
      'Update Service',
      data.service.State ? `${data.service.State} (${data.service.StartMode})` : null,
      String(data.service.State) === 'Running' ? 'brightGreen' : 'brightYellow'
    );
    kit.kv('Last Update Check', system.formatWmiDate(data.lastDetect));
    kit.kv('Last Successful Install', system.formatWmiDate(data.lastInstall));
    kit.kv(
      'Reboot Required',
      data.rebootRequired ? 'YES - restart to finish installing' : 'No',
      data.rebootRequired ? 'brightYellow' : 'brightGreen'
    );
    kit.kv('Updates Installed (history)', system.formatNumber(history.length));
    kit.kv(
      'Updates Offered',
      data.pending.length ? String(data.pending.length) : 'none found',
      data.pending.length ? 'brightYellow' : 'brightGreen'
    );

    if (data.pending.length) {
      ui.blank();
      ui.section('PENDING UPDATES');
      ui.blank();
      kit.table(
        ['UPDATE', 'SEVERITY', 'SIZE'],
        data.pending.map((update) => [
          ui.c.brightWhite(ui.truncate(system.clean(update.Title) || 'Unknown', 40)),
          ui.c.brightYellow(system.clean(update.Severity) || '-'),
          ui.c.gray(update.Size ? system.formatBytes(update.Size) : '-'),
        ]),
        { limit: 15 }
      );
      ui.blank();
      ui.println('  ' + ui.c.gray('Install them through Settings > Windows Update - this tool does not install updates.'));
    }

    ui.blank();
    ui.section('RECENTLY INSTALLED UPDATES');
    ui.blank();
    if (!history.length) {
      ui.println('  ' + ui.c.gray('No update history is available.'));
    } else {
      kit.table(
        ['UPDATE', 'TYPE', 'INSTALLED'],
        history.map((entry) => [
          ui.c.brightWhite(system.clean(entry.HotFixID) || '-'),
          ui.c.gray(ui.truncate(system.clean(entry.Description) || '-', 20)),
          ui.c.gray((system.formatWmiDate(entry.InstalledOn) || '-').slice(0, 10)),
        ]),
        { limit: 15 }
      );
    }

    ui.blank();
    if (data.rebootRequired) {
      ui.warning('A restart is pending - Windows cannot finish installing until you reboot.');
    } else if (data.pending.length) {
      ui.info(`${data.pending.length} update(s) are waiting to be installed.`);
    } else {
      ui.completed('No pending update was found.');
    }

    await kit.offerExport('Windows Update Report', 'WindowsUpdate', (report) => {
      const section = report.section('UPDATE STATUS');
      section.add('Update Service', data.service.State ? `${data.service.State} (${data.service.StartMode})` : null);
      section.add('Last Update Check', system.formatWmiDate(data.lastDetect));
      section.add('Last Successful Install', system.formatWmiDate(data.lastInstall));
      section.add('Reboot Required', data.rebootRequired ? 'Yes' : 'No');
      section.add('Updates Offered', String(data.pending.length));

      if (data.pending.length) {
        const pending = report.section('PENDING UPDATES');
        data.pending.forEach((update, index) => {
          pending.add(`${index + 1}. ${system.clean(update.Title)}`, `${system.clean(update.Severity) || 'no severity'} - ${update.Size ? system.formatBytes(update.Size) : 'size unknown'}`);
        });
      }

      const installed = report.section('INSTALLED UPDATE HISTORY');
      history.forEach((entry, index) => {
        installed.add(
          `${index + 1}. ${system.clean(entry.HotFixID)}`,
          `${system.clean(entry.Description) || ''} - ${system.formatWmiDate(entry.InstalledOn) || 'date unknown'}`
        );
      });
    });

    return kit.done('Windows Update status collected.');
  },
};
