/**
 * Function/System/windows-info.js
 * ---------------------------------------------------------------------------
 * WINDOWS INFORMATION
 *
 * Edition, version, build, architecture, installation date, user, locale,
 * time zone, boot time and uptime - with an optional TXT export.
 * Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const CV = 'HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion';

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  `$cv = "${CV}"`,
  // NOTE: never name a helper R / H / S / G - those are built-in aliases and
  // aliases outrank functions, so the call would resolve to Invoke-History.
  'function RegVal($n) { try { (Get-ItemProperty -Path $cv -Name $n -ErrorAction Stop).$n } catch { $null } }',
  '[ordered]@{',
  '  os = (Get-CimInstance Win32_OperatingSystem | Select-Object -First 1 Caption, Version, BuildNumber, OSArchitecture, InstallDate, LastBootUpTime, LocalDateTime, RegisteredUser, Organization, CSName, Locale, CountryCode, SystemDrive, WindowsDirectory, NumberOfUsers, NumberOfProcesses, SerialNumber);',
  '  cs = (Get-CimInstance Win32_ComputerSystem | Select-Object -First 1 Name, UserName, Domain, Workgroup, PartOfDomain, Manufacturer, Model, SystemType, TotalPhysicalMemory);',
  '  tz = (Get-CimInstance Win32_TimeZone | Select-Object -First 1 Caption, StandardName, Bias);',
  '  culture = (Get-Culture | Select-Object -First 1 Name, DisplayName);',
  '  reg = [ordered]@{ DisplayVersion = (RegVal "DisplayVersion"); ReleaseId = (RegVal "ReleaseId"); UBR = (RegVal "UBR"); EditionID = (RegVal "EditionID"); ProductId = (RegVal "ProductId"); InstallationType = (RegVal "InstallationType"); BuildLabEx = (RegVal "BuildLabEx"); InstallDate = (RegVal "InstallDate"); RegisteredOwner = (RegVal "RegisteredOwner") }',
  '} | ConvertTo-Json -Depth 4 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 45000 });
  return result.ok ? result.data : null;
}

/** Turn the raw query into label/value pairs used by both screen and report. */
function buildFields(data) {
  const osInfo = (data && data.os) || {};
  const cs = (data && data.cs) || {};
  const reg = (data && data.reg) || {};
  const tz = (data && data.tz) || {};
  const culture = (data && data.culture) || {};

  const build =
    system.clean(reg.UBR) && system.clean(osInfo.BuildNumber)
      ? `${system.clean(osInfo.BuildNumber)}.${system.clean(reg.UBR)}`
      : system.clean(osInfo.BuildNumber);

  const installDate =
    system.formatWmiDate(osInfo.InstallDate) ||
    (Number(reg.InstallDate) ? system.isoLocal(new Date(Number(reg.InstallDate) * 1000)) : null);

  return [
    ['Windows Edition', system.firstOf(osInfo, ['Caption'])],
    ['Edition ID', system.clean(reg.EditionID)],
    ['Display Version', system.clean(reg.DisplayVersion) || system.clean(reg.ReleaseId)],
    ['Windows Version', system.firstOf(osInfo, ['Version'])],
    ['Build Number', build],
    ['Build Lab', system.clean(reg.BuildLabEx)],
    ['Installation Type', system.clean(reg.InstallationType)],
    ['Architecture', system.firstOf(osInfo, ['OSArchitecture']) || process.arch],
    ['System Type', system.firstOf(cs, ['SystemType'])],
    ['Installation Date', installDate],
    ['Windows Product ID', system.clean(reg.ProductId)],
    ['Registered Owner', system.clean(reg.RegisteredOwner) || system.firstOf(osInfo, ['RegisteredUser'])],
    ['Organization', system.firstOf(osInfo, ['Organization'])],
    ['Computer Name', system.firstOf(cs, ['Name']) || os.hostname()],
    ['Current User', system.firstOf(cs, ['UserName']) || os.userInfo().username],
    ['Domain / Workgroup', system.firstOf(cs, ['Domain']) || system.firstOf(cs, ['Workgroup'])],
    ['Logged-on Users', system.clean(osInfo.NumberOfUsers)],
    ['Running Processes', system.clean(osInfo.NumberOfProcesses)],
    ['System Locale', system.clean(culture.Name) ? `${system.clean(culture.Name)} - ${system.clean(culture.DisplayName)}` : system.clean(osInfo.Locale)],
    ['Country Code', system.clean(osInfo.CountryCode)],
    ['Time Zone', system.firstOf(tz, ['Caption', 'StandardName'])],
    ['Boot Time', system.formatWmiDate(osInfo.LastBootUpTime)],
    ['System Uptime', system.formatDuration(os.uptime())],
    ['System Drive', system.firstOf(osInfo, ['SystemDrive'])],
    ['Windows Directory', system.firstOf(osInfo, ['WindowsDirectory'])],
    ['Installed Memory', cs.TotalPhysicalMemory ? system.formatBytes(cs.TotalPhysicalMemory) : system.formatBytes(os.totalmem())],
  ];
}

module.exports = {
  name: 'Windows Information',
  menuName: 'Windows Information',
  description: 'Windows edition, version, build, install date, locale, time zone, boot time and uptime.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 20,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'registry:read', 'filesystem:write(reports)'],
  tags: ['windows', 'os', 'version', 'build', 'uptime'],
  documentation:
    'Reads the Windows edition, version and build (including the update revision), the installation ' +
    'date, the registered owner, locale and time zone, the last boot time and the current uptime. ' +
    'The information can be exported to reports/<time>_<date>_WindowsInfo.txt.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await collect();
    return data && data.os ? { ok: true, message: 'OS information available' } : { ok: false, message: 'no OS data' };
  },

  reportProvider: {
    id: 'windows',
    label: 'Windows',
    order: 20,
    async build(report) {
      const data = await collect();
      const section = report.section('WINDOWS');
      if (!data) {
        section.note('Windows information was not available during export.');
        return;
      }
      for (const [label, value] of buildFields(data)) section.add(label, value);
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Windows Information', ['Edition, version, build, locale and uptime.']);
    if (!kit.requireWindows('Windows Information')) return { ok: false, reason: 'unsupported-platform' };

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

    const fields = buildFields(data);

    ui.section('OPERATING SYSTEM');
    ui.blank();
    for (const [label, value] of fields.slice(0, 12)) kit.kv(label, value);

    ui.blank();
    ui.section('USER & ENVIRONMENT');
    ui.blank();
    for (const [label, value] of fields.slice(12)) kit.kv(label, value);

    await kit.offerExport('Windows Information Report', 'WindowsInfo', (report) => {
      const section = report.section('WINDOWS');
      for (const [label, value] of fields) section.add(label, value);
    });

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