/**
 * Function/Diagnostics/security-status.js
 * ---------------------------------------------------------------------------
 * WINDOWS SECURITY STATUS
 *
 * Antivirus products, Defender state, real-time protection, firewall profiles,
 * Windows Update service, UAC, Secure Boot and BitLocker.
 *
 * Read-only by design: this module reports on protection, it never disables,
 * weakens or reconfigures any of it.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

/** SecurityCenter2 productState is a bit field. */
function decodeProductState(state) {
  const value = Number(state);
  if (!Number.isFinite(value)) return { enabled: null, upToDate: null };
  const enabled = (value & 0x1000) !== 0 || (value & 0x0010) !== 0;
  const upToDate = (value & 0x10) === 0;
  return { enabled, upToDate };
}

/**
 * NOTE: every risky cmdlet is evaluated into a variable through SafeGet first.
 * A cmdlet that hard-fails *inside* a hashtable literal (Confirm-SecureBootUEFI
 * on a legacy BIOS, a missing WMI namespace, ...) aborts the whole statement
 * and the script returns nothing at all - even with SilentlyContinue.
 */
const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  'function SafeGet([scriptblock] $block) { try { & $block } catch { $null } }',
  '$uac = SafeGet { (Get-ItemProperty "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System" -Name EnableLUA -ErrorAction Stop).EnableLUA }',
  '$antivirus = SafeGet { @(Get-CimInstance -Namespace root\\SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction Stop | Select-Object displayName, productState, timestamp) }',
  '$antispyware = SafeGet { @(Get-CimInstance -Namespace root\\SecurityCenter2 -ClassName AntiSpywareProduct -ErrorAction Stop | Select-Object displayName, productState) }',
  '$firewallProducts = SafeGet { @(Get-CimInstance -Namespace root\\SecurityCenter2 -ClassName FirewallProduct -ErrorAction Stop | Select-Object displayName, productState) }',
  '$defender = SafeGet { Get-MpComputerStatus -ErrorAction Stop | Select-Object -First 1 AMServiceEnabled, AntivirusEnabled, RealTimeProtectionEnabled, BehaviorMonitorEnabled, AntispywareEnabled, AntivirusSignatureAge, AntivirusSignatureLastUpdated, QuickScanAge, FullScanAge }',
  '$firewall = SafeGet { @(Get-NetFirewallProfile -ErrorAction Stop | Select-Object Name, Enabled, DefaultInboundAction, DefaultOutboundAction) }',
  '$services = SafeGet { @(Get-CimInstance Win32_Service -Filter "Name = \'wuauserv\' OR Name = \'WinDefend\' OR Name = \'MpsSvc\' OR Name = \'SecurityHealthService\' OR Name = \'wscsvc\'" -ErrorAction Stop | Select-Object Name, DisplayName, State, StartMode) }',
  '$secureBoot = SafeGet { Confirm-SecureBootUEFI -ErrorAction Stop }',
  '$bitlocker = SafeGet { @(Get-CimInstance -Namespace root\\cimv2\\security\\microsoftvolumeencryption -ClassName Win32_EncryptableVolume -ErrorAction Stop | Select-Object DriveLetter, ProtectionStatus, EncryptionMethod) }',
  '$lastUpdate = SafeGet { Get-CimInstance Win32_QuickFixEngineering -ErrorAction Stop | Sort-Object InstalledOn -Descending | Select-Object -First 1 HotFixID, InstalledOn }',
  '[ordered]@{',
  '  antivirus = @($antivirus);',
  '  antispyware = @($antispyware);',
  '  firewallProducts = @($firewallProducts);',
  '  defender = $defender;',
  '  firewall = @($firewall);',
  '  services = @($services);',
  '  secureBoot = $secureBoot;',
  '  bitlocker = @($bitlocker);',
  '  uac = $uac;',
  '  lastUpdate = $lastUpdate',
  '} | 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 {
    antivirus: system.asArray(result.data.antivirus),
    antispyware: system.asArray(result.data.antispyware),
    firewallProducts: system.asArray(result.data.firewallProducts),
    defender: result.data.defender || {},
    firewall: system.asArray(result.data.firewall),
    services: system.asArray(result.data.services),
    secureBoot: result.data.secureBoot,
    bitlocker: system.asArray(result.data.bitlocker),
    uac: result.data.uac,
    lastUpdate: result.data.lastUpdate || {},
  };
}

