/**
 * Function/System/battery-info.js
 * ---------------------------------------------------------------------------
 * BATTERY INFORMATION
 *
 * Charge, charging state, estimated runtime, design and full-charge capacity
 * and a health estimate. Desktops without a battery get a clear message.
 * Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const BATTERY_STATUS = {
  1: 'Discharging', 2: 'On AC power', 3: 'Fully charged', 4: 'Low', 5: 'Critical',
  6: 'Charging', 7: 'Charging (high)', 8: 'Charging (low)', 9: 'Charging (critical)',
  10: 'Undefined', 11: 'Partially charged',
};

const CHEMISTRY = {
  1: 'Other', 2: 'Unknown', 3: 'Lead Acid', 4: 'Nickel Cadmium', 5: 'Nickel Metal Hydride',
  6: 'Lithium-ion', 7: 'Zinc air', 8: 'Lithium Polymer',
};

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '[ordered]@{',
  '  battery = (Get-CimInstance Win32_Battery | Select-Object -First 1 Name, DeviceID, EstimatedChargeRemaining, EstimatedRunTime, BatteryStatus, Chemistry, DesignVoltage, DesignCapacity, FullChargeCapacity, Status);',
  '  static = (Get-CimInstance -Namespace root\\wmi -ClassName BatteryStaticData | Select-Object -First 1 DesignedCapacity, DeviceName, ManufactureName, SerialNumber, Chemistry, CycleCount);',
  '  full = (Get-CimInstance -Namespace root\\wmi -ClassName BatteryFullChargedCapacity | Select-Object -First 1 FullChargedCapacity);',
  '  cycle = (Get-CimInstance -Namespace root\\wmi -ClassName BatteryCycleCount | Select-Object -First 1 CycleCount);',
  '  status = (Get-CimInstance -Namespace root\\wmi -ClassName BatteryStatus | Select-Object -First 1 RemainingCapacity, Charging, Discharging, PowerOnline, Voltage)',
  '} | ConvertTo-Json -Depth 4 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 45000 });
  if (!result.ok || !result.data) return null;
  return {
    battery: result.data.battery || null,
    static: result.data.static || {},
    full: result.data.full || {},
    cycle: result.data.cycle || {},
    status: result.data.status || {},
  };
}

function mWh(value) {
  const n = Number(value);
  return Number.isFinite(n) && n > 0 ? `${system.formatNumber(n)} mWh` : null;
}

function buildFields(data) {
  const battery = data.battery || {};
  const design = Number(data.static.DesignedCapacity) || Number(battery.DesignCapacity) || 0;
  const full = Number(data.full.FullChargedCapacity) || Number(battery.FullChargeCapacity) || 0;
  const health = design > 0 && full > 0 ? Math.round((full / design) * 100) : null;
  const runtime = Number(battery.EstimatedRunTime);

  return [
    ['Battery Name', system.firstOf(battery, ['Name']) || system.clean(data.static.DeviceName)],
    ['Manufacturer', system.clean(data.static.ManufactureName)],
    ['Serial Number', system.clean(data.static.SerialNumber)],
    ['Battery Status', BATTERY_STATUS[Number(battery.BatteryStatus)] || system.clean(battery.Status)],
    [
      'Charge Percentage',
      battery.EstimatedChargeRemaining !== undefined && battery.EstimatedChargeRemaining !== null
        ? `${battery.EstimatedChargeRemaining} %`
        : null,
    ],
    [
      'Charging Status',
      data.status.Charging === true
        ? 'Charging'
        : data.status.Discharging === true
        ? 'Discharging'
        : data.status.PowerOnline === true
        ? 'On AC power'
        : BATTERY_STATUS[Number(battery.BatteryStatus)] || null,
    ],
    [
      'Estimated Remaining',
      Number.isFinite(runtime) && runtime > 0 && runtime < 71582788
        ? system.formatDuration(runtime * 60)
        : null,
    ],
    ['Design Capacity', mWh(design)],
    ['Full Charge Capacity', mWh(full)],
    ['Remaining Capacity', mWh(data.status.RemainingCapacity)],
    ['Battery Health', health === null ? null : `${health} % of design capacity`],
    ['Cycle Count', system.clean(data.cycle.CycleCount) || system.clean(data.static.CycleCount)],
    ['Chemistry', CHEMISTRY[Number(battery.Chemistry)] || system.clean(data.static.Chemistry)],
    ['Design Voltage', battery.DesignVoltage ? `${system.formatNumber(battery.DesignVoltage)} mV` : null],
    ['Current Voltage', data.status.Voltage ? `${system.formatNumber(data.status.Voltage)} mV` : null],
  ];
}

module.exports = {
  name: 'Battery Information',
  menuName: 'Battery Information',
  description: 'Charge, charging state, runtime estimate, capacities and a battery health estimate.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'System',
  order: 90,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'filesystem:write(reports)'],
  tags: ['battery', 'power', 'laptop', 'charge', 'health'],
  documentation:
    'For laptops and tablets: charge level, charging state, estimated remaining time, design and ' +
    'full-charge capacity, cycle count and a health estimate (full charge capacity divided by ' +
    'design capacity). On a desktop without a battery it simply reports that none was detected.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await collect();
    if (!data) return { ok: false, message: 'battery query failed' };
    return { ok: true, message: data.battery ? 'battery detected' : 'no battery (desktop)' };
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Battery Information', ['Charge, capacity and health of the system battery.']);
    if (!kit.requireWindows('Battery information')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Reading battery information...');
    const data = await collect();
    spin.stop();

    if (!data || !data.battery || !system.clean(data.battery.Name)) {
      ui.blank();
      ui.info('Battery not detected.');
      ui.println('  ' + ui.c.gray('This system runs on AC power only - there is no battery to report.'));
      return { ok: true, battery: false };
    }

    const fields = buildFields(data);
    const charge = Number(data.battery.EstimatedChargeRemaining);

    ui.section('CHARGE');
    ui.blank();
    if (Number.isFinite(charge)) {
      ui.println('  ' + ui.c.brightWhite(ui.padEnd('Charge', 14)) + ui.progressBar(charge, 100, 26));
      ui.blank();
    }

    for (const [label, value] of fields) kit.kv(label, value);

    const health = fields.find(([label]) => label === 'Battery Health');
    if (health && health[1]) {
      const percent = parseInt(health[1], 10);
      ui.blank();
      if (percent < 60) ui.warning('The battery has lost a significant part of its original capacity.');
      else if (percent < 80) ui.info('The battery shows normal wear for its age.');
      else ui.ok('The battery is in good condition.');
    }

    await kit.offerExport('Battery Report', 'BatteryInfo', (report) => {
      const section = report.section('BATTERY');
      for (const [label, value] of fields) section.add(label, value);
    });

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