/**
 * Function/System/monitor-info.js
 * ---------------------------------------------------------------------------
 * MONITOR INFORMATION
 *
 * Connected displays: EDID name and manufacturer, resolution, refresh rate,
 * connection type, physical size and which one is primary. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

/** WMI video output technology codes (WmiMonitorConnectionParams). */
const CONNECTION_TYPES = {
  0: 'VGA (D-Sub)', 1: 'S-Video', 2: 'Composite', 3: 'Component', 4: 'DVI',
  5: 'HDMI', 6: 'LVDS', 8: 'D-Jpn', 9: 'SDI', 10: 'DisplayPort (external)',
  11: 'DisplayPort (embedded)', 12: 'UDI (external)', 13: 'UDI (embedded)',
  14: 'SDTV dongle', 15: 'Miracast', 16: 'Indirect wired', 2147483648: 'Internal',
};

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  'function Decode($arr) { if ($null -eq $arr) { return $null }; ($arr | Where-Object { $_ -gt 0 } | ForEach-Object { [char] $_ }) -join "" }',
  '[ordered]@{',
  '  ids = @(Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorID | ForEach-Object { [ordered]@{ InstanceName = $_.InstanceName; Manufacturer = (Decode $_.ManufacturerName); Model = (Decode $_.UserFriendlyName); Serial = (Decode $_.SerialNumberID); ProductCode = (Decode $_.ProductCodeID); Year = $_.YearOfManufacture; Week = $_.WeekOfManufacture } });',
  '  params = @(Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorBasicDisplayParams | Select-Object InstanceName, MaxHorizontalImageSize, MaxVerticalImageSize, SupportedDisplayFeatures, VideoInputType);',
  '  conn = @(Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | Select-Object InstanceName, VideoOutputTechnology);',
  '  desktop = @(Get-CimInstance Win32_DesktopMonitor | Select-Object Name, DeviceID, ScreenWidth, ScreenHeight, MonitorManufacturer, PNPDeviceID);',
  '  video = @(Get-CimInstance Win32_VideoController | Select-Object Name, CurrentHorizontalResolution, CurrentVerticalResolution, CurrentRefreshRate, MaxRefreshRate)',
  '} | ConvertTo-Json -Depth 5 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 45000 });
  if (!result.ok || !result.data) return null;
  return {
    ids: system.asArray(result.data.ids),
    params: system.asArray(result.data.params),
    conn: system.asArray(result.data.conn),
    desktop: system.asArray(result.data.desktop),
    video: system.asArray(result.data.video),
  };
}

/** Physical diagonal in inches from the EDID centimetre values. */
function diagonalInches(params) {
  const w = Number(params && params.MaxHorizontalImageSize);
  const h = Number(params && params.MaxVerticalImageSize);
  if (!w || !h) return null;
  const inches = Math.sqrt(w * w + h * h) / 2.54;
  return `${inches.toFixed(1)}"  (${w} x ${h} cm)`;
}

function buildFields(display, index, data) {
  const params = data.params.find((p) => p.InstanceName === display.InstanceName);
  const conn = data.conn.find((c) => c.InstanceName === display.InstanceName);
  // Only the adapter knows the active mode; pair by position as a best effort.
  const video = data.video[index] || data.video[0] || {};

  return [
    ['Monitor Name', system.clean(display.Model) || system.clean(display.Manufacturer)],
    ['Manufacturer (EDID)', system.clean(display.Manufacturer)],
    ['Serial Number', system.clean(display.Serial)],
    ['Product Code', system.clean(display.ProductCode)],
    [
      'Resolution',
      video.CurrentHorizontalResolution
        ? `${video.CurrentHorizontalResolution} x ${video.CurrentVerticalResolution}`
        : null,
    ],
    ['Refresh Rate', video.CurrentRefreshRate ? `${video.CurrentRefreshRate} Hz` : null],
    ['Connection Type', conn ? CONNECTION_TYPES[Number(conn.VideoOutputTechnology)] || `Code ${conn.VideoOutputTechnology}` : null],
    ['Screen Size', diagonalInches(params)],
    ['Manufactured', display.Year ? `Year ${display.Year}${display.Week ? ', week ' + display.Week : ''}` : null],
    ['Primary Monitor', index === 0 ? 'Yes (first enumerated)' : 'No'],
    ['Instance', system.clean(display.InstanceName)],
  ];
}

module.exports = {
  name: 'Monitor Information',
  menuName: 'Monitor Information',
  description: 'Connected displays with EDID name, resolution, refresh rate, connection type and size.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 60,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'filesystem:write(reports)'],
  tags: ['monitor', 'display', 'screen', 'edid', 'resolution'],
  documentation:
    'Reads the EDID data Windows exposes for every connected display: manufacturer, model, serial ' +
    'number, manufacture date, physical size and connection type, combined with the active ' +
    'resolution and refresh rate reported by the display adapter.',

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

  reportProvider: {
    id: 'monitors',
    label: 'Monitors',
    order: 60,
    async build(report) {
      const data = await collect();
      const section = report.section('MONITORS');
      if (!data || !data.ids.length) {
        section.note('Monitor information was not available during export.');
        return;
      }
      data.ids.forEach((display, index) => {
        const group = section.group(`Display ${index + 1}`);
        for (const [label, value] of buildFields(display, index, data)) group.add(label, value);
      });
    },
  },

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

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

    if (!data.ids.length) {
      ui.warning('No EDID information was returned - showing what the adapter reports instead.');
      ui.blank();
      data.desktop.forEach((monitor, index) => {
        ui.section(`DISPLAY ${index + 1}`);
        kit.kv('Monitor Name', system.firstOf(monitor, ['Name']));
        kit.kv('Manufacturer', system.firstOf(monitor, ['MonitorManufacturer']));
        kit.kv('Resolution', monitor.ScreenWidth ? `${monitor.ScreenWidth} x ${monitor.ScreenHeight}` : null);
        ui.blank();
      });
      return kit.done('Monitor information collected.');
    }

    ui.info(`Number of displays: ${ui.c.brightYellow(String(data.ids.length))}`);

    data.ids.forEach((display, index) => {
      ui.blank();
      ui.section(`DISPLAY ${index + 1}`);
      ui.blank();
      for (const [label, value] of buildFields(display, index, data)) kit.kv(label, value);
    });

    await kit.offerExport('Monitor Information Report', 'MonitorInfo', (report) => {
      const section = report.section('MONITORS');
      section.add('Number of Displays', String(data.ids.length));
      data.ids.forEach((display, index) => {
        const group = section.group(`Display ${index + 1}`);
        for (const [label, value] of buildFields(display, index, data)) group.add(label, value);
      });
    });

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