/**
 * Function/Network/network-info.js
 * ---------------------------------------------------------------------------
 * NETWORK INFORMATION
 *
 * Adapters, MAC addresses, IPv4/IPv6 configuration, gateway, DNS, DHCP and
 * link speed. Read-only.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

const NET_STATUS = {
  0: 'Disconnected', 1: 'Connecting', 2: 'Connected', 3: 'Disconnecting',
  4: 'Hardware not present', 5: 'Hardware disabled', 6: 'Hardware malfunction',
  7: 'Media disconnected', 8: 'Authenticating', 9: 'Authentication succeeded',
  10: 'Authentication failed', 11: 'Invalid address', 12: 'Credentials required',
};

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '[ordered]@{',
  '  adapters = @(Get-CimInstance Win32_NetworkAdapter -Filter "PhysicalAdapter = True" | Select-Object Name, NetConnectionID, MACAddress, Manufacturer, AdapterType, NetConnectionStatus, Speed, GUID, PNPDeviceID, NetEnabled);',
  '  configs = @(Get-CimInstance Win32_NetworkAdapterConfiguration -Filter "MACAddress IS NOT NULL" | Select-Object Description, MACAddress, IPAddress, IPSubnet, DefaultIPGateway, DNSServerSearchOrder, DHCPEnabled, DHCPServer, DNSDomain, DNSHostName, IPEnabled, SettingID);',
  '  profile = @(Get-NetConnectionProfile | Select-Object Name, InterfaceAlias, NetworkCategory, IPv4Connectivity, IPv6Connectivity)',
  '} | ConvertTo-Json -Depth 5 -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 60000 });
  if (!result.ok || !result.data) return null;
  return {
    adapters: system.asArray(result.data.adapters),
    configs: system.asArray(result.data.configs),
    profiles: system.asArray(result.data.profile),
  };
}

function ipv4Of(config) {
  return system.asArray(config && config.IPAddress).filter((ip) => /^\d+\.\d+\.\d+\.\d+$/.test(String(ip)));
}

function ipv6Of(config) {
  return system.asArray(config && config.IPAddress).filter((ip) => String(ip).includes(':'));
}

function adapterFields(adapter, config, profile) {
  return [
    ['Adapter Name', system.firstOf(adapter, ['NetConnectionID', 'Name'])],
    ['Description', system.firstOf(adapter, ['Name'])],
    ['Manufacturer', system.firstOf(adapter, ['Manufacturer'])],
    ['MAC Address', system.firstOf(adapter, ['MACAddress'])],
    ['Status', NET_STATUS[Number(adapter.NetConnectionStatus)] || null],
    ['Link Speed', Number(adapter.Speed) > 0 ? `${system.formatNumber(Math.round(Number(adapter.Speed) / 1e6))} Mbps` : null],
    ['IPv4 Address', ipv4Of(config).join(', ') || null],
    ['Subnet Mask', system.asArray(config && config.IPSubnet).filter((s) => /^\d+\.\d+\.\d+\.\d+$/.test(String(s))).join(', ') || null],
    ['Default Gateway', system.asArray(config && config.DefaultIPGateway).join(', ') || null],
    ['DNS Servers', system.asArray(config && config.DNSServerSearchOrder).join(', ') || null],
    ['DHCP Enabled', config && config.DHCPEnabled !== undefined ? (config.DHCPEnabled ? 'Yes' : 'No') : null],
    ['DHCP Server', system.clean(config && config.DHCPServer)],
    ['IPv6 Address', ipv6Of(config).slice(0, 2).join(', ') || null],
    ['Network Profile', profile ? `${system.clean(profile.Name)} (${system.clean(profile.NetworkCategory) || 'unknown'})` : null],
    ['Adapter GUID', system.clean(adapter.GUID)],
  ];
}

module.exports = {
  name: 'Network Information',
  menuName: 'Network Information',
  description: 'Adapters, MAC addresses, IP configuration, gateway, DNS, DHCP and link speed.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Network',
  order: 10,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'wmi:query', 'filesystem:write(reports)'],
  tags: ['network', 'adapter', 'ip', 'mac', 'dns', 'dhcp'],
  documentation:
    'Lists every physical network adapter with its MAC address, connection status, link speed and ' +
    'full IP configuration - address, subnet mask, gateway, DNS servers and DHCP state - together ' +
    'with the Windows network profile (private/public) of the active connection.',

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

  reportProvider: {
    id: 'network',
    label: 'Network',
    order: 70,
    async build(report) {
      const data = await collect();
      const section = report.section('NETWORK');
      if (!data) {
        section.note('Network information was not available during export.');
        return;
      }
      section.add('Host Name', os.hostname());
      data.adapters.forEach((adapter, index) => {
        const config = data.configs.find((c) => system.clean(c.MACAddress) === system.clean(adapter.MACAddress));
        const profile = data.profiles.find((p) => system.clean(p.InterfaceAlias) === system.clean(adapter.NetConnectionID));
        const group = section.group(`Adapter ${index + 1} - ${system.clean(adapter.NetConnectionID) || system.clean(adapter.Name)}`);
        for (const [label, value] of adapterFields(adapter, config, profile)) group.add(label, value);
      });
    },
  },

  async execute(ctx) {
    const { ui } = ctx;
    kit.header('Network Information', ['Adapters, addresses, gateway and DNS configuration.']);
    if (!kit.requireWindows('Network information')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Reading network configuration...');
    const data = await collect();
    spin.stop();
    if (!data || !data.adapters.length) return kit.noData('network information');

    const connected = data.adapters.filter((a) => Number(a.NetConnectionStatus) === 2);

    ui.section('SUMMARY');
    ui.blank();
    kit.kv('Host Name', os.hostname());
    kit.kv('Physical Adapters', String(data.adapters.length));
    kit.kv('Connected', String(connected.length), connected.length ? 'brightGreen' : 'brightYellow');
    if (data.profiles.length) {
      kit.kv('Active Network', `${system.clean(data.profiles[0].Name)} (${system.clean(data.profiles[0].NetworkCategory)})`);
      kit.kv('IPv4 Connectivity', system.clean(data.profiles[0].IPv4Connectivity));
    }

    data.adapters.forEach((adapter, index) => {
      const config = data.configs.find((c) => system.clean(c.MACAddress) === system.clean(adapter.MACAddress));
      const profile = data.profiles.find((p) => system.clean(p.InterfaceAlias) === system.clean(adapter.NetConnectionID));
      ui.blank();
      ui.section(`ADAPTER ${index + 1} - ${system.clean(adapter.NetConnectionID) || 'Unknown'}`);
      ui.blank();
      for (const [label, value] of adapterFields(adapter, config, profile)) kit.kv(label, value);
    });

    await kit.offerExport('Network Information Report', 'NetworkInfo', (report) => {
      const section = report.section('NETWORK');
      section.add('Host Name', os.hostname());
      section.add('Physical Adapters', String(data.adapters.length));
      data.adapters.forEach((adapter, index) => {
        const config = data.configs.find((c) => system.clean(c.MACAddress) === system.clean(adapter.MACAddress));
        const profile = data.profiles.find((p) => system.clean(p.InterfaceAlias) === system.clean(adapter.NetConnectionID));
        const group = section.group(`Adapter ${index + 1} - ${system.clean(adapter.NetConnectionID) || system.clean(adapter.Name)}`);
        for (const [label, value] of adapterFields(adapter, config, profile)) group.add(label, value);
      });
    });

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