function stateText(value, onText = 'Enabled', offText = 'Disabled') {
  if (value === undefined || value === null) return null;
  return value ? onText : offText;
}

module.exports = {
  name: 'Windows Security Status',
  menuName: 'Windows Security Status',
  description: 'Antivirus, Defender, firewall profiles, updates, UAC, Secure Boot and BitLocker.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Diagnostics',
  order: 40,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'registry:read', 'filesystem:write(reports)'],
  tags: ['security', 'defender', 'antivirus', 'firewall', 'uac', 'bitlocker'],
  documentation:
    'Reports the protection state of this machine: registered antivirus and antispyware products ' +
    'with their enabled/up-to-date flags, Microsoft Defender details, the three firewall profiles, ' +
    'the security-related services, UAC, Secure Boot, BitLocker protection per volume and the most ' +
    'recently installed update. This module only reads - it can never disable or weaken a protection.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await collect();
    return data ? { ok: true, message: `${data.antivirus.length} antivirus product(s) registered` } : { ok: false, message: 'security query failed' };
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Windows Security Status', ['Protection state of this machine.', 'Read-only - nothing is changed.']);
    if (!kit.requireWindows('The security status')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Reading security status...');
    const data = await collect();
    spin.stop();
    if (!data) return kit.noData('security information');

    const warnings = [];

    ui.section('ANTIVIRUS');
    ui.blank();
    if (!data.antivirus.length) {
      ui.println('  ' + ui.c.gray('No antivirus product is registered with Windows Security Center.'));
    } else {
      for (const product of data.antivirus) {
        const state = decodeProductState(product.productState);
        kit.kv(
          system.clean(product.displayName) || 'Antivirus',
          `${stateText(state.enabled, 'Active', 'Inactive') || 'Unknown'}${state.upToDate === false ? ' - signatures outdated' : ''}`,
          state.enabled ? 'brightGreen' : 'brightRed'
        );
        if (state.enabled === false) warnings.push(`${system.clean(product.displayName)} is not active`);
        if (state.upToDate === false) warnings.push(`${system.clean(product.displayName)} signatures are outdated`);
      }
    }

    if (Object.keys(data.defender).length) {
      ui.blank();
      ui.section('MICROSOFT DEFENDER');
      ui.blank();
      kit.kv('Service Enabled', stateText(data.defender.AMServiceEnabled), data.defender.AMServiceEnabled ? 'brightGreen' : 'brightYellow');
      kit.kv('Antivirus', stateText(data.defender.AntivirusEnabled), data.defender.AntivirusEnabled ? 'brightGreen' : 'brightYellow');
      kit.kv(
        'Real-Time Protection',
        stateText(data.defender.RealTimeProtectionEnabled),
        data.defender.RealTimeProtectionEnabled ? 'brightGreen' : 'brightRed'
      );
      kit.kv('Behaviour Monitoring', stateText(data.defender.BehaviorMonitorEnabled));
      kit.kv('Antispyware', stateText(data.defender.AntispywareEnabled));
      kit.kv(
        'Signature Age',
        data.defender.AntivirusSignatureAge === undefined ? null : `${data.defender.AntivirusSignatureAge} day(s)`
      );
      kit.kv('Signatures Updated', system.formatWmiDate(data.defender.AntivirusSignatureLastUpdated));
      kit.kv('Last Quick Scan', data.defender.QuickScanAge === undefined ? null : `${data.defender.QuickScanAge} day(s) ago`);
      if (data.defender.RealTimeProtectionEnabled === false) warnings.push('Defender real-time protection is off');
      if (Number(data.defender.AntivirusSignatureAge) > 7) warnings.push('Defender signatures are more than a week old');
    }

    ui.blank();
    ui.section('FIREWALL');
    ui.blank();
    if (!data.firewall.length) {
      ui.println('  ' + ui.c.gray('The firewall profiles could not be read.'));
    } else {
      for (const profile of data.firewall) {
        const enabled = profile.Enabled === true || Number(profile.Enabled) === 1;
        kit.kv(`${system.clean(profile.Name)} Profile`, enabled ? 'Enabled' : 'Disabled', enabled ? 'brightGreen' : 'brightRed');
        if (!enabled) warnings.push(`${system.clean(profile.Name)} firewall profile is disabled`);
      }
    }

    ui.blank();
    ui.section('SYSTEM PROTECTION');
    ui.blank();
    kit.kv('User Account Control', data.uac === undefined ? null : Number(data.uac) === 1 ? 'Enabled' : 'Disabled', Number(data.uac) === 1 ? 'brightGreen' : 'brightRed');
    kit.kv('Secure Boot', data.secureBoot === null || data.secureBoot === undefined ? 'Not available (legacy BIOS or no rights)' : data.secureBoot ? 'Enabled' : 'Disabled');
    if (Number(data.uac) === 0) warnings.push('User Account Control is disabled');

    const protectedVolumes = data.bitlocker.filter((v) => Number(v.ProtectionStatus) === 1);
    kit.kv(
      'BitLocker',
      data.bitlocker.length ? `${protectedVolumes.length} of ${data.bitlocker.length} volume(s) protected` : 'Not available'
    );

    ui.blank();
    ui.section('SECURITY SERVICES');
    ui.blank();
    kit.table(
      ['SERVICE', 'STATE', 'STARTUP'],
      data.services.map((service) => [
        ui.c.brightWhite(ui.truncate(system.clean(service.DisplayName) || service.Name, 32)),
        String(service.State) === 'Running' ? ui.c.brightGreen('Running') : ui.c.brightYellow(String(service.State)),
        ui.c.gray(String(service.StartMode)),
      ])
    );

    ui.blank();
    kit.kv('Last Installed Update', system.clean(data.lastUpdate.HotFixID));
    kit.kv('Installed On', system.formatWmiDate(data.lastUpdate.InstalledOn));

    ui.blank();
    if (warnings.length) {
      ui.section('ATTENTION', 'yellow');
      ui.blank();
      for (const warning of warnings) {
        ui.println('  ' + ui.c.yellow(ui.glyphs().bullet + ' ') + ui.c.white(warning));
      }
      ui.blank();
      ui.println('  ' + ui.c.gray('Fix these in Windows Security - this tool never changes protection settings.'));
    } else {
      ui.completed('All reported protections are active.');
    }

    await kit.offerExport('Security Status Report', 'SecurityStatus', (report) => {
      const section = report.section('SECURITY STATUS');
      section.add('Antivirus Products', String(data.antivirus.length));
      for (const product of data.antivirus) {
        const state = decodeProductState(product.productState);
        section.add(system.clean(product.displayName) || 'Antivirus', `${stateText(state.enabled, 'Active', 'Inactive')}, ${state.upToDate ? 'up to date' : 'outdated'}`);
      }
      const defender = section.group('Microsoft Defender');
      defender.add('Real-Time Protection', stateText(data.defender.RealTimeProtectionEnabled));
      defender.add('Antivirus Enabled', stateText(data.defender.AntivirusEnabled));
      defender.add('Signature Age', data.defender.AntivirusSignatureAge === undefined ? null : `${data.defender.AntivirusSignatureAge} day(s)`);

      const firewall = section.group('Firewall Profiles');
      for (const profile of data.firewall) {
        firewall.add(system.clean(profile.Name), profile.Enabled ? 'Enabled' : 'Disabled');
      }

      const protection = section.group('System Protection');
      protection.add('User Account Control', Number(data.uac) === 1 ? 'Enabled' : 'Disabled');
      protection.add('Secure Boot', data.secureBoot === null ? 'Not available' : data.secureBoot ? 'Enabled' : 'Disabled');
      protection.add('BitLocker Volumes Protected', `${protectedVolumes.length} of ${data.bitlocker.length}`);

      if (warnings.length) {
        const attention = report.section('ATTENTION');
        warnings.forEach((warning, index) => attention.add(`${index + 1}`, warning));
      }
    });

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