/**
 * Function/System/audio-info.js
 * ---------------------------------------------------------------------------
 * AUDIO DEVICE INFORMATION
 *
 * Playback (render) and recording (capture) endpoints with their state, plus
 * the installed audio controllers. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

/** MMDevices DeviceState bit values. */
const STATES = { 1: 'Active', 2: 'Disabled', 4: 'Not present', 8: 'Unplugged' };

const MM = 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio';
/** EDID-style property key holding the friendly endpoint name. */
const NAME_KEY = '{a45c254e-df1c-4efd-8020-67d146a850e0},2';
const DEVICE_KEY = '{b3f8fa53-0004-438e-9003-51a46e139bfc},6';

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  'function Endpoints($kind) {',
  `  $root = "${MM}\\$kind"`,
  '  if (-not (Test-Path $root)) { return @() }',
  '  Get-ChildItem $root | ForEach-Object {',
  '    $state = (Get-ItemProperty -Path $_.PSPath -Name DeviceState).DeviceState',
  '    $props = Get-ItemProperty -Path (Join-Path $_.PSPath "Properties")',
  `    [ordered]@{ Kind = $kind; Id = $_.PSChildName; Name = $props."${NAME_KEY}"; Device = $props."${DEVICE_KEY}"; State = $state }`,
  '  }',
  '}',
  '[ordered]@{',
  '  render = @(Endpoints "Render");',
  '  capture = @(Endpoints "Capture");',
  '  controllers = @(Get-CimInstance Win32_SoundDevice | Select-Object Name, Manufacturer, Status, DeviceID, StatusInfo)',
  '} | 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 {
    render: system.asArray(result.data.render),
    capture: system.asArray(result.data.capture),
    controllers: system.asArray(result.data.controllers),
  };
}

function stateLabel(value) {
  const key = Number(value);
  return STATES[key] || (Number.isFinite(key) ? `State ${key}` : null);
}

/** Active endpoints first, then by name. */
function sortEndpoints(list) {
  return [...list].sort((a, b) => {
    const activeDiff = (Number(a.State) === 1 ? 0 : 1) - (Number(b.State) === 1 ? 0 : 1);
    if (activeDiff) return activeDiff;
    return String(a.Name || '').localeCompare(String(b.Name || ''));
  });
}

function endpointRows(ui, list) {
  return sortEndpoints(list).map((endpoint) => {
    const active = Number(endpoint.State) === 1;
    return [
      active ? ui.c.brightGreen(ui.glyphs().check) : ui.c.gray('-'),
      ui.c.brightWhite(ui.truncate(system.clean(endpoint.Name) || system.clean(endpoint.Device) || 'Unknown', 34)),
      active ? ui.c.brightGreen('Active') : ui.c.gray(stateLabel(endpoint.State) || 'Unknown'),
    ];
  });
}

