/**
 * Function/System/gpu-info.js
 * ---------------------------------------------------------------------------
 * GPU INFORMATION
 *
 * Every installed display adapter: name, vendor, VRAM, driver, resolution,
 * refresh rate and - where a vendor counter exposes it - usage and
 * temperature. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '[ordered]@{',
  '  gpu = @(Get-CimInstance Win32_VideoController | Select-Object Name, VideoProcessor, AdapterCompatibility, AdapterRAM, DriverVersion, DriverDate, VideoModeDescription, CurrentHorizontalResolution, CurrentVerticalResolution, CurrentRefreshRate, MaxRefreshRate, MinRefreshRate, CurrentBitsPerPixel, PNPDeviceID, Status, VideoArchitecture, InstalledDisplayDrivers);',
  // Real VRAM for large cards - WMI caps AdapterRAM at 4 GB.
  '  vram = @(Get-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\*" | Select-Object PSChildName, DriverDesc, "HardwareInformation.qwMemorySize");',
  '  usage = @(Get-Counter "\\GPU Engine(*engtype_3D)\\Utilization Percentage" -ErrorAction SilentlyContinue).CounterSamples | Measure-Object -Property CookedValue -Sum | Select-Object Sum',
  '} | ConvertTo-Json -Depth 4 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 60000 });
  if (!result.ok || !result.data) return null;
  return {
    gpus: system.asArray(result.data.gpu),
    vram: system.asArray(result.data.vram),
    usage: result.data.usage && result.data.usage.Sum !== undefined ? Number(result.data.usage.Sum) : null,
  };
}

/**
 * Prefer the registry value: WMI reports at most 4 GB.
 * The registry entries are matched by adapter NAME - their order does not
 * follow the WMI order, so matching by index would report another card's VRAM.
 */
function resolveVram(card, vramList) {
  const name = system.clean(card.Name);
  const entry = name
    ? vramList.find((v) => system.clean(v.DriverDesc) && system.clean(v.DriverDesc) === name)
    : null;
  const registryValue = entry ? Number(entry['HardwareInformation.qwMemorySize']) : NaN;
  if (Number.isFinite(registryValue) && registryValue > 0) return registryValue;
  const wmiValue = Number(card.AdapterRAM);
  return Number.isFinite(wmiValue) && wmiValue > 0 ? wmiValue : null;
}

function buildFields(card, vram, usage) {
  return [
    ['GPU Name', system.firstOf(card, ['Name'])],
    ['GPU Vendor', system.firstOf(card, ['AdapterCompatibility'])],
    ['Video Processor', system.firstOf(card, ['VideoProcessor'])],
    ['VRAM', vram ? system.formatBytes(vram) : null],
    ['Driver Version', system.firstOf(card, ['DriverVersion'])],
    ['Driver Date', system.formatWmiDate(card.DriverDate)],
    [
      'Current Resolution',
      card.CurrentHorizontalResolution
        ? `${card.CurrentHorizontalResolution} x ${card.CurrentVerticalResolution}`
        : null,
    ],
    ['Refresh Rate', card.CurrentRefreshRate ? `${card.CurrentRefreshRate} Hz` : null],
    [
      'Refresh Range',
      card.MinRefreshRate && card.MaxRefreshRate ? `${card.MinRefreshRate} - ${card.MaxRefreshRate} Hz` : null,
    ],
    ['Colour Depth', card.CurrentBitsPerPixel ? `${card.CurrentBitsPerPixel}-bit` : null],
    ['Video Mode', system.firstOf(card, ['VideoModeDescription'])],
    ['GPU Usage (3D)', usage === null || !Number.isFinite(usage) ? null : `${Math.round(usage)} %`],
    ['PNP Device ID', system.firstOf(card, ['PNPDeviceID'])],
    ['Status', system.firstOf(card, ['Status'])],
  ];
}

module.exports = {
  name: 'GPU Information',
  menuName: 'GPU Information',
  description: 'Display adapters: name, vendor, VRAM, driver, resolution, refresh rate and usage.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 40,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'registry:read', 'filesystem:write(reports)'],
  tags: ['gpu', 'graphics', 'video', 'display', 'driver', 'vram'],
  documentation:
    'Lists every installed graphics adapter with its driver, VRAM and current display mode. ' +
    'VRAM is read from the display class registry key because WMI caps AdapterRAM at 4 GB. ' +
    'GPU usage comes from the Windows GPU Engine performance counters; temperature is only ' +
    'available through vendor tools and is therefore not reported here.',

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

  reportProvider: {
    id: 'gpu',
    label: 'GPU',
    order: 40,
    async build(report) {
      const data = await collect();
      const section = report.section('GPU');
      if (!data || !data.gpus.length) {
        section.note('GPU information was not available during export.');
        return;
      }
      data.gpus.forEach((card, index) => {
        const target = section.group(`Adapter ${index + 1} - ${system.clean(card.Name) || 'Unknown'}`);
        for (const [label, value] of buildFields(card, resolveVram(card, data.vram), data.usage)) {
          target.add(label, value);
        }
      });
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('GPU Information', ['Installed graphics adapters and their current display mode.']);
    if (!kit.requireWindows('GPU information')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Reading display adapter information...');
    const data = await collect();
    spin.stop();
    if (!data || !data.gpus.length) return kit.noData('display adapter information');

    ui.info(`Installed graphics adapters: ${ui.c.brightYellow(String(data.gpus.length))}`);

    data.gpus.forEach((card, index) => {
      ui.blank();
      ui.section(`ADAPTER ${index + 1}`);
      ui.blank();
      for (const [label, value] of buildFields(card, resolveVram(card, data.vram), data.usage)) {
        kit.kv(label, value);
      }
    });

    ui.blank();
    ui.println('  ' + ui.c.gray('Note: GPU temperature is only exposed by vendor tools, not by Windows itself.'));

    await kit.offerExport('GPU Information Report', 'GpuInfo', (report) => {
      const section = report.section('GPU');
      data.gpus.forEach((card, index) => {
        const target = section.group(`Adapter ${index + 1} - ${system.clean(card.Name) || 'Unknown'}`);
        for (const [label, value] of buildFields(card, resolveVram(card, data.vram), data.usage)) {
          target.add(label, value);
        }
      });
    });

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