module.exports = {
  name: 'Audio Device Information',
  menuName: 'Audio Information',
  description: 'Playback and recording endpoints with their state, plus the installed audio controllers.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 70,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'registry:read', 'filesystem:write(reports)'],
  tags: ['audio', 'sound', 'playback', 'recording', 'microphone', 'speaker'],
  documentation:
    'Lists the audio endpoints Windows knows about, split into playback (render) and recording ' +
    '(capture), each with its state - active, disabled, unplugged or not present - and the audio ' +
    'controllers reported by WMI. Windows does not expose which endpoint is the default through a ' +
    'documented interface, so the active endpoints are listed first instead.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await collect();
    if (!data) return { ok: false, message: 'no audio data' };
    return {
      ok: true,
      message: `${data.render.length} playback, ${data.capture.length} recording endpoint(s)`,
    };
  },

  reportProvider: {
    id: 'audio',
    label: 'Audio',
    order: 70,
    async build(report) {
      const data = await collect();
      const section = report.section('AUDIO');
      if (!data) {
        section.note('Audio information was not available during export.');
        return;
      }
      const render = section.group('Playback Devices');
      sortEndpoints(data.render).forEach((endpoint, index) => {
        render.add(`${index + 1}. ${system.clean(endpoint.Name) || 'Unknown'}`, stateLabel(endpoint.State));
      });
      const capture = section.group('Recording Devices');
      sortEndpoints(data.capture).forEach((endpoint, index) => {
        capture.add(`${index + 1}. ${system.clean(endpoint.Name) || 'Unknown'}`, stateLabel(endpoint.State));
      });
      const controllers = section.group('Audio Controllers');
      data.controllers.forEach((device, index) => {
        controllers.add(`${index + 1}. ${system.clean(device.Name) || 'Unknown'}`, system.clean(device.Status));
      });
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Audio Information', ['Playback and recording devices detected by Windows.']);
    if (!kit.requireWindows('Audio information')) return { ok: false, reason: 'unsupported-platform' };

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

    const activeRender = data.render.filter((e) => Number(e.State) === 1);
    const activeCapture = data.capture.filter((e) => Number(e.State) === 1);

    ui.section('SUMMARY');
    ui.blank();
    kit.kv('Playback Devices', `${data.render.length} (${activeRender.length} active)`);
    kit.kv('Recording Devices', `${data.capture.length} (${activeCapture.length} active)`);
    kit.kv('Audio Controllers', String(data.controllers.length));
    kit.kv('Default Output', activeRender.length ? system.clean(activeRender[0].Name) : null);
    kit.kv('Default Input', activeCapture.length ? system.clean(activeCapture[0].Name) : null);

    ui.blank();
    ui.section('PLAYBACK DEVICES');
    ui.blank();
    if (data.render.length) kit.table(['', 'DEVICE', 'STATE'], endpointRows(ui, data.render), { limit: 20 });
    else ui.println('  ' + ui.c.gray('No playback endpoint was found.'));

    ui.blank();
    ui.section('RECORDING DEVICES');
    ui.blank();
    if (data.capture.length) kit.table(['', 'DEVICE', 'STATE'], endpointRows(ui, data.capture), { limit: 20 });
    else ui.println('  ' + ui.c.gray('No recording endpoint was found.'));

    if (data.controllers.length) {
      ui.blank();
      ui.section('AUDIO CONTROLLERS');
      ui.blank();
      kit.table(
        ['CONTROLLER', 'MANUFACTURER', 'STATUS'],
        data.controllers.map((device) => [
          ui.c.brightWhite(ui.truncate(system.clean(device.Name) || 'Unknown', 30)),
          ui.c.gray(ui.truncate(system.clean(device.Manufacturer) || '-', 20)),
          system.clean(device.Status) === 'OK' ? ui.c.brightGreen('OK') : ui.c.yellow(system.clean(device.Status) || '?'),
        ]),
        { limit: 15 }
      );
    }

    ui.blank();
    ui.println('  ' + ui.c.gray('Note: the first active endpoint is shown as default - Windows does not publish'));
    ui.println('  ' + ui.c.gray('the default device through a documented read-only interface.'));

    await kit.offerExport('Audio Device Report', 'AudioInfo', (report) => {
      const section = report.section('AUDIO');
      section.add('Playback Devices', String(data.render.length));
      section.add('Recording Devices', String(data.capture.length));
      const render = section.group('Playback');
      sortEndpoints(data.render).forEach((endpoint, index) => {
        render.add(`${index + 1}. ${system.clean(endpoint.Name) || 'Unknown'}`, stateLabel(endpoint.State));
      });
      const capture = section.group('Recording');
      sortEndpoints(data.capture).forEach((endpoint, index) => {
        capture.add(`${index + 1}. ${system.clean(endpoint.Name) || 'Unknown'}`, stateLabel(endpoint.State));
      });
      const controllers = section.group('Controllers');
      data.controllers.forEach((device, index) => {
        controllers.add(`${index + 1}. ${system.clean(device.Name) || 'Unknown'}`, system.clean(device.Status));
      });
    });

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