Skip to main content

Gets system information from local or remote computers. Retrieves comprehensive system information including CPU architecture, processor speed, temperature (when available), operating system details, computer model and name, memory information, page file/swap usage, GPU/video card details, physical disk specifications, audio devices, monitor/display information, input devices (keyboard and mouse), network adapters, battery status (for laptops), virtualization detection, system load metrics, process and thread counts, and other hardware specifications. Supports both local and remote computer queries.

function Get-SystemInfo
{
    <#
    .SYNOPSIS
        Gets system information from local or remote computers.

    .DESCRIPTION
        Retrieves comprehensive system information including CPU architecture, processor speed,
        temperature (when available), operating system details, computer model and name, memory
        information, page file/swap usage, GPU/video card details, physical disk specifications,
        audio devices, monitor/display information, input devices (keyboard and mouse), network
        adapters, battery status (for laptops), virtualization detection, system load metrics,
        process and thread counts, and other hardware specifications. Supports both local and
        remote computer queries.

        Remote computer queries are only available on Windows systems via PowerShell remoting (WinRM).
        On macOS and Linux, only local computer queries are supported.

        Hardware and system information includes:
        - CPU details with temperature monitoring (when available)
        - GPU/Video card name and memory
        - Physical disks (embedded drives only, excludes USB and removable media)
        - Audio devices
        - Monitors/displays with resolution information
        - Keyboard devices
        - Mouse/pointing devices
        - Network adapters (physical adapters only)
        - Battery status and charge level (laptops only)
        - Virtualization detection (VM type identification)
        - System load average (macOS and Linux)
        - Memory and page file/swap usage
        - Process and thread counts

        Compatible with PowerShell Desktop 5.1 and PowerShell Core 6.2+ on Windows, macOS, and Linux.

    .PARAMETER ComputerName
        Target computers to retrieve system information from. Accepts an array of computer names or IP addresses.
        If not specified, 'localhost' is used as the default.
        Supports pipeline input by property name for object-based input.

        Note: Remote computer queries require Windows and PowerShell remoting (WinRM) to be enabled.

    .PARAMETER Credential
        Specifies credentials for remote computer access. Required for remote computers that need authentication.
        Only applicable on Windows systems with PowerShell remoting enabled.

    .PARAMETER NoPII
        Excludes private and personally identifiable information from the output. When specified, the following
        properties will be omitted: ComputerName, HostName, Domain, IPAddresses, Username, SerialNumber,
        BIOSVersion, TimeZone, LastBootTime, and Uptime.

    .PARAMETER NoEmptyProps
        Excludes properties with null or empty values from the output. This provides cleaner results by only
        showing properties that have actual values, which is particularly useful for cross-platform scenarios
        where certain properties may not be available on all operating systems.

    .EXAMPLE
        PS > Get-SystemInfo -NoEmptyProps -NoPII

        OperatingSystem       : macOS Tahoe 26.3
        OSArchitecture        : arm64
        CPUArchitecture       : arm64
        CPUName               : Apple M4 Pro
        CPUCores              : 12
        CPULogicalProcessors  : 12
        HyperthreadingEnabled : False
        GPUName               : Apple M4 Pro
        Monitors              : Displays (3024 x 1964 Retina)
        NetworkAdapters       : Ethernet Adapter (en4) (Ethernet), Ethernet Adapter (en5) (Ethernet), Ethernet Adapter (en6) (Ethernet),
                                Thunderbolt Bridge (Ethernet), Wi-Fi (AirPort)
        TotalMemoryGB         : 24
        FreeMemoryGB          : 12.39
        PageFileTotalGB       : 0.94
        PageFileUsedGB        : 0.94
        SystemDriveTotalGB    : 460.43
        SystemDriveUsedGB     : 11.45
        SystemDriveFreeGB     : 292.16
        PhysicalDisks         : APPLE SSD AP0512Z (500.3 GB, Apple Fabric), APPLE SSD AP0512Z (494.4 GB, Apple Fabric)
        Manufacturer          : Apple Inc.
        Model                 : Mac16,8
        ModelFriendlyName     : MacBook Pro (14-inch, 2024)
        IsVirtualMachine      : False
        SystemLoadAverage     : 1.25 (1m), 1.78 (5m), 1.82 (15m)
        ProcessCount          : 709
        ThreadCount           : 2603
        BatteryStatus         : Fully Charged
        BatteryChargePercent  : 100

        Gets system information from the local computer while excluding personally identifiable information
        and any properties that have null or empty values, resulting in a concise output of only populated properties.

    .EXAMPLE
        PS > Get-SystemInfo -ComputerName 'server01'

        Gets system information from a remote computer (Windows only).

    .EXAMPLE
        PS > Get-SystemInfo -ComputerName 'server01' -Credential (Get-Credential)

        Gets system information from a remote computer using specified credentials (Windows only).

    .EXAMPLE
        PS > 'server01','server02' | Get-SystemInfo

        Gets system information from multiple computers using pipeline input (Windows only).

    .EXAMPLE
        PS > Get-SystemInfo | Format-Table -AutoSize

        Gets system information and displays it in a formatted table.

    .EXAMPLE
        PS > Get-SystemInfo -NoPII

        Gets system information while excluding private and personally identifiable information
        such as computer name, hostname, IP addresses, serial number, and BIOS version.

    .EXAMPLE
        PS > Get-SystemInfo -NoEmptyProps

        Gets system information and excludes any properties that have null or empty values,
        resulting in a cleaner output showing only populated properties.

    .OUTPUTS
        System.Object[]

        Returns custom objects with system information properties including:

        - ComputerName: Name of the computer
        - HostName: Fully qualified domain name or hostname
        - Domain: Domain name (Windows only, null for workgroup or non-Windows)
        - IPAddresses: Array of IP addresses assigned to the computer
        - Username: Current logged-in username
        - OperatingSystem: Operating system name and version
        - OSArchitecture: Operating system architecture (32-bit/64-bit)
        - CPUArchitecture: Processor architecture
        - CPUName: Processor name/model
        - CPUCores: Number of processor cores
        - CPULogicalProcessors: Number of logical processors
        - HyperthreadingEnabled: Whether hyperthreading/SMT is enabled
        - CPUSpeedMHz: Processor speed in MHz
        - CPUTemperatureCelsius: CPU temperature in Celsius (when available, requires admin on some platforms)
        - CPUTemperatureFahrenheit: CPU temperature in Fahrenheit (when available, requires admin on some platforms)
        - GPUName: GPU/video card name(s)
        - GPUMemoryGB: Total GPU memory in GB (when available)
        - Monitors: Monitor/display information with resolution
        - Keyboard: Keyboard device name(s)
        - Mouse: Mouse/pointing device name(s)
        - NetworkAdapters: Network adapter information (physical adapters only)
        - TotalMemoryGB: Total physical memory in GB
        - FreeMemoryGB: Available physical memory in GB
        - PageFileTotalGB: Total page file/swap space in GB
        - PageFileUsedGB: Used page file/swap space in GB
        - SystemDriveTotalGB: Total system drive capacity in GB
        - SystemDriveUsedGB: Used space on system drive in GB
        - SystemDriveFreeGB: Free space on system drive in GB
        - PhysicalDisks: Physical disk information (embedded drives only, excludes USB/removable)
        - AudioDevices: Audio device names
        - Manufacturer: Computer manufacturer
        - Model: Computer model
        - ModelFriendlyName: Human-friendly model name (marketing model on macOS when available)
        - SerialNumber: Computer serial number (when available)
        - BIOSVersion: BIOS version
        - IsVirtualMachine: Boolean indicating if running in a virtual machine
        - VirtualizationType: Type of virtualization (Hyper-V, VMware, VirtualBox, etc.)
        - SystemLoadAverage: System load average (1m, 5m, 15m) - macOS and Linux only
        - ProcessCount: Total number of running processes
        - ThreadCount: Total number of threads
        - BatteryStatus: Battery status (Charging, Discharging, Fully Charged, etc.) - laptops only
        - BatteryChargePercent: Battery charge percentage - laptops only
        - BatteryEstimatedRuntime: Estimated battery runtime - Windows laptops only
        - TimeZone: System time zone
        - LastBootTime: Last system boot time
        - Uptime: System uptime as a timespan

    .NOTES
        Remote execution uses PowerShell remoting (WinRM) and requires:
        - Windows operating system
        - Appropriate permissions
        - PowerShell remoting enabled on target computers

        On macOS and Linux:
        - Only local computer queries are supported
        - Remote queries will generate a warning and skip non-local targets

        Author: Jon LaBelle
        License: MIT
        Source: https://github.com/jonlabelle/pwsh-profile/blob/main/Functions/SystemAdministration/Get-SystemInfo.ps1

    .LINK
        https://github.com/jonlabelle/pwsh-profile/blob/main/Functions/SystemAdministration/Get-SystemInfo.ps1
    #>
    [CmdletBinding(ConfirmImpact = 'Low')]
    [OutputType([System.Object[]])]
    param(
        [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Cn', 'PSComputerName', 'Server', 'Target')]
        [String[]]$ComputerName,

        [Parameter()]
        [PSCredential]$Credential,

        [Parameter()]
        [Switch]$NoPII,

        [Parameter()]
        [Switch]$NoEmptyProps
    )

    begin
    {
        # Initialize results collection
        $results = New-Object System.Collections.ArrayList

        # Platform detection
        if ($PSVersionTable.PSVersion.Major -lt 6)
        {
            # PowerShell 5.1 - Windows only
            $script:IsWindowsPlatform = $true
            $script:IsMacOSPlatform = $false
            $script:IsLinuxPlatform = $false
        }
        else
        {
            # PowerShell Core - cross-platform
            $script:IsWindowsPlatform = $IsWindows
            $script:IsMacOSPlatform = $IsMacOS
            $script:IsLinuxPlatform = $IsLinux
        }

        # Default to localhost if no computer name specified
        if (-not $ComputerName)
        {
            $ComputerName = @('localhost')
        }

        Write-Verbose "Platform detection: Windows=$script:IsWindowsPlatform, macOS=$script:IsMacOSPlatform, Linux=$script:IsLinuxPlatform"
    }

    process
    {
        # Helper function to translate Windows CPU architecture codes
        function ConvertFrom-CpuArchitectureCode
        {
            param([int]$Code)

            switch ($Code)
            {
                0 { 'x86' }
                1 { 'MIPS' }
                2 { 'Alpha' }
                3 { 'PowerPC' }
                5 { 'ARM' }
                6 { 'ia64' }
                9 { 'x64' }
                12 { 'ARM64' }
                default { "Unknown ($Code)" }
            }
        }

        # Helper function to convert Apple CoreTypes model identifiers to readable model names
        function ConvertFrom-AppleModelTypeIdentifier
        {
            param([string]$TypeIdentifier)

            if ([string]::IsNullOrWhiteSpace($TypeIdentifier))
            {
                return $null
            }

            $normalizedType = $TypeIdentifier.Trim().ToLowerInvariant()
            if ($normalizedType -notlike 'com.apple.*')
            {
                return $null
            }

            $slug = $normalizedType -replace '^com\.apple\.', ''
            $slugParts = $slug -split '-', 2
            $baseToken = $slugParts[0]

            $baseModelNames = @{
                'imac' = 'iMac'
                'imacpro' = 'iMac Pro'
                'macbook' = 'MacBook'
                'macbookair' = 'MacBook Air'
                'macbookpro' = 'MacBook Pro'
                'macmini' = 'Mac mini'
                'macpro' = 'Mac Pro'
                'macstudio' = 'Mac Studio'
                'xserve' = 'Xserve'
            }

            if (-not $baseModelNames.ContainsKey($baseToken))
            {
                return $null
            }

            $baseName = $baseModelNames[$baseToken]

            if ($slug -match '^[a-z0-9]+-(\d+)-(early|mid|late)-((?:19|20)\d{2})(?:-\d+)?$')
            {
                $releasePeriod = [System.Globalization.CultureInfo]::InvariantCulture.TextInfo.ToTitleCase($matches[2])
                return "$baseName ($($matches[1])-inch, $releasePeriod $($matches[3]))"
            }

            if ($slug -match '^[a-z0-9]+-(\d+)-((?:19|20)\d{2})(?:-\d+)?$')
            {
                return "$baseName ($($matches[1])-inch, $($matches[2]))"
            }

            if ($slug -match '^[a-z0-9]+-((?:19|20)\d{2})(?:-\d+)?$')
            {
                return "$baseName ($($matches[1]))"
            }

            return $baseName
        }

        foreach ($computer in $ComputerName)
        {
            Write-Verbose "Processing computer: $computer"

            # Determine if this is a local or remote query
            $isLocal = ($computer -eq 'localhost' -or $computer -eq '127.0.0.1' -or $computer -eq $env:COMPUTERNAME -or $computer -eq [System.Net.Dns]::GetHostName())

            if ($isLocal)
            {
                # Local computer processing
                Write-Verbose 'Querying local computer for system information'

                try
                {
                    $systemInfo = [PSCustomObject]@{
                        PSTypeName = 'SystemInfo.Result'
                        ComputerName = $computer
                        HostName = $null
                        Domain = $null
                        IPAddresses = $null
                        Username = $null
                        OperatingSystem = $null
                        OSArchitecture = $null
                        CPUArchitecture = $null
                        CPUName = $null
                        CPUCores = $null
                        CPULogicalProcessors = $null
                        HyperthreadingEnabled = $null
                        CPUSpeedMHz = $null
                        CPUTemperatureCelsius = $null
                        CPUTemperatureFahrenheit = $null
                        GPUName = $null
                        GPUMemoryGB = $null
                        Monitors = $null
                        Keyboard = $null
                        Mouse = $null
                        NetworkAdapters = $null
                        TotalMemoryGB = $null
                        FreeMemoryGB = $null
                        PageFileTotalGB = $null
                        PageFileUsedGB = $null
                        SystemDriveTotalGB = $null
                        SystemDriveUsedGB = $null
                        SystemDriveFreeGB = $null
                        PhysicalDisks = $null
                        AudioDevices = $null
                        Manufacturer = $null
                        Model = $null
                        ModelFriendlyName = $null
                        SerialNumber = $null
                        BIOSVersion = $null
                        IsVirtualMachine = $null
                        VirtualizationType = $null
                        SystemLoadAverage = $null
                        ProcessCount = $null
                        ThreadCount = $null
                        BatteryStatus = $null
                        BatteryChargePercent = $null
                        BatteryEstimatedRuntime = $null
                        TimeZone = $null
                        LastBootTime = $null
                        Uptime = $null
                    }

                    # Get hostname and IP addresses (cross-platform)
                    try
                    {
                        $systemInfo.HostName = [System.Net.Dns]::GetHostName()

                        # Get all IP addresses for the local host
                        $hostEntry = [System.Net.Dns]::GetHostEntry([System.Net.Dns]::GetHostName())
                        $ipAddresses = $hostEntry.AddressList | Where-Object {
                            # Filter out IPv6 link-local addresses
                            $_.AddressFamily -eq 'InterNetwork' -or
                            ($_.AddressFamily -eq 'InterNetworkV6' -and -not $_.IsIPv6LinkLocal)
                        } | ForEach-Object { $_.IPAddressToString }

                        $systemInfo.IPAddresses = $ipAddresses
                    }
                    catch
                    {
                        Write-Verbose \"Could not retrieve hostname or IP addresses: $($_.Exception.Message)\"
                    }

                    # Get current username (cross-platform)
                    try
                    {
                        $systemInfo.Username = [System.Environment]::UserName
                    }
                    catch
                    {
                        Write-Verbose \"Could not retrieve username: $($_.Exception.Message)\"
                    }

                    # Get OS information
                    if ($script:IsWindowsPlatform)
                    {
                        # Windows-specific information using CIM/WMI
                        Write-Verbose 'Using CIM/WMI for Windows system information'

                        try
                        {
                            # Get OS information
                            $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
                            $systemInfo.OperatingSystem = $os.Caption
                            $systemInfo.OSArchitecture = $os.OSArchitecture
                            $systemInfo.TotalMemoryGB = [Math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
                            $systemInfo.FreeMemoryGB = [Math]::Round($os.FreePhysicalMemory / 1MB, 2)
                            $systemInfo.LastBootTime = $os.LastBootUpTime
                            $systemInfo.Uptime = (Get-Date) - $os.LastBootUpTime
                            $systemInfo.ProcessCount = $os.NumberOfProcesses

                            # Get page file information
                            try
                            {
                                $pageFiles = Get-CimInstance -ClassName Win32_PageFileUsage -ErrorAction Stop
                                if ($pageFiles)
                                {
                                    $totalPageFileMB = ($pageFiles | Measure-Object -Property AllocatedBaseSize -Sum).Sum
                                    $usedPageFileMB = ($pageFiles | Measure-Object -Property CurrentUsage -Sum).Sum
                                    if ($totalPageFileMB -gt 0)
                                    {
                                        $systemInfo.PageFileTotalGB = [Math]::Round($totalPageFileMB / 1KB, 2)
                                        $systemInfo.PageFileUsedGB = [Math]::Round($usedPageFileMB / 1KB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve page file information: $($_.Exception.Message)"
                            }

                            # Get thread count
                            try
                            {
                                $threads = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfProc_Thread -ErrorAction Stop
                                if ($threads)
                                {
                                    $systemInfo.ThreadCount = $threads.Count
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve thread count: $($_.Exception.Message)"
                            }

                            # Get system drive information (Windows)
                            try
                            {
                                $systemDrive = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='$($env:SystemDrive)'" -ErrorAction Stop
                                if ($systemDrive)
                                {
                                    $systemInfo.SystemDriveTotalGB = [Math]::Round($systemDrive.Size / 1GB, 2)
                                    $systemInfo.SystemDriveFreeGB = [Math]::Round($systemDrive.FreeSpace / 1GB, 2)
                                    $systemInfo.SystemDriveUsedGB = [Math]::Round(($systemDrive.Size - $systemDrive.FreeSpace) / 1GB, 2)
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve system drive information: $($_.Exception.Message)"
                            }

                            # Get processor information
                            $cpu = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | Select-Object -First 1
                            $systemInfo.CPUArchitecture = ConvertFrom-CpuArchitectureCode -Code $cpu.Architecture
                            $systemInfo.CPUName = $cpu.Name.Trim()
                            $systemInfo.CPUCores = $cpu.NumberOfCores
                            $systemInfo.CPULogicalProcessors = $cpu.NumberOfLogicalProcessors

                            # Detect hyperthreading/SMT (if logical processors > cores, HT is enabled)
                            if ($cpu.NumberOfLogicalProcessors -gt $cpu.NumberOfCores)
                            {
                                $systemInfo.HyperthreadingEnabled = $true
                            }
                            else
                            {
                                $systemInfo.HyperthreadingEnabled = $false
                            }

                            $systemInfo.CPUSpeedMHz = $cpu.MaxClockSpeed

                            # Get CPU temperature (Windows - using thermal zone information)
                            try
                            {
                                $thermalZones = Get-CimInstance -ClassName Win32_PerfFormattedData_Counters_ThermalZoneInformation -ErrorAction SilentlyContinue
                                if ($thermalZones)
                                {
                                    # Find the CPU thermal zone (typically named CPUZ)
                                    $cpuZone = $thermalZones | Where-Object { $_.Name -like '*CPUZ*' } | Select-Object -First 1
                                    if ($cpuZone -and $cpuZone.HighPrecisionTemperature)
                                    {
                                        # HighPrecisionTemperature is in tenths of Kelvin, convert to Celsius and Fahrenheit
                                        $tempKelvin = $cpuZone.HighPrecisionTemperature / 10
                                        $tempCelsius = $tempKelvin - 273.15
                                        $systemInfo.CPUTemperatureCelsius = [Math]::Round($tempCelsius, 1)
                                        $systemInfo.CPUTemperatureFahrenheit = [Math]::Round(($tempCelsius * 9 / 5) + 32, 1)
                                        Write-Verbose "CPU temperature: $($systemInfo.CPUTemperatureCelsius)°C / $($systemInfo.CPUTemperatureFahrenheit)°F (from zone: $($cpuZone.Name))"
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve CPU temperature: $($_.Exception.Message)"
                            }

                            # Get computer system information
                            $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
                            $systemInfo.Manufacturer = $cs.Manufacturer
                            $systemInfo.Model = $cs.Model
                            $systemInfo.ModelFriendlyName = $cs.Model

                            # Detect virtualization
                            try
                            {
                                $systemInfo.IsVirtualMachine = $false
                                $systemInfo.VirtualizationType = $null

                                # Check manufacturer and model for VM indicators
                                $vmIndicators = @{
                                    'Microsoft Corporation' = 'Hyper-V'
                                    'VMware' = 'VMware'
                                    'innotek GmbH' = 'VirtualBox'
                                    'QEMU' = 'QEMU/KVM'
                                    'Xen' = 'Xen'
                                    'Parallels' = 'Parallels'
                                }

                                foreach ($indicator in $vmIndicators.Keys)
                                {
                                    if ($cs.Manufacturer -like "*$indicator*" -or $cs.Model -like "*$indicator*")
                                    {
                                        $systemInfo.IsVirtualMachine = $true
                                        $systemInfo.VirtualizationType = $vmIndicators[$indicator]
                                        break
                                    }
                                }

                                # Check BIOS version for additional indicators
                                if (-not $systemInfo.IsVirtualMachine)
                                {
                                    $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction SilentlyContinue
                                    if ($bios)
                                    {
                                        if ($bios.Version -like '*VBOX*')
                                        {
                                            $systemInfo.IsVirtualMachine = $true
                                            $systemInfo.VirtualizationType = 'VirtualBox'
                                        }
                                        elseif ($bios.Version -like '*Hyper-V*')
                                        {
                                            $systemInfo.IsVirtualMachine = $true
                                            $systemInfo.VirtualizationType = 'Hyper-V'
                                        }
                                    }
                                }

                                # Check for specific VM registry keys or services as additional verification
                                if (-not $systemInfo.IsVirtualMachine)
                                {
                                    $vmwareService = Get-Service -Name 'VMTools' -ErrorAction SilentlyContinue
                                    if ($vmwareService)
                                    {
                                        $systemInfo.IsVirtualMachine = $true
                                        $systemInfo.VirtualizationType = 'VMware'
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not detect virtualization: $($_.Exception.Message)"
                            }

                            # Get domain information (null if workgroup)
                            if ($cs.PartOfDomain)
                            {
                                $systemInfo.Domain = $cs.Domain
                            }
                            else
                            {
                                $systemInfo.Domain = $null  # Workgroup
                            }

                            # Get BIOS information
                            $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop
                            $systemInfo.SerialNumber = $bios.SerialNumber
                            $systemInfo.BIOSVersion = $bios.SMBIOSBIOSVersion

                            # Get time zone with DST awareness
                            $timeZone = [System.TimeZoneInfo]::Local
                            $isDst = $timeZone.IsDaylightSavingTime((Get-Date))
                            $offset = $timeZone.GetUtcOffset((Get-Date))
                            $offsetString = if ($offset.TotalHours -ge 0) { "+$($offset.Hours)" } else { "$($offset.Hours)" }

                            if ($isDst -and $timeZone.DaylightName)
                            {
                                $systemInfo.TimeZone = "$($timeZone.DaylightName) (UTC$offsetString)"
                            }
                            else
                            {
                                $systemInfo.TimeZone = "$($timeZone.StandardName) (UTC$offsetString)"
                            }

                            # Get video card information
                            try
                            {
                                $videoCards = Get-CimInstance -ClassName Win32_VideoController -ErrorAction Stop |
                                Where-Object { $_.Status -eq 'OK' -or $null -eq $_.Status }

                                if ($videoCards)
                                {
                                    $gpuInfo = @()
                                    foreach ($gpu in $videoCards)
                                    {
                                        $gpuName = $gpu.Name
                                        $gpuMemoryBytes = $gpu.AdapterRAM

                                        if ($gpuMemoryBytes -and $gpuMemoryBytes -gt 0)
                                        {
                                            $gpuMemoryGB = [Math]::Round($gpuMemoryBytes / 1GB, 2)
                                            $gpuInfo += "$gpuName ($gpuMemoryGB GB)"
                                        }
                                        else
                                        {
                                            $gpuInfo += $gpuName
                                        }
                                    }

                                    $systemInfo.GPUName = $gpuInfo -join ', '

                                    # Set GPUMemoryGB to total memory if available
                                    $totalGpuMemory = ($videoCards | Where-Object { $_.AdapterRAM -gt 0 } |
                                        Measure-Object -Property AdapterRAM -Sum).Sum
                                    if ($totalGpuMemory -gt 0)
                                    {
                                        $systemInfo.GPUMemoryGB = [Math]::Round($totalGpuMemory / 1GB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve video card information: $($_.Exception.Message)"
                            }

                            # Get physical disk information (embedded drives only - exclude USB/removable)
                            try
                            {
                                $physicalDisks = Get-CimInstance -ClassName Win32_DiskDrive -ErrorAction Stop |
                                Where-Object {
                                    $_.MediaType -notmatch 'Removable' -and
                                    $_.InterfaceType -notmatch 'USB' -and
                                    $_.Size -gt 0
                                }

                                if ($physicalDisks)
                                {
                                    $diskInfo = @()
                                    foreach ($disk in $physicalDisks)
                                    {
                                        $diskModel = $disk.Model
                                        $diskSizeGB = [Math]::Round($disk.Size / 1GB, 2)
                                        $diskInterface = $disk.InterfaceType

                                        $diskInfo += "$diskModel ($diskSizeGB GB, $diskInterface)"
                                    }

                                    $systemInfo.PhysicalDisks = $diskInfo -join ', '
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve physical disk information: $($_.Exception.Message)"
                            }

                            # Get audio device information
                            try
                            {
                                $audioDevices = Get-CimInstance -ClassName Win32_SoundDevice -ErrorAction Stop |
                                Where-Object { $_.Status -eq 'OK' -or $null -eq $_.Status }

                                if ($audioDevices)
                                {
                                    $audioInfo = @()
                                    foreach ($audio in $audioDevices)
                                    {
                                        $audioInfo += $audio.Name
                                    }

                                    $systemInfo.AudioDevices = $audioInfo -join ', '
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve audio device information: $($_.Exception.Message)"
                            }

                            # Get monitor information
                            try
                            {
                                $monitors = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID -ErrorAction Stop

                                if ($monitors)
                                {
                                    $monitorInfo = @()
                                    foreach ($monitor in $monitors)
                                    {
                                        # Decode manufacturer name
                                        $mfgName = if ($monitor.ManufacturerName)
                                        {
                                            -join ($monitor.ManufacturerName | Where-Object { $_ -ne 0 } | ForEach-Object { [char]$_ })
                                        }
                                        else { 'Unknown' }

                                        # Decode user-friendly name
                                        $monitorName = if ($monitor.UserFriendlyName)
                                        {
                                            -join ($monitor.UserFriendlyName | Where-Object { $_ -ne 0 } | ForEach-Object { [char]$_ })
                                        }
                                        else { 'Unknown Monitor' }

                                        # Get resolution using WMI
                                        try
                                        {
                                            Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue
                                            $screen = [System.Windows.Forms.Screen]::AllScreens | Select-Object -First 1
                                            $resolution = "$($screen.Bounds.Width)x$($screen.Bounds.Height)"
                                        }
                                        catch
                                        {
                                            $resolution = $null
                                        }

                                        if ($resolution)
                                        {
                                            $monitorInfo += "$mfgName $monitorName ($resolution)"
                                        }
                                        else
                                        {
                                            $monitorInfo += "$mfgName $monitorName"
                                        }
                                    }

                                    $systemInfo.Monitors = $monitorInfo -join ', '
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve monitor information: $($_.Exception.Message)"
                            }

                            # Get keyboard information
                            try
                            {
                                $keyboards = Get-CimInstance -ClassName Win32_Keyboard -ErrorAction Stop

                                if ($keyboards)
                                {
                                    $keyboardInfo = @()
                                    foreach ($kb in $keyboards)
                                    {
                                        if ($kb.Description)
                                        {
                                            $keyboardInfo += $kb.Description
                                        }
                                    }

                                    if ($keyboardInfo.Count -gt 0)
                                    {
                                        $systemInfo.Keyboard = $keyboardInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve keyboard information: $($_.Exception.Message)"
                            }

                            # Get mouse information
                            try
                            {
                                $mice = Get-CimInstance -ClassName Win32_PointingDevice -ErrorAction Stop

                                if ($mice)
                                {
                                    $mouseInfo = @()
                                    foreach ($mouse in $mice)
                                    {
                                        if ($mouse.Name)
                                        {
                                            $mouseInfo += $mouse.Name
                                        }
                                    }

                                    if ($mouseInfo.Count -gt 0)
                                    {
                                        $systemInfo.Mouse = $mouseInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve mouse information: $($_.Exception.Message)"
                            }

                            # Get network adapter information (physical adapters only)
                            try
                            {
                                $netAdapters = Get-CimInstance -ClassName Win32_NetworkAdapter -ErrorAction Stop |
                                Where-Object {
                                    $_.PhysicalAdapter -eq $true -and
                                    $_.AdapterType -notmatch 'Tunnel|Loopback|Virtual' -and
                                    $_.Name -notmatch 'Virtual|Bluetooth|TAP|VPN'
                                }

                                if ($netAdapters)
                                {
                                    $networkInfo = @()
                                    foreach ($adapter in $netAdapters)
                                    {
                                        $adapterName = $adapter.Name
                                        $speed = $adapter.Speed

                                        if ($speed -and $speed -gt 0)
                                        {
                                            $speedMbps = [Math]::Round($speed / 1MB, 0)
                                            $networkInfo += "$adapterName ($speedMbps Mbps)"
                                        }
                                        else
                                        {
                                            $networkInfo += $adapterName
                                        }
                                    }

                                    if ($networkInfo.Count -gt 0)
                                    {
                                        $systemInfo.NetworkAdapters = $networkInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve network adapter information: $($_.Exception.Message)"
                            }

                            # Get battery information (laptops/portable devices)
                            try
                            {
                                $batteries = Get-CimInstance -ClassName Win32_Battery -ErrorAction Stop
                                if ($batteries)
                                {
                                    $battery = $batteries | Select-Object -First 1

                                    # Battery status codes: 1=Discharging, 2=AC, 3=Fully Charged, 4=Low, 5=Critical
                                    $statusMap = @{
                                        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'
                                    }

                                    $systemInfo.BatteryStatus = $statusMap[[int]$battery.BatteryStatus]
                                    $systemInfo.BatteryChargePercent = $battery.EstimatedChargeRemaining

                                    # Try to get more accurate runtime from WMI BatteryStatus class
                                    try
                                    {
                                        $batteryStatus = Get-CimInstance -Namespace root/WMI -ClassName BatteryStatus -ErrorAction SilentlyContinue | Select-Object -First 1
                                        if ($batteryStatus -and $batteryStatus.Discharging -and $batteryStatus.DischargeRate -gt 0)
                                        {
                                            # Calculate runtime based on remaining capacity and discharge rate
                                            $runtimeHours = [Math]::Round($batteryStatus.RemainingCapacity / $batteryStatus.DischargeRate, 1)
                                            $systemInfo.BatteryEstimatedRuntime = "$runtimeHours hours"
                                        }
                                        elseif ($battery.EstimatedRunTime -and $battery.EstimatedRunTime -lt 71582788)
                                        {
                                            # Fall back to Win32_Battery if WMI method not available or not discharging
                                            $runtimeHours = [Math]::Round($battery.EstimatedRunTime / 60, 1)
                                            $systemInfo.BatteryEstimatedRuntime = "$runtimeHours hours"
                                        }
                                    }
                                    catch
                                    {
                                        Write-Verbose "Could not calculate battery runtime: $($_.Exception.Message)"
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve battery information: $($_.Exception.Message)"
                            }
                        }
                        catch
                        {
                            Write-Warning "Failed to retrieve some Windows system information: $($_.Exception.Message)"
                        }
                    }
                    elseif ($script:IsMacOSPlatform)
                    {
                        # macOS-specific information using system commands
                        Write-Verbose 'Using system commands for macOS system information'

                        try
                        {
                            # Get OS version and name
                            $osVersion = sw_vers -productVersion 2>$null
                            $osName = sw_vers -productName 2>$null

                            # Try to get the macOS release name (e.g., Sequoia, Sonoma, Ventura)
                            $osReleaseName = $null
                            try
                            {
                                $licenseFile = '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf'
                                if (Test-Path $licenseFile)
                                {
                                    $licenseContent = Get-Content $licenseFile -Raw -ErrorAction SilentlyContinue
                                    if ($licenseContent -match 'SOFTWARE LICENSE AGREEMENT FOR macOS\s+(\w+)')
                                    {
                                        $osReleaseName = $matches[1]
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve macOS release name: $($_.Exception.Message)"
                            }

                            # Build the OS string with release name if available
                            if ($osReleaseName)
                            {
                                $systemInfo.OperatingSystem = "$osName $osReleaseName $osVersion".Trim()
                            }
                            else
                            {
                                $systemInfo.OperatingSystem = "$osName $osVersion".Trim()
                            }

                            # Get architecture
                            $arch = uname -m 2>$null
                            $systemInfo.OSArchitecture = $arch
                            $systemInfo.CPUArchitecture = $arch

                            # Get CPU information using sysctl
                            $cpuBrand = sysctl -n machdep.cpu.brand_string 2>$null
                            $systemInfo.CPUName = $cpuBrand

                            $cpuCores = sysctl -n hw.physicalcpu 2>$null
                            if ($cpuCores) { $systemInfo.CPUCores = [int]$cpuCores }

                            $cpuLogical = sysctl -n hw.logicalcpu 2>$null
                            if ($cpuLogical) { $systemInfo.CPULogicalProcessors = [int]$cpuLogical }

                            # Detect hyperthreading/SMT
                            if ($cpuCores -and $cpuLogical)
                            {
                                $coresInt = [int]$cpuCores
                                $logicalInt = [int]$cpuLogical
                                if ($logicalInt -gt $coresInt)
                                {
                                    $systemInfo.HyperthreadingEnabled = $true
                                }
                                else
                                {
                                    $systemInfo.HyperthreadingEnabled = $false
                                }
                            }

                            # CPU frequency: Only available on Intel Macs via hw.cpufrequency
                            # Apple Silicon Macs use dynamic frequency scaling and don't expose a fixed value
                            $cpuFreq = sysctl -n hw.cpufrequency 2>$null
                            if ($cpuFreq -and $cpuFreq -match '^\d+$')
                            {
                                $systemInfo.CPUSpeedMHz = [Math]::Round([int64]$cpuFreq / 1MB, 0)
                            }

                            # Get memory information (in bytes, convert to GB)
                            $totalMem = sysctl -n hw.memsize 2>$null
                            if ($totalMem) { $systemInfo.TotalMemoryGB = [Math]::Round([int64]$totalMem / 1GB, 2) }

                            # Get system load average (macOS)
                            try
                            {
                                $loadAvg = sysctl -n vm.loadavg 2>$null
                                if ($loadAvg)
                                {
                                    # Output format: { 1.23 1.45 1.67 }
                                    $loadValues = $loadAvg -replace '[{}]', '' -split '\s+' | Where-Object { $_ -match '^\d' }
                                    if ($loadValues.Count -ge 3)
                                    {
                                        $systemInfo.SystemLoadAverage = "$($loadValues[0]) (1m), $($loadValues[1]) (5m), $($loadValues[2]) (15m)"
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve system load average: $($_.Exception.Message)"
                            }

                            # Get process and thread count
                            try
                            {
                                $processCount = (Get-Process -ErrorAction SilentlyContinue | Measure-Object).Count
                                if ($processCount)
                                {
                                    $systemInfo.ProcessCount = $processCount
                                }

                                # Get thread count using ps -M (macOS-specific)
                                $threadCountOutput = ps -M -A 2>$null | wc -l 2>$null
                                if ($threadCountOutput)
                                {
                                    $threadCount = [int]$threadCountOutput.Trim()
                                    if ($threadCount -gt 1)
                                    {
                                        # Subtract 1 for the header line
                                        $systemInfo.ThreadCount = $threadCount - 1
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve process/thread count: $($_.Exception.Message)"
                            }

                            # Get available memory using vm_stat
                            # On macOS, "available" memory includes free + inactive + speculative + purgeable pages
                            $vmStat = vm_stat 2>$null
                            if ($vmStat)
                            {
                                # Get actual page size from vm_stat output
                                $pageSizeLine = $vmStat | Select-String 'page size of (\d+) bytes'
                                $pageSize = if ($pageSizeLine -and $pageSizeLine.Matches.Groups.Count -gt 1)
                                {
                                    [int64]$pageSizeLine.Matches.Groups[1].Value
                                }
                                else
                                {
                                    16384  # Default for Apple Silicon, 4096 for Intel
                                }

                                # Parse memory page counts
                                $freePages = 0
                                $inactivePages = 0
                                $speculativePages = 0
                                $purgeablePages = 0

                                $freePagesLine = $vmStat | Select-String 'Pages free:\s+(\d+)'
                                if ($freePagesLine -and $freePagesLine.Matches.Groups.Count -gt 1)
                                {
                                    $freePages = [int64]$freePagesLine.Matches.Groups[1].Value
                                }

                                $inactivePagesLine = $vmStat | Select-String 'Pages inactive:\s+(\d+)'
                                if ($inactivePagesLine -and $inactivePagesLine.Matches.Groups.Count -gt 1)
                                {
                                    $inactivePages = [int64]$inactivePagesLine.Matches.Groups[1].Value
                                }

                                $speculativePagesLine = $vmStat | Select-String 'Pages speculative:\s+(\d+)'
                                if ($speculativePagesLine -and $speculativePagesLine.Matches.Groups.Count -gt 1)
                                {
                                    $speculativePages = [int64]$speculativePagesLine.Matches.Groups[1].Value
                                }

                                $purgeablePagesLine = $vmStat | Select-String 'Pages purgeable:\s+(\d+)'
                                if ($purgeablePagesLine -and $purgeablePagesLine.Matches.Groups.Count -gt 1)
                                {
                                    $purgeablePages = [int64]$purgeablePagesLine.Matches.Groups[1].Value
                                }

                                # Calculate available memory (free + inactive + speculative + purgeable)
                                $availablePages = $freePages + $inactivePages + $speculativePages + $purgeablePages
                                $systemInfo.FreeMemoryGB = [Math]::Round(($availablePages * $pageSize) / 1GB, 2)
                            }

                            # Get swap space information
                            try
                            {
                                $swapUsage = sysctl -n vm.swapusage 2>$null
                                if ($swapUsage)
                                {
                                    # Format: total = 1024.00M  used = 256.50M  free = 767.50M  (encrypted)
                                    if ($swapUsage -match 'total = ([\d.]+)([MG])' -and $swapUsage -match 'used = ([\d.]+)([MG])')
                                    {
                                        $totalValue = [double]$matches[1]
                                        $totalUnit = $matches[2]
                                        $swapUsage -match 'used = ([\d.]+)([MG])' | Out-Null
                                        $usedValue = [double]$matches[1]
                                        $usedUnit = $matches[2]

                                        # Convert to GB
                                        $totalGB = if ($totalUnit -eq 'G') { $totalValue } else { $totalValue / 1KB }
                                        $usedGB = if ($usedUnit -eq 'G') { $usedValue } else { $usedValue / 1KB }

                                        $systemInfo.PageFileTotalGB = [Math]::Round($totalGB, 2)
                                        $systemInfo.PageFileUsedGB = [Math]::Round($usedGB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve swap space information: $($_.Exception.Message)"
                            }

                            # Get serial number and firmware version from system_profiler
                            $hardwareProfile = system_profiler SPHardwareDataType 2>$null

                            # Get hardware model identifier (e.g., Mac16,8)
                            $model = sysctl -n hw.model 2>$null
                            if ([string]::IsNullOrWhiteSpace($model))
                            {
                                $modelIdentifier = $hardwareProfile | Select-String 'Model Identifier:' | Select-Object -First 1
                                if ($modelIdentifier)
                                {
                                    $model = ($modelIdentifier -replace '.*:\s*', '').Trim()
                                }
                            }

                            if ($model)
                            {
                                $model = $model.Trim()
                                $systemInfo.Model = $model
                            }

                            # Get human-friendly model name (generic model name or marketing model, when available)
                            $modelFriendlyName = $null
                            $modelName = $hardwareProfile | Select-String 'Model Name:' | Select-Object -First 1
                            if ($modelName)
                            {
                                $modelFriendlyName = ($modelName -replace '.*:\s*', '').Trim()
                            }

                            $resolvedByMachineAttributes = $false
                            if ($systemInfo.Model)
                            {
                                try
                                {
                                    $machineAttributesPath = '/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/en.lproj/SIMachineAttributes.plist'
                                    if (Test-Path -Path $machineAttributesPath)
                                    {
                                        $marketingModel = & /usr/libexec/PlistBuddy -c "Print :$($systemInfo.Model):_LOCALIZABLE_:marketingModel" $machineAttributesPath 2>$null
                                        if (-not [string]::IsNullOrWhiteSpace($marketingModel))
                                        {
                                            $modelFriendlyName = $marketingModel.Trim()
                                            $resolvedByMachineAttributes = $true
                                        }
                                    }
                                }
                                catch
                                {
                                    Write-Verbose "Could not resolve marketing model name for '$($systemInfo.Model)': $($_.Exception.Message)"
                                }
                            }

                            # Fallback for newer Macs where SIMachineAttributes.plist does not include the model identifier
                            if (-not $resolvedByMachineAttributes -and $systemInfo.Model)
                            {
                                try
                                {
                                    $coreTypesPath = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/CoreTypes-0022.bundle/Contents/Info.plist'
                                    if (Test-Path -Path $coreTypesPath)
                                    {
                                        $coreTypesJson = plutil -convert json -o - $coreTypesPath 2>$null
                                        if ($coreTypesJson)
                                        {
                                            $coreTypesInfo = $coreTypesJson | ConvertFrom-Json -ErrorAction Stop
                                            $declarations = @($coreTypesInfo.UTExportedTypeDeclarations)

                                            foreach ($declaration in $declarations)
                                            {
                                                $tagSpecification = $declaration.UTTypeTagSpecification
                                                if (-not $tagSpecification)
                                                {
                                                    continue
                                                }

                                                $deviceModelCodes = @($tagSpecification.'com.apple.device-model-code')
                                                if (-not $deviceModelCodes -or $deviceModelCodes.Count -eq 0)
                                                {
                                                    continue
                                                }

                                                $matchingCode = $deviceModelCodes | Where-Object {
                                                    $_ -eq $systemInfo.Model -or $_ -like "$($systemInfo.Model)@*"
                                                } | Select-Object -First 1

                                                if (-not $matchingCode)
                                                {
                                                    continue
                                                }

                                                $typeCandidates = @()
                                                if ($declaration.UTTypeIdentifier)
                                                {
                                                    $typeCandidates += [string]$declaration.UTTypeIdentifier
                                                }

                                                if ($declaration.UTTypeConformsTo)
                                                {
                                                    $typeCandidates += @($declaration.UTTypeConformsTo | ForEach-Object { [string]$_ })
                                                }

                                                $canonicalType = $typeCandidates | Where-Object {
                                                    $_ -like 'com.apple.mac*' -and
                                                    $_ -notlike '*-silver' -and
                                                    $_ -notlike '*-space-black' -and
                                                    $_ -notlike '*-midnight' -and
                                                    $_ -notlike '*-starlight'
                                                } | Select-Object -First 1

                                                $derivedFriendlyName = ConvertFrom-AppleModelTypeIdentifier -TypeIdentifier $canonicalType
                                                if ($derivedFriendlyName)
                                                {
                                                    $modelFriendlyName = $derivedFriendlyName
                                                }

                                                break
                                            }
                                        }
                                    }
                                }
                                catch
                                {
                                    Write-Verbose "Could not derive model marketing name from CoreTypes: $($_.Exception.Message)"
                                }
                            }

                            if ($modelFriendlyName)
                            {
                                $systemInfo.ModelFriendlyName = $modelFriendlyName
                            }

                            # Get system manufacturer (Apple)
                            $systemInfo.Manufacturer = 'Apple Inc.'

                            $serial = $hardwareProfile | Select-String 'Serial Number'
                            if ($serial)
                            {
                                $systemInfo.SerialNumber = ($serial -replace '.*:\s*', '').Trim()
                            }

                            # Get firmware version (macOS equivalent of BIOS version)
                            $firmware = $hardwareProfile | Select-String 'System Firmware Version'
                            if ($firmware)
                            {
                                $systemInfo.BIOSVersion = ($firmware -replace '.*:\s*', '').Trim()
                            }

                            # Detect virtualization (macOS)
                            try
                            {
                                $systemInfo.IsVirtualMachine = $false
                                $systemInfo.VirtualizationType = $null

                                # Check manufacturer and model
                                if ($model -like '*VMware*')
                                {
                                    $systemInfo.IsVirtualMachine = $true
                                    $systemInfo.VirtualizationType = 'VMware'
                                }
                                elseif ($model -like '*VirtualBox*')
                                {
                                    $systemInfo.IsVirtualMachine = $true
                                    $systemInfo.VirtualizationType = 'VirtualBox'
                                }
                                elseif ($model -like '*Parallels*')
                                {
                                    $systemInfo.IsVirtualMachine = $true
                                    $systemInfo.VirtualizationType = 'Parallels'
                                }

                                # Check for hypervisor using sysctl
                                $hypervisor = sysctl -n kern.hv_support 2>$null
                                if ($hypervisor -eq '1' -and -not $systemInfo.IsVirtualMachine)
                                {
                                    # Running on a system with hypervisor support, check for specific VM indicators
                                    $hvVendor = sysctl -n machdep.cpu.brand_string 2>$null
                                    if ($hvVendor -like '*QEMU*' -or $hvVendor -like '*Virtual*')
                                    {
                                        $systemInfo.IsVirtualMachine = $true
                                        $systemInfo.VirtualizationType = 'QEMU/KVM'
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not detect virtualization: $($_.Exception.Message)"
                            }

                            # Get CPU temperature (macOS - requires third-party tools or specific hardware)
                            try
                            {
                                # Try using powermetrics (requires sudo for detailed info, but basic temp might work)
                                # This is not reliable without admin, so we'll skip it in standard usage
                                # Most macOS systems require specialized tools like iStats or SMC readers
                                Write-Verbose 'CPU temperature monitoring on macOS typically requires third-party tools'
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve CPU temperature: $($_.Exception.Message)"
                            }

                            # Get boot time
                            $bootTime = sysctl -n kern.boottime 2>$null
                            if ($bootTime -match 'sec = (\d+)')
                            {
                                $bootEpoch = [int64]$matches[1]
                                $systemInfo.LastBootTime = [DateTimeOffset]::FromUnixTimeSeconds($bootEpoch).LocalDateTime
                                $systemInfo.Uptime = (Get-Date) - $systemInfo.LastBootTime
                            }

                            # Get system drive information (macOS)
                            try
                            {
                                $dfOutput = df -k / 2>$null | Select-Object -Skip 1
                                if ($dfOutput)
                                {
                                    # Parse df output: Filesystem 1K-blocks Used Available Capacity iused ifree %iused Mounted
                                    $parts = $dfOutput -split '\s+' | Where-Object { $_ }
                                    if ($parts.Count -ge 4)
                                    {
                                        $totalKB = [int64]$parts[1]
                                        $usedKB = [int64]$parts[2]
                                        $availKB = [int64]$parts[3]

                                        $systemInfo.SystemDriveTotalGB = [Math]::Round($totalKB / 1MB, 2)
                                        $systemInfo.SystemDriveUsedGB = [Math]::Round($usedKB / 1MB, 2)
                                        $systemInfo.SystemDriveFreeGB = [Math]::Round($availKB / 1MB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve system drive information: $($_.Exception.Message)"
                            }

                            # Get time zone using .NET (cross-platform, no admin required)
                            try
                            {
                                $timeZone = [System.TimeZoneInfo]::Local
                                $isDst = $timeZone.IsDaylightSavingTime((Get-Date))
                                $offset = $timeZone.GetUtcOffset((Get-Date))
                                $offsetString = if ($offset.TotalHours -ge 0) { "+$($offset.Hours)" } else { "$($offset.Hours)" }

                                if ($isDst -and $timeZone.DaylightName)
                                {
                                    $systemInfo.TimeZone = "$($timeZone.DaylightName) (UTC$offsetString)"
                                }
                                else
                                {
                                    $systemInfo.TimeZone = "$($timeZone.StandardName) (UTC$offsetString)"
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve timezone: $($_.Exception.Message)"
                            }

                            # Get GPU information using system_profiler
                            try
                            {
                                $displayProfile = system_profiler SPDisplaysDataType 2>$null
                                if ($displayProfile)
                                {
                                    # Parse GPU chipset model
                                    $chipsetLines = $displayProfile | Select-String 'Chipset Model:'
                                    if ($chipsetLines)
                                    {
                                        $gpuInfo = @()
                                        foreach ($line in $chipsetLines)
                                        {
                                            $gpuName = ($line -replace '.*Chipset Model:\s*', '').Trim()

                                            # Try to get VRAM for this GPU
                                            # Find the line number and look for VRAM in subsequent lines
                                            $lineIndex = [array]::IndexOf($displayProfile, $line.Line)
                                            $vramLine = $displayProfile[($lineIndex + 1)..($lineIndex + 10)] |
                                            Select-String 'VRAM.*:' | Select-Object -First 1

                                            if ($vramLine)
                                            {
                                                $vramText = ($vramLine -replace '.*VRAM.*:\s*', '').Trim()
                                                # Extract numeric value and convert to GB
                                                if ($vramText -match '(\d+)\s*GB')
                                                {
                                                    $vramGB = [int]$matches[1]
                                                    $gpuInfo += "$gpuName ($vramGB GB)"
                                                }
                                                elseif ($vramText -match '(\d+)\s*MB')
                                                {
                                                    $vramMB = [int]$matches[1]
                                                    $vramGB = [Math]::Round($vramMB / 1024, 2)
                                                    $gpuInfo += "$gpuName ($vramGB GB)"
                                                }
                                                else
                                                {
                                                    $gpuInfo += "$gpuName ($vramText)"
                                                }
                                            }
                                            else
                                            {
                                                $gpuInfo += $gpuName
                                            }
                                        }

                                        $systemInfo.GPUName = $gpuInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve GPU information: $($_.Exception.Message)"
                            }

                            # Get physical disk information using diskutil (embedded drives only)
                            try
                            {
                                $diskList = diskutil list 2>$null
                                if ($diskList)
                                {
                                    $diskInfo = @()

                                    # Parse disk list to find physical disks (disk0, disk1, etc., but not external)
                                    foreach ($line in $diskList)
                                    {
                                        # Match disk identifiers like /dev/disk0
                                        if ($line -match '/dev/(disk\d+)\s+\(([^)]+)\)')
                                        {
                                            $diskId = $matches[1]
                                            $diskType = $matches[2]

                                            # Skip external/removable drives
                                            if ($diskType -notmatch 'external|removable')
                                            {

                                                # Get detailed disk info
                                                $diskInfo_detailed = diskutil info $diskId 2>$null
                                                if ($diskInfo_detailed)
                                                {
                                                    $deviceName = $diskInfo_detailed | Select-String 'Device / Media Name:' |
                                                    ForEach-Object { ($_ -replace '.*Device / Media Name:\s*', '').Trim() }

                                                    $diskSize = $diskInfo_detailed | Select-String 'Disk Size:' |
                                                    ForEach-Object { ($_ -replace '.*Disk Size:\s*', '').Trim() }

                                                    $protocol = $diskInfo_detailed | Select-String 'Protocol:' |
                                                    ForEach-Object { ($_ -replace '.*Protocol:\s*', '').Trim() }

                                                    if ($deviceName -and $diskSize)
                                                    {
                                                        # Extract size in GB from the size string (e.g., "500.1 GB")
                                                        if ($diskSize -match '([\d.]+)\s*GB')
                                                        {
                                                            $sizeGB = [Math]::Round([double]$matches[1], 2)
                                                            if ($protocol)
                                                            {
                                                                $diskInfo += "$deviceName ($sizeGB GB, $protocol)"
                                                            }
                                                            else
                                                            {
                                                                $diskInfo += "$deviceName ($sizeGB GB)"
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    if ($diskInfo.Count -gt 0)
                                    {
                                        $systemInfo.PhysicalDisks = $diskInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve physical disk information: $($_.Exception.Message)"
                            }

                            # Get audio device information
                            try
                            {
                                $audioProfile = system_profiler SPAudioDataType 2>$null
                                if ($audioProfile)
                                {
                                    # Parse audio device names (look for device names under different categories)
                                    $deviceLines = $audioProfile | Select-String '^\s{4}\w.*:$' |
                                    Where-Object { $_ -notmatch 'Devices:|Audio ID' }

                                    if ($deviceLines)
                                    {
                                        $audioInfo = @()
                                        foreach ($line in $deviceLines)
                                        {
                                            $deviceName = ($line -replace ':\s*$', '').Trim()
                                            if ($deviceName -and $deviceName -notmatch '^(Input|Output)')
                                            {
                                                $audioInfo += $deviceName
                                            }
                                        }

                                        # Remove duplicates
                                        $audioInfo = $audioInfo | Select-Object -Unique

                                        if ($audioInfo.Count -gt 0)
                                        {
                                            $systemInfo.AudioDevices = $audioInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve audio device information: $($_.Exception.Message)"
                            }

                            # Get monitor/display information
                            try
                            {
                                $displayProfile = system_profiler SPDisplaysDataType 2>$null
                                if ($displayProfile)
                                {
                                    $monitorInfo = @()
                                    # Parse display information
                                    $displayLines = $displayProfile | Select-String '^\s{6}\w.*:$'

                                    foreach ($line in $displayLines)
                                    {
                                        $displayName = ($line -replace ':\s*$', '').Trim()

                                        # Get resolution for this display
                                        $lineIndex = [array]::IndexOf($displayProfile, $line.Line)
                                        $resolutionLine = $displayProfile[($lineIndex + 1)..($lineIndex + 10)] |
                                        Select-String 'Resolution:' | Select-Object -First 1

                                        if ($resolutionLine)
                                        {
                                            $resolution = ($resolutionLine -replace '.*Resolution:\s*', '').Trim()
                                            $monitorInfo += "$displayName ($resolution)"
                                        }
                                        else
                                        {
                                            $monitorInfo += $displayName
                                        }
                                    }

                                    if ($monitorInfo.Count -gt 0)
                                    {
                                        $systemInfo.Monitors = $monitorInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve monitor information: $($_.Exception.Message)"
                            }

                            # Get keyboard information
                            try
                            {
                                $usbProfile = system_profiler SPUSBDataType 2>$null
                                if ($usbProfile)
                                {
                                    $keyboardLines = $usbProfile | Select-String 'Keyboard'
                                    if ($keyboardLines)
                                    {
                                        $keyboardInfo = @()
                                        foreach ($line in $keyboardLines)
                                        {
                                            # Extract keyboard name
                                            if ($line -match '([^:]+Keyboard[^:]*):')
                                            {
                                                $kbName = $matches[1].Trim()
                                                if ($kbName -and $keyboardInfo -notcontains $kbName)
                                                {
                                                    $keyboardInfo += $kbName
                                                }
                                            }
                                        }

                                        if ($keyboardInfo.Count -gt 0)
                                        {
                                            $systemInfo.Keyboard = $keyboardInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve keyboard information: $($_.Exception.Message)"
                            }

                            # Get mouse/pointing device information
                            try
                            {
                                $usbProfile = system_profiler SPUSBDataType 2>$null
                                if ($usbProfile)
                                {
                                    $mouseLines = $usbProfile | Select-String 'Mouse|Trackpad|Pointing'
                                    if ($mouseLines)
                                    {
                                        $mouseInfo = @()
                                        foreach ($line in $mouseLines)
                                        {
                                            # Extract mouse/trackpad name
                                            if ($line -match '([^:]+(?:Mouse|Trackpad|Pointing)[^:]*):')
                                            {
                                                $mouseName = $matches[1].Trim()
                                                if ($mouseName -and $mouseInfo -notcontains $mouseName)
                                                {
                                                    $mouseInfo += $mouseName
                                                }
                                            }
                                        }

                                        if ($mouseInfo.Count -gt 0)
                                        {
                                            $systemInfo.Mouse = $mouseInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve mouse information: $($_.Exception.Message)"
                            }

                            # Get network adapter information
                            try
                            {
                                $networkProfile = system_profiler SPNetworkDataType 2>$null
                                if ($networkProfile)
                                {
                                    $networkInfo = @()
                                    # Get Ethernet and Wi-Fi interfaces
                                    $interfaceLines = $networkProfile | Select-String '^\s{4}(Ethernet|Wi-Fi|Thunderbolt).*:$'

                                    foreach ($line in $interfaceLines)
                                    {
                                        $interfaceName = ($line -replace ':\s*$', '').Trim()

                                        # Try to get hardware info
                                        $lineIndex = [array]::IndexOf($networkProfile, $line.Line)
                                        $hardwareLine = $networkProfile[($lineIndex + 1)..($lineIndex + 10)] |
                                        Select-String 'Hardware:' | Select-Object -First 1

                                        if ($hardwareLine)
                                        {
                                            $hardware = ($hardwareLine -replace '.*Hardware:\s*', '').Trim()
                                            $networkInfo += "$interfaceName ($hardware)"
                                        }
                                        else
                                        {
                                            $networkInfo += $interfaceName
                                        }
                                    }

                                    if ($networkInfo.Count -gt 0)
                                    {
                                        $systemInfo.NetworkAdapters = $networkInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve network adapter information: $($_.Exception.Message)"
                            }

                            # Get battery information (macOS laptops)
                            try
                            {
                                $batteryInfo = system_profiler SPPowerDataType 2>$null
                                if ($batteryInfo)
                                {
                                    # Check if battery information exists
                                    $batteryInstalled = $batteryInfo | Select-String 'Battery Information:'
                                    if ($batteryInstalled)
                                    {
                                        # Get charging status
                                        $chargingStatus = $batteryInfo | Select-String 'Charging:\s*(.*)' | Select-Object -First 1
                                        if ($chargingStatus -and $chargingStatus.Matches.Groups.Count -gt 1)
                                        {
                                            $isCharging = $chargingStatus.Matches.Groups[1].Value.Trim()
                                            if ($isCharging -eq 'Yes')
                                            {
                                                $systemInfo.BatteryStatus = 'Charging (AC)'
                                            }
                                            else
                                            {
                                                # Check if fully charged
                                                $fullyCharged = $batteryInfo | Select-String 'Fully Charged:\s*(.*)' | Select-Object -First 1
                                                if ($fullyCharged -and $fullyCharged.Matches.Groups.Count -gt 1)
                                                {
                                                    $isFullyCharged = $fullyCharged.Matches.Groups[1].Value.Trim()
                                                    if ($isFullyCharged -eq 'Yes')
                                                    {
                                                        $systemInfo.BatteryStatus = 'Fully Charged'
                                                    }
                                                    else
                                                    {
                                                        $systemInfo.BatteryStatus = 'Discharging'
                                                    }
                                                }
                                                else
                                                {
                                                    $systemInfo.BatteryStatus = 'Discharging'
                                                }
                                            }
                                        }

                                        # Get charge percentage
                                        $chargeInfo = $batteryInfo | Select-String 'State of Charge \(%\):\s*(\d+)'
                                        if ($chargeInfo -and $chargeInfo.Matches.Groups.Count -gt 1)
                                        {
                                            $systemInfo.BatteryChargePercent = [int]$chargeInfo.Matches.Groups[1].Value
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve battery information: $($_.Exception.Message)"
                            }
                        }
                        catch
                        {
                            Write-Warning "Failed to retrieve some macOS system information: $($_.Exception.Message)"
                        }
                    }
                    elseif ($script:IsLinuxPlatform)
                    {
                        # Linux-specific information using system commands
                        Write-Verbose 'Using system commands for Linux system information'

                        try
                        {
                            # Get OS information from /etc/os-release
                            if (Test-Path '/etc/os-release')
                            {
                                $osRelease = Get-Content '/etc/os-release' -ErrorAction SilentlyContinue
                                $prettyName = $osRelease | Where-Object { $_ -match '^PRETTY_NAME=' }
                                if ($prettyName)
                                {
                                    $systemInfo.OperatingSystem = ($prettyName -replace 'PRETTY_NAME=', '' -replace '"', '').Trim()
                                }
                            }

                            # Get architecture
                            $arch = uname -m 2>$null
                            $systemInfo.OSArchitecture = $arch
                            $systemInfo.CPUArchitecture = $arch

                            # Get CPU information from /proc/cpuinfo
                            if (Test-Path '/proc/cpuinfo')
                            {
                                $cpuInfo = Get-Content '/proc/cpuinfo' -ErrorAction SilentlyContinue

                                # Get CPU model name
                                $modelName = $cpuInfo | Where-Object { $_ -match '^model name' } | Select-Object -First 1
                                if ($modelName)
                                {
                                    $systemInfo.CPUName = ($modelName -replace 'model name\s*:\s*', '').Trim()
                                }

                                # Get CPU frequency
                                $cpuMhz = $cpuInfo | Where-Object { $_ -match '^cpu MHz' } | Select-Object -First 1
                                if ($cpuMhz)
                                {
                                    $freq = ($cpuMhz -replace 'cpu MHz\s*:\s*', '').Trim()
                                    $systemInfo.CPUSpeedMHz = [Math]::Round([double]$freq, 0)
                                }

                                # Count physical and logical processors
                                $physicalIds = $cpuInfo | Where-Object { $_ -match '^physical id' } | ForEach-Object { ($_ -split ':')[1].Trim() } | Select-Object -Unique
                                $coresPerSocket = $cpuInfo | Where-Object { $_ -match '^cpu cores' } | Select-Object -First 1
                                if ($coresPerSocket)
                                {
                                    $coresCount = [int]($coresPerSocket -replace 'cpu cores\s*:\s*', '').Trim()
                                    $socketCount = if ($physicalIds.Count -gt 0) { $physicalIds.Count } else { 1 }
                                    $systemInfo.CPUCores = $coresCount * $socketCount
                                }

                                $processors = $cpuInfo | Where-Object { $_ -match '^processor' }
                                $systemInfo.CPULogicalProcessors = $processors.Count

                                # Detect hyperthreading/SMT
                                if ($systemInfo.CPUCores -and $systemInfo.CPULogicalProcessors)
                                {
                                    if ($systemInfo.CPULogicalProcessors -gt $systemInfo.CPUCores)
                                    {
                                        $systemInfo.HyperthreadingEnabled = $true
                                    }
                                    else
                                    {
                                        $systemInfo.HyperthreadingEnabled = $false
                                    }
                                }
                            }

                            # Remove duplicate CPU cores calculation
                            # Already handled above with PowerShell 5.1 compatible syntax

                            # Get system load average (Linux)
                            try
                            {
                                if (Test-Path '/proc/loadavg')
                                {
                                    $loadAvg = Get-Content '/proc/loadavg' -ErrorAction SilentlyContinue
                                    if ($loadAvg)
                                    {
                                        $loadValues = $loadAvg -split '\s+'
                                        if ($loadValues.Count -ge 3)
                                        {
                                            $systemInfo.SystemLoadAverage = "$($loadValues[0]) (1m), $($loadValues[1]) (5m), $($loadValues[2]) (15m)"
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve system load average: $($_.Exception.Message)"
                            }

                            # Get process and thread count
                            try
                            {
                                $processCount = (Get-Process -ErrorAction SilentlyContinue | Measure-Object).Count
                                if ($processCount)
                                {
                                    $systemInfo.ProcessCount = $processCount
                                }

                                # Get thread count using ps with nlwp (number of light-weight processes/threads)
                                # This is more efficient than parsing /proc filesystem
                                $threadCountOutput = ps -A -o pid, nlwp 2>$null | awk 'NR>1 {sum+=$2} END {print sum}' 2>$null
                                if ($threadCountOutput)
                                {
                                    $threadCount = [int]$threadCountOutput.Trim()
                                    if ($threadCount -gt 0)
                                    {
                                        $systemInfo.ThreadCount = $threadCount
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve process/thread count: $($_.Exception.Message)"
                            }

                            # Get memory information from /proc/meminfo
                            if (Test-Path '/proc/meminfo')
                            {
                                $memInfo = Get-Content '/proc/meminfo' -ErrorAction SilentlyContinue

                                $totalMem = $memInfo | Where-Object { $_ -match '^MemTotal:' }
                                if ($totalMem)
                                {
                                    $totalKb = [int64](($totalMem -replace '[^\d]', '').Trim())
                                    $systemInfo.TotalMemoryGB = [Math]::Round($totalKb / 1MB, 2)
                                }

                                $availMem = $memInfo | Where-Object { $_ -match '^MemAvailable:' }
                                if ($availMem)
                                {
                                    $availKb = [int64](($availMem -replace '[^\d]', '').Trim())
                                    $systemInfo.FreeMemoryGB = [Math]::Round($availKb / 1MB, 2)
                                }

                                # Get swap space information
                                $swapTotal = $memInfo | Where-Object { $_ -match '^SwapTotal:' }
                                if ($swapTotal)
                                {
                                    $swapTotalKB = [int64](($swapTotal -replace '[^\d]', '').Trim())
                                    $systemInfo.PageFileTotalGB = [Math]::Round($swapTotalKB / 1MB, 2)
                                }

                                $swapFree = $memInfo | Where-Object { $_ -match '^SwapFree:' }
                                if ($swapFree -and $swapTotal)
                                {
                                    $swapFreeKB = [int64](($swapFree -replace '[^\d]', '').Trim())
                                    $swapTotalKB = [int64](($swapTotal -replace '[^\d]', '').Trim())
                                    $swapUsedKB = $swapTotalKB - $swapFreeKB
                                    $systemInfo.PageFileUsedGB = [Math]::Round($swapUsedKB / 1MB, 2)
                                }
                            }

                            # Get system manufacturer and model using dmidecode (requires sudo)
                            $dmidecodeAvailable = Get-Command dmidecode -ErrorAction SilentlyContinue
                            if ($dmidecodeAvailable)
                            {
                                try
                                {
                                    $systemManufacturer = dmidecode -s system-manufacturer 2>$null
                                    if ($systemManufacturer) { $systemInfo.Manufacturer = $systemManufacturer.Trim() }

                                    $systemProduct = dmidecode -s system-product-name 2>$null
                                    if ($systemProduct)
                                    {
                                        $systemInfo.Model = $systemProduct.Trim()
                                        $systemInfo.ModelFriendlyName = $systemInfo.Model
                                    }

                                    $systemSerial = dmidecode -s system-serial-number 2>$null
                                    if ($systemSerial) { $systemInfo.SerialNumber = $systemSerial.Trim() }

                                    $biosVersion = dmidecode -s bios-version 2>$null
                                    if ($biosVersion) { $systemInfo.BIOSVersion = $biosVersion.Trim() }
                                }
                                catch
                                {
                                    Write-Verbose 'dmidecode commands require elevated privileges for full information'
                                }
                            }

                            # Detect virtualization (Linux)
                            try
                            {
                                $systemInfo.IsVirtualMachine = $false
                                $systemInfo.VirtualizationType = $null

                                # Check for systemd-detect-virt
                                $detectVirt = Get-Command systemd-detect-virt -ErrorAction SilentlyContinue
                                if ($detectVirt)
                                {
                                    $virtType = systemd-detect-virt 2>$null
                                    if ($virtType -and $virtType -ne 'none')
                                    {
                                        $systemInfo.IsVirtualMachine = $true
                                        $systemInfo.VirtualizationType = $virtType.Trim()
                                    }
                                }
                                else
                                {
                                    # Fallback: Check manufacturer and model
                                    if ($systemInfo.Manufacturer -or $systemInfo.Model)
                                    {
                                        $vmIndicators = @(
                                            @{ Pattern = 'VMware'; Type = 'VMware' }
                                            @{ Pattern = 'VirtualBox'; Type = 'VirtualBox' }
                                            @{ Pattern = 'QEMU'; Type = 'QEMU/KVM' }
                                            @{ Pattern = 'Microsoft'; Type = 'Hyper-V' }
                                            @{ Pattern = 'Xen'; Type = 'Xen' }
                                            @{ Pattern = 'KVM'; Type = 'KVM' }
                                        )

                                        foreach ($indicator in $vmIndicators)
                                        {
                                            if ($systemInfo.Manufacturer -like "*$($indicator.Pattern)*" -or
                                                $systemInfo.Model -like "*$($indicator.Pattern)*")
                                            {
                                                $systemInfo.IsVirtualMachine = $true
                                                $systemInfo.VirtualizationType = $indicator.Type
                                                break
                                            }
                                        }
                                    }

                                    # Check /proc/cpuinfo for hypervisor flag
                                    if (-not $systemInfo.IsVirtualMachine -and (Test-Path '/proc/cpuinfo'))
                                    {
                                        $cpuinfoContent = Get-Content '/proc/cpuinfo' -ErrorAction SilentlyContinue
                                        if ($cpuinfoContent -match 'hypervisor')
                                        {
                                            $systemInfo.IsVirtualMachine = $true
                                            $systemInfo.VirtualizationType = 'Unknown Hypervisor'
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not detect virtualization: $($_.Exception.Message)"
                            }

                            # Get CPU temperature (Linux)
                            try
                            {
                                # Try reading from thermal zones
                                $thermalZones = Get-ChildItem -Path '/sys/class/thermal/thermal_zone*' -ErrorAction SilentlyContinue
                                if ($thermalZones)
                                {
                                    $temps = @()
                                    foreach ($zone in $thermalZones)
                                    {
                                        $tempFile = Join-Path -Path $zone.FullName -ChildPath 'temp'
                                        if (Test-Path $tempFile)
                                        {
                                            $tempValue = Get-Content $tempFile -ErrorAction SilentlyContinue
                                            if ($tempValue -and $tempValue -match '^\d+$')
                                            {
                                                # Temperature is in millidegrees Celsius
                                                $temps += [int]$tempValue / 1000
                                            }
                                        }
                                    }

                                    if ($temps.Count -gt 0)
                                    {
                                        $avgTemp = ($temps | Measure-Object -Average).Average
                                        $systemInfo.CPUTemperatureCelsius = [Math]::Round($avgTemp, 1)
                                        $systemInfo.CPUTemperatureFahrenheit = [Math]::Round(($avgTemp * 9 / 5) + 32, 1)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve CPU temperature: $($_.Exception.Message)"
                            }

                            # Get uptime
                            if (Test-Path '/proc/uptime')
                            {
                                $uptimeContent = Get-Content '/proc/uptime' -ErrorAction SilentlyContinue
                                if ($uptimeContent)
                                {
                                    $uptimeSeconds = [Math]::Floor([double]($uptimeContent -split ' ')[0])
                                    $systemInfo.LastBootTime = (Get-Date).AddSeconds(-$uptimeSeconds)
                                    $systemInfo.Uptime = New-TimeSpan -Seconds $uptimeSeconds
                                }
                            }

                            # Get system drive information (Linux)
                            try
                            {
                                $dfOutput = df -k / 2>$null | Select-Object -Skip 1
                                if ($dfOutput)
                                {
                                    # Parse df output: Filesystem 1K-blocks Used Available Use% Mounted
                                    $parts = $dfOutput -split '\s+' | Where-Object { $_ }
                                    if ($parts.Count -ge 4)
                                    {
                                        $totalKB = [int64]$parts[1]
                                        $usedKB = [int64]$parts[2]
                                        $availKB = [int64]$parts[3]

                                        $systemInfo.SystemDriveTotalGB = [Math]::Round($totalKB / 1MB, 2)
                                        $systemInfo.SystemDriveUsedGB = [Math]::Round($usedKB / 1MB, 2)
                                        $systemInfo.SystemDriveFreeGB = [Math]::Round($availKB / 1MB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve system drive information: $($_.Exception.Message)"
                            }

                            # Get time zone using .NET (cross-platform, consistent with other platforms)
                            try
                            {
                                $timeZone = [System.TimeZoneInfo]::Local
                                $isDst = $timeZone.IsDaylightSavingTime((Get-Date))
                                $offset = $timeZone.GetUtcOffset((Get-Date))
                                $offsetString = if ($offset.TotalHours -ge 0) { "+$($offset.Hours)" } else { "$($offset.Hours)" }

                                if ($isDst -and $timeZone.DaylightName)
                                {
                                    $systemInfo.TimeZone = "$($timeZone.DaylightName) (UTC$offsetString)"
                                }
                                else
                                {
                                    $systemInfo.TimeZone = "$($timeZone.StandardName) (UTC$offsetString)"
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve timezone: $($_.Exception.Message)"
                            }

                            # Get GPU information using lspci (requires pciutils package)
                            try
                            {
                                $lspciAvailable = Get-Command lspci -ErrorAction SilentlyContinue
                                if ($lspciAvailable)
                                {
                                    $gpuDevices = lspci 2>$null | Select-String 'VGA|3D|Display'
                                    if ($gpuDevices)
                                    {
                                        $gpuInfo = @()
                                        foreach ($gpu in $gpuDevices)
                                        {
                                            # Extract GPU name from lspci output
                                            # Format: "00:02.0 VGA compatible controller: Intel Corporation Device"
                                            if ($gpu -match ':\s*(.+)')
                                            {
                                                $gpuName = $matches[1].Trim()
                                                # Clean up the name (remove "VGA compatible controller:" prefix)
                                                $gpuName = $gpuName -replace '^(VGA compatible controller|3D controller|Display controller):\s*', ''
                                                $gpuInfo += $gpuName
                                            }
                                        }

                                        if ($gpuInfo.Count -gt 0)
                                        {
                                            $systemInfo.GPUName = $gpuInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve GPU information: $($_.Exception.Message)"
                            }

                            # Get physical disk information using lsblk (embedded drives only)
                            try
                            {
                                $lsblkAvailable = Get-Command lsblk -ErrorAction SilentlyContinue
                                if ($lsblkAvailable)
                                {
                                    # Get block devices that are disks (not partitions) and not removable
                                    $diskList = lsblk -d -o NAME, SIZE, TYPE, TRAN, MODEL -n 2>$null |
                                    Where-Object { $_ -match '\bdisk\b' -and $_ -notmatch '\busb\b' }

                                    if ($diskList)
                                    {
                                        $diskInfo = @()
                                        foreach ($disk in $diskList)
                                        {
                                            # Parse lsblk output: NAME SIZE TYPE TRAN MODEL
                                            $parts = $disk -split '\s+' | Where-Object { $_ }
                                            if ($parts.Count -ge 3)
                                            {
                                                $diskSize = $parts[1]
                                                $diskTran = if ($parts.Count -ge 4) { $parts[3] } else { $null }
                                                $diskModel = if ($parts.Count -ge 5) { $parts[4..($parts.Count - 1)] -join ' ' } else { 'Unknown' }

                                                # Skip if transport is USB
                                                if ($diskTran -eq 'usb')
                                                {
                                                    continue
                                                }

                                                if ($diskTran)
                                                {
                                                    $diskInfo += "$diskModel ($diskSize, $diskTran)"
                                                }
                                                else
                                                {
                                                    $diskInfo += "$diskModel ($diskSize)"
                                                }
                                            }
                                        }

                                        if ($diskInfo.Count -gt 0)
                                        {
                                            $systemInfo.PhysicalDisks = $diskInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve physical disk information: $($_.Exception.Message)"
                            }

                            # Get audio device information using aplay (ALSA)
                            try
                            {
                                $aplayAvailable = Get-Command aplay -ErrorAction SilentlyContinue
                                if ($aplayAvailable)
                                {
                                    $audioDevices = aplay -l 2>$null | Select-String 'card \d+:'
                                    if ($audioDevices)
                                    {
                                        $audioInfo = @()
                                        foreach ($device in $audioDevices)
                                        {
                                            # Parse output: "card 0: PCH [HDA Intel PCH], device 0: ..."
                                            if ($device -match 'card \d+:\s*([^\[,]+)')
                                            {
                                                $audioName = $matches[1].Trim()
                                                if ($audioName -and $audioInfo -notcontains $audioName)
                                                {
                                                    $audioInfo += $audioName
                                                }
                                            }
                                        }

                                        if ($audioInfo.Count -gt 0)
                                        {
                                            $systemInfo.AudioDevices = $audioInfo -join ', '
                                        }
                                    }
                                }
                                else
                                {
                                    # Try PulseAudio as fallback
                                    $pacmdAvailable = Get-Command pactl -ErrorAction SilentlyContinue
                                    if ($pacmdAvailable)
                                    {
                                        $audioSinks = pactl list sinks short 2>$null
                                        if ($audioSinks)
                                        {
                                            $audioInfo = @()
                                            foreach ($sink in $audioSinks)
                                            {
                                                # Parse pactl output
                                                $parts = $sink -split '\t' | Where-Object { $_ }
                                                if ($parts.Count -ge 2)
                                                {
                                                    $sinkName = $parts[1]
                                                    if ($sinkName -and $audioInfo -notcontains $sinkName)
                                                    {
                                                        $audioInfo += $sinkName
                                                    }
                                                }
                                            }

                                            if ($audioInfo.Count -gt 0)
                                            {
                                                $systemInfo.AudioDevices = $audioInfo -join ', '
                                            }
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve audio device information: $($_.Exception.Message)"
                            }

                            # Get monitor/display information using xrandr
                            try
                            {
                                $xrandrAvailable = Get-Command xrandr -ErrorAction SilentlyContinue
                                if ($xrandrAvailable)
                                {
                                    $displayOutput = xrandr 2>$null
                                    if ($displayOutput)
                                    {
                                        $monitorInfo = @()
                                        $connectedDisplays = $displayOutput | Select-String ' connected'

                                        foreach ($display in $connectedDisplays)
                                        {
                                            # Parse xrandr output: "HDMI-1 connected 1920x1080+0+0 ..."
                                            if ($display -match '^(\S+)\s+connected\s+(?:primary\s+)?(\d+x\d+)')
                                            {
                                                $displayName = $matches[1]
                                                $resolution = $matches[2]
                                                $monitorInfo += "$displayName ($resolution)"
                                            }
                                            elseif ($display -match '^(\S+)\s+connected')
                                            {
                                                $displayName = $matches[1]
                                                $monitorInfo += $displayName
                                            }
                                        }

                                        if ($monitorInfo.Count -gt 0)
                                        {
                                            $systemInfo.Monitors = $monitorInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve monitor information: $($_.Exception.Message)"
                            }

                            # Get keyboard information
                            try
                            {
                                $xinputAvailable = Get-Command xinput -ErrorAction SilentlyContinue
                                if ($xinputAvailable)
                                {
                                    $inputDevices = xinput list 2>$null
                                    if ($inputDevices)
                                    {
                                        $keyboardLines = $inputDevices | Select-String 'keyboard|Keyboard'
                                        if ($keyboardLines)
                                        {
                                            $keyboardInfo = @()
                                            foreach ($line in $keyboardLines)
                                            {
                                                # Parse xinput output: "↳ Keyboard Name    id=X [slave keyboard (Y)]"
                                                if ($line -match '(?:↳\s+)?([^↳]+?)\s+id=\d+')
                                                {
                                                    $kbName = $matches[1].Trim()
                                                    if ($kbName -and $kbName -notmatch 'Virtual|XTEST' -and $keyboardInfo -notcontains $kbName)
                                                    {
                                                        $keyboardInfo += $kbName
                                                    }
                                                }
                                            }

                                            if ($keyboardInfo.Count -gt 0)
                                            {
                                                $systemInfo.Keyboard = $keyboardInfo -join ', '
                                            }
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve keyboard information: $($_.Exception.Message)"
                            }

                            # Get mouse/pointing device information
                            try
                            {
                                $xinputAvailable = Get-Command xinput -ErrorAction SilentlyContinue
                                if ($xinputAvailable)
                                {
                                    $inputDevices = xinput list 2>$null
                                    if ($inputDevices)
                                    {
                                        $pointerLines = $inputDevices | Select-String 'pointer|Mouse|Touchpad|Trackpad'
                                        if ($pointerLines)
                                        {
                                            $mouseInfo = @()
                                            foreach ($line in $pointerLines)
                                            {
                                                # Parse xinput output
                                                if ($line -match '(?:↳\s+)?([^↳]+?)\s+id=\d+')
                                                {
                                                    $mouseName = $matches[1].Trim()
                                                    if ($mouseName -and $mouseName -notmatch 'Virtual|XTEST|pointer' -and $mouseInfo -notcontains $mouseName)
                                                    {
                                                        $mouseInfo += $mouseName
                                                    }
                                                }
                                            }

                                            if ($mouseInfo.Count -gt 0)
                                            {
                                                $systemInfo.Mouse = $mouseInfo -join ', '
                                            }
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve mouse information: $($_.Exception.Message)"
                            }

                            # Get network adapter information
                            try
                            {
                                $ipAvailable = Get-Command ip -ErrorAction SilentlyContinue
                                if ($ipAvailable)
                                {
                                    # Get network interfaces
                                    $interfaceOutput = ip link show 2>$null
                                    if ($interfaceOutput)
                                    {
                                        $networkInfo = @()
                                        $interfaces = $interfaceOutput | Select-String '^\d+:\s+(\w+):' | ForEach-Object {
                                            if ($_ -match '^\d+:\s+(\w+):')
                                            {
                                                $matches[1]
                                            }
                                        }

                                        foreach ($interface in $interfaces)
                                        {
                                            # Skip virtual, loopback, and docker interfaces
                                            if ($interface -match '^(lo|docker|veth|br-|virbr)')
                                            {
                                                continue
                                            }

                                            # Get interface details
                                            $ethtoolAvailable = Get-Command ethtool -ErrorAction SilentlyContinue
                                            if ($ethtoolAvailable)
                                            {
                                                $speedInfo = ethtool $interface 2>$null | Select-String 'Speed:'
                                                if ($speedInfo -and $speedInfo -match 'Speed:\s*(\d+)Mb/s')
                                                {
                                                    $speed = $matches[1]
                                                    $networkInfo += "$interface ($speed Mbps)"
                                                }
                                                else
                                                {
                                                    $networkInfo += $interface
                                                }
                                            }
                                            else
                                            {
                                                $networkInfo += $interface
                                            }
                                        }

                                        if ($networkInfo.Count -gt 0)
                                        {
                                            $systemInfo.NetworkAdapters = $networkInfo -join ', '
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve network adapter information: $($_.Exception.Message)"
                            }

                            # Get battery information (Linux laptops)
                            try
                            {
                                $batteryPath = '/sys/class/power_supply/BAT0'
                                if (-not (Test-Path $batteryPath))
                                {
                                    # Try BAT1 or other battery names
                                    $batteries = Get-ChildItem -Path '/sys/class/power_supply' -Directory -ErrorAction SilentlyContinue |
                                    Where-Object { $_.Name -like 'BAT*' }
                                    if ($batteries)
                                    {
                                        $batteryPath = $batteries[0].FullName
                                    }
                                }

                                if (Test-Path $batteryPath)
                                {
                                    # Get battery status
                                    $statusFile = Join-Path -Path $batteryPath -ChildPath 'status'
                                    if (Test-Path $statusFile)
                                    {
                                        $status = (Get-Content $statusFile -ErrorAction SilentlyContinue).Trim()
                                        if ($status -eq 'Charging')
                                        {
                                            $systemInfo.BatteryStatus = 'Charging (AC)'
                                        }
                                        elseif ($status -eq 'Discharging')
                                        {
                                            $systemInfo.BatteryStatus = 'Discharging'
                                        }
                                        elseif ($status -eq 'Full')
                                        {
                                            $systemInfo.BatteryStatus = 'Fully Charged'
                                        }
                                        elseif ($status -eq 'Not charging')
                                        {
                                            $systemInfo.BatteryStatus = 'Not Charging'
                                        }
                                        else
                                        {
                                            $systemInfo.BatteryStatus = $status
                                        }
                                    }

                                    # Get battery capacity percentage
                                    $capacityFile = Join-Path -Path $batteryPath -ChildPath 'capacity'
                                    if (Test-Path $capacityFile)
                                    {
                                        $capacity = Get-Content $capacityFile -ErrorAction SilentlyContinue
                                        if ($capacity -and $capacity -match '^\d+$')
                                        {
                                            $systemInfo.BatteryChargePercent = [int]$capacity
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve battery information: $($_.Exception.Message)"
                            }
                        }
                        catch
                        {
                            Write-Warning "Failed to retrieve some Linux system information: $($_.Exception.Message)"
                        }
                    }

                    # Apply privacy filter if requested
                    if ($NoPII)
                    {
                        $systemInfo.PSObject.Properties.Remove('ComputerName')
                        $systemInfo.PSObject.Properties.Remove('HostName')
                        $systemInfo.PSObject.Properties.Remove('Domain')
                        $systemInfo.PSObject.Properties.Remove('IPAddresses')
                        $systemInfo.PSObject.Properties.Remove('Username')
                        $systemInfo.PSObject.Properties.Remove('SerialNumber')
                        $systemInfo.PSObject.Properties.Remove('BIOSVersion')
                        $systemInfo.PSObject.Properties.Remove('TimeZone')
                        $systemInfo.PSObject.Properties.Remove('LastBootTime')
                        $systemInfo.PSObject.Properties.Remove('Uptime')
                    }

                    # Remove null/empty properties if requested
                    if ($NoEmptyProps)
                    {
                        $propertiesToRemove = @()
                        foreach ($prop in $systemInfo.PSObject.Properties)
                        {
                            if ($null -eq $prop.Value -or
                                ($prop.Value -is [string] -and [string]::IsNullOrWhiteSpace($prop.Value)))
                            {
                                $propertiesToRemove += $prop.Name
                            }
                        }

                        foreach ($propName in $propertiesToRemove)
                        {
                            $systemInfo.PSObject.Properties.Remove($propName)
                        }
                    }

                    [void]$results.Add($systemInfo)
                }
                catch
                {
                    $errorMessage = $_.Exception.Message
                    Write-Error "Failed to retrieve system information for $computer`: $errorMessage"
                }
            }
            else
            {
                # Remote computer processing - Windows only via PowerShell Remoting
                if (-not $script:IsWindowsPlatform)
                {
                    Write-Warning "Remote computer queries are only supported on Windows. Skipping remote computer: $computer"
                    continue
                }

                Write-Verbose "Querying remote computer '$computer' for system information"

                $sessionParams = @{
                    ComputerName = $computer
                    ErrorAction = 'Stop'
                }

                if ($Credential)
                {
                    $sessionParams.Credential = $Credential
                }

                $session = $null
                try
                {
                    $session = New-PSSession @sessionParams

                    $remoteResults = Invoke-Command -Session $session -ScriptBlock {
                        $systemInfo = [PSCustomObject]@{
                            Username = $null
                            PSTypeName = 'SystemInfo.Result'
                            ComputerName = $env:COMPUTERNAME
                            HostName = $null
                            Domain = $null
                            IPAddresses = $null
                            OperatingSystem = $null
                            OSArchitecture = $null
                            CPUArchitecture = $null
                            CPUName = $null
                            CPUCores = $null
                            CPULogicalProcessors = $null
                            HyperthreadingEnabled = $null
                            CPUSpeedMHz = $null
                            CPUTemperatureCelsius = $null
                            CPUTemperatureFahrenheit = $null
                            GPUName = $null
                            GPUMemoryGB = $null
                            Monitors = $null
                            Keyboard = $null
                            Mouse = $null
                            NetworkAdapters = $null
                            TotalMemoryGB = $null
                            FreeMemoryGB = $null
                            PageFileTotalGB = $null
                            PageFileUsedGB = $null
                            SystemDriveTotalGB = $null
                            SystemDriveUsedGB = $null
                            SystemDriveFreeGB = $null
                            PhysicalDisks = $null
                            AudioDevices = $null
                            Manufacturer = $null
                            Model = $null
                            ModelFriendlyName = $null
                            SerialNumber = $null
                            BIOSVersion = $null
                            IsVirtualMachine = $null
                            VirtualizationType = $null
                            SystemLoadAverage = $null
                            ProcessCount = $null
                            ThreadCount = $null
                            BatteryStatus = $null
                            BatteryChargePercent = $null
                            BatteryEstimatedRuntime = $null
                            TimeZone = $null
                            LastBootTime = $null
                            Uptime = $null
                        }

                        try
                        {
                            # Get hostname and IP addresses
                            try
                            {
                                $systemInfo.HostName = [System.Net.Dns]::GetHostName()

                                # Get all IP addresses for the local host
                                $hostEntry = [System.Net.Dns]::GetHostEntry([System.Net.Dns]::GetHostName())
                                $ipAddresses = $hostEntry.AddressList | Where-Object {
                                    # Filter out IPv6 link-local addresses
                                    $_.AddressFamily -eq 'InterNetwork' -or
                                    ($_.AddressFamily -eq 'InterNetworkV6' -and -not $_.IsIPv6LinkLocal)
                                } | ForEach-Object { $_.IPAddressToString }

                                $systemInfo.IPAddresses = $ipAddresses
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve hostname or IP addresses: $($_.Exception.Message)"
                            }

                            # Get OS information
                            $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop
                            $systemInfo.OperatingSystem = $os.Caption
                            $systemInfo.OSArchitecture = $os.OSArchitecture
                            $systemInfo.TotalMemoryGB = [Math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
                            $systemInfo.FreeMemoryGB = [Math]::Round($os.FreePhysicalMemory / 1MB, 2)
                            $systemInfo.LastBootTime = $os.LastBootUpTime
                            $systemInfo.Uptime = (Get-Date) - $os.LastBootUpTime
                            $systemInfo.ProcessCount = $os.NumberOfProcesses

                            # Get page file information
                            try
                            {
                                $pageFiles = Get-CimInstance -ClassName Win32_PageFileUsage -ErrorAction Stop
                                if ($pageFiles)
                                {
                                    $totalPageFileMB = ($pageFiles | Measure-Object -Property AllocatedBaseSize -Sum).Sum
                                    $usedPageFileMB = ($pageFiles | Measure-Object -Property CurrentUsage -Sum).Sum
                                    if ($totalPageFileMB -gt 0)
                                    {
                                        $systemInfo.PageFileTotalGB = [Math]::Round($totalPageFileMB / 1KB, 2)
                                        $systemInfo.PageFileUsedGB = [Math]::Round($usedPageFileMB / 1KB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve page file information: $($_.Exception.Message)"
                            }

                            # Get thread count
                            try
                            {
                                $threads = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfProc_Thread -ErrorAction Stop
                                if ($threads)
                                {
                                    $systemInfo.ThreadCount = $threads.Count
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve thread count: $($_.Exception.Message)"
                            }

                            # Get system drive information (Windows remote)
                            try
                            {
                                $systemDrive = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='$($env:SystemDrive)'" -ErrorAction Stop
                                if ($systemDrive)
                                {
                                    $systemInfo.SystemDriveTotalGB = [Math]::Round($systemDrive.Size / 1GB, 2)
                                    $systemInfo.SystemDriveFreeGB = [Math]::Round($systemDrive.FreeSpace / 1GB, 2)
                                    $systemInfo.SystemDriveUsedGB = [Math]::Round(($systemDrive.Size - $systemDrive.FreeSpace) / 1GB, 2)
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve system drive information: $($_.Exception.Message)"
                            }

                            # Get processor information
                            $cpu = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | Select-Object -First 1
                            # Translate CPU architecture code to readable string
                            $cpuArchCode = $cpu.Architecture
                            $cpuArchString = switch ($cpuArchCode)
                            {
                                0 { 'x86' }
                                1 { 'MIPS' }
                                2 { 'Alpha' }
                                3 { 'PowerPC' }
                                5 { 'ARM' }
                                6 { 'ia64' }
                                9 { 'x64' }
                                12 { 'ARM64' }
                                default { "Unknown ($cpuArchCode)" }
                            }
                            $systemInfo.CPUArchitecture = $cpuArchString
                            $systemInfo.CPUName = $cpu.Name.Trim()
                            $systemInfo.CPUCores = $cpu.NumberOfCores
                            $systemInfo.CPULogicalProcessors = $cpu.NumberOfLogicalProcessors

                            # Detect hyperthreading/SMT (if logical processors > cores, HT is enabled)
                            if ($cpu.NumberOfLogicalProcessors -gt $cpu.NumberOfCores)
                            {
                                $systemInfo.HyperthreadingEnabled = $true
                            }
                            else
                            {
                                $systemInfo.HyperthreadingEnabled = $false
                            }

                            $systemInfo.CPUSpeedMHz = $cpu.MaxClockSpeed

                            # Get CPU temperature (Windows - using thermal zone information)
                            try
                            {
                                $thermalZones = Get-CimInstance -ClassName Win32_PerfFormattedData_Counters_ThermalZoneInformation -ErrorAction SilentlyContinue
                                if ($thermalZones)
                                {
                                    # Find the CPU thermal zone (typically named CPUZ)
                                    $cpuZone = $thermalZones | Where-Object { $_.Name -like '*CPUZ*' } | Select-Object -First 1
                                    if ($cpuZone -and $cpuZone.HighPrecisionTemperature)
                                    {
                                        # HighPrecisionTemperature is in tenths of Kelvin, convert to Celsius and Fahrenheit
                                        $tempKelvin = $cpuZone.HighPrecisionTemperature / 10
                                        $tempCelsius = $tempKelvin - 273.15
                                        $systemInfo.CPUTemperatureCelsius = [Math]::Round($tempCelsius, 1)
                                        $systemInfo.CPUTemperatureFahrenheit = [Math]::Round(($tempCelsius * 9 / 5) + 32, 1)
                                        Write-Verbose "CPU temperature: $($systemInfo.CPUTemperatureCelsius)°C / $($systemInfo.CPUTemperatureFahrenheit)°F (from zone: $($cpuZone.Name))"
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve CPU temperature: $($_.Exception.Message)"
                            }

                            # Get computer system information
                            $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
                            $systemInfo.Manufacturer = $cs.Manufacturer
                            $systemInfo.Model = $cs.Model
                            $systemInfo.ModelFriendlyName = $cs.Model

                            # Detect virtualization
                            try
                            {
                                $systemInfo.IsVirtualMachine = $false
                                $systemInfo.VirtualizationType = $null

                                # Check manufacturer and model for VM indicators
                                $vmIndicators = @{
                                    'Microsoft Corporation' = 'Hyper-V'
                                    'VMware' = 'VMware'
                                    'innotek GmbH' = 'VirtualBox'
                                    'QEMU' = 'QEMU/KVM'
                                    'Xen' = 'Xen'
                                    'Parallels' = 'Parallels'
                                }

                                foreach ($indicator in $vmIndicators.Keys)
                                {
                                    if ($cs.Manufacturer -like "*$indicator*" -or $cs.Model -like "*$indicator*")
                                    {
                                        $systemInfo.IsVirtualMachine = $true
                                        $systemInfo.VirtualizationType = $vmIndicators[$indicator]
                                        break
                                    }
                                }

                                # Check BIOS version for additional indicators
                                if (-not $systemInfo.IsVirtualMachine)
                                {
                                    $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction SilentlyContinue
                                    if ($bios)
                                    {
                                        if ($bios.Version -like '*VBOX*')
                                        {
                                            $systemInfo.IsVirtualMachine = $true
                                            $systemInfo.VirtualizationType = 'VirtualBox'
                                        }
                                        elseif ($bios.Version -like '*Hyper-V*')
                                        {
                                            $systemInfo.IsVirtualMachine = $true
                                            $systemInfo.VirtualizationType = 'Hyper-V'
                                        }
                                    }
                                }

                                # Check for specific VM registry keys or services as additional verification
                                if (-not $systemInfo.IsVirtualMachine)
                                {
                                    $vmwareService = Get-Service -Name 'VMTools' -ErrorAction SilentlyContinue
                                    if ($vmwareService)
                                    {
                                        $systemInfo.IsVirtualMachine = $true
                                        $systemInfo.VirtualizationType = 'VMware'
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not detect virtualization: $($_.Exception.Message)"
                            }

                            # Get domain information (null if workgroup)
                            if ($cs.PartOfDomain)
                            {
                                $systemInfo.Domain = $cs.Domain
                            }
                            else
                            {
                                $systemInfo.Domain = $null  # Workgroup
                            }

                            # Get BIOS information
                            $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop
                            $systemInfo.SerialNumber = $bios.SerialNumber
                            $systemInfo.BIOSVersion = $bios.SMBIOSBIOSVersion

                            # Get time zone with DST awareness
                            $timeZone = [System.TimeZoneInfo]::Local
                            $isDst = $timeZone.IsDaylightSavingTime((Get-Date))
                            $offset = $timeZone.GetUtcOffset((Get-Date))
                            $offsetString = if ($offset.TotalHours -ge 0) { "+$($offset.Hours)" } else { "$($offset.Hours)" }

                            if ($isDst -and $timeZone.DaylightName)
                            {
                                $systemInfo.TimeZone = "$($timeZone.DaylightName) (UTC$offsetString)"
                            }
                            else
                            {
                                $systemInfo.TimeZone = "$($timeZone.StandardName) (UTC$offsetString)"
                            }

                            # Get video card information
                            try
                            {
                                $videoCards = Get-CimInstance -ClassName Win32_VideoController -ErrorAction Stop |
                                Where-Object { $_.Status -eq 'OK' -or $null -eq $_.Status }

                                if ($videoCards)
                                {
                                    $gpuInfo = @()
                                    foreach ($gpu in $videoCards)
                                    {
                                        $gpuName = $gpu.Name
                                        $gpuMemoryBytes = $gpu.AdapterRAM

                                        if ($gpuMemoryBytes -and $gpuMemoryBytes -gt 0)
                                        {
                                            $gpuMemoryGB = [Math]::Round($gpuMemoryBytes / 1GB, 2)
                                            $gpuInfo += "$gpuName ($gpuMemoryGB GB)"
                                        }
                                        else
                                        {
                                            $gpuInfo += $gpuName
                                        }
                                    }

                                    $systemInfo.GPUName = $gpuInfo -join ', '

                                    # Set GPUMemoryGB to total memory if available
                                    $totalGpuMemory = ($videoCards | Where-Object { $_.AdapterRAM -gt 0 } |
                                        Measure-Object -Property AdapterRAM -Sum).Sum
                                    if ($totalGpuMemory -gt 0)
                                    {
                                        $systemInfo.GPUMemoryGB = [Math]::Round($totalGpuMemory / 1GB, 2)
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve video card information: $($_.Exception.Message)"
                            }

                            # Get physical disk information (embedded drives only - exclude USB/removable)
                            try
                            {
                                $physicalDisks = Get-CimInstance -ClassName Win32_DiskDrive -ErrorAction Stop |
                                Where-Object {
                                    $_.MediaType -notmatch 'Removable' -and
                                    $_.InterfaceType -notmatch 'USB' -and
                                    $_.Size -gt 0
                                }

                                if ($physicalDisks)
                                {
                                    $diskInfo = @()
                                    foreach ($disk in $physicalDisks)
                                    {
                                        $diskModel = $disk.Model
                                        $diskSizeGB = [Math]::Round($disk.Size / 1GB, 2)
                                        $diskInterface = $disk.InterfaceType

                                        $diskInfo += "$diskModel ($diskSizeGB GB, $diskInterface)"
                                    }

                                    $systemInfo.PhysicalDisks = $diskInfo -join ', '
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve physical disk information: $($_.Exception.Message)"
                            }

                            # Get audio device information
                            try
                            {
                                $audioDevices = Get-CimInstance -ClassName Win32_SoundDevice -ErrorAction Stop |
                                Where-Object { $_.Status -eq 'OK' -or $null -eq $_.Status }

                                if ($audioDevices)
                                {
                                    $audioInfo = @()
                                    foreach ($audio in $audioDevices)
                                    {
                                        $audioInfo += $audio.Name
                                    }

                                    $systemInfo.AudioDevices = $audioInfo -join ', '
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve audio device information: $($_.Exception.Message)"
                            }

                            # Get monitor information
                            try
                            {
                                $monitors = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID -ErrorAction Stop

                                if ($monitors)
                                {
                                    $monitorInfo = @()
                                    foreach ($monitor in $monitors)
                                    {
                                        # Decode manufacturer name
                                        $mfgName = if ($monitor.ManufacturerName)
                                        {
                                            -join ($monitor.ManufacturerName | Where-Object { $_ -ne 0 } | ForEach-Object { [char]$_ })
                                        }
                                        else { 'Unknown' }

                                        # Decode user-friendly name
                                        $monitorName = if ($monitor.UserFriendlyName)
                                        {
                                            -join ($monitor.UserFriendlyName | Where-Object { $_ -ne 0 } | ForEach-Object { [char]$_ })
                                        }
                                        else { 'Unknown Monitor' }

                                        # Get resolution using WMI
                                        try
                                        {
                                            Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue
                                            $screen = [System.Windows.Forms.Screen]::AllScreens | Select-Object -First 1
                                            $resolution = "$($screen.Bounds.Width)x$($screen.Bounds.Height)"
                                        }
                                        catch
                                        {
                                            $resolution = $null
                                        }

                                        if ($resolution)
                                        {
                                            $monitorInfo += "$mfgName $monitorName ($resolution)"
                                        }
                                        else
                                        {
                                            $monitorInfo += "$mfgName $monitorName"
                                        }
                                    }

                                    $systemInfo.Monitors = $monitorInfo -join ', '
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve monitor information: $($_.Exception.Message)"
                            }

                            # Get keyboard information
                            try
                            {
                                $keyboards = Get-CimInstance -ClassName Win32_Keyboard -ErrorAction Stop

                                if ($keyboards)
                                {
                                    $keyboardInfo = @()
                                    foreach ($kb in $keyboards)
                                    {
                                        if ($kb.Description)
                                        {
                                            $keyboardInfo += $kb.Description
                                        }
                                    }

                                    if ($keyboardInfo.Count -gt 0)
                                    {
                                        $systemInfo.Keyboard = $keyboardInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve keyboard information: $($_.Exception.Message)"
                            }

                            # Get mouse information
                            try
                            {
                                $mice = Get-CimInstance -ClassName Win32_PointingDevice -ErrorAction Stop

                                if ($mice)
                                {
                                    $mouseInfo = @()
                                    foreach ($mouse in $mice)
                                    {
                                        if ($mouse.Name)
                                        {
                                            $mouseInfo += $mouse.Name
                                        }
                                    }

                                    if ($mouseInfo.Count -gt 0)
                                    {
                                        $systemInfo.Mouse = $mouseInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve mouse information: $($_.Exception.Message)"
                            }

                            # Get network adapter information (physical adapters only)
                            try
                            {
                                $netAdapters = Get-CimInstance -ClassName Win32_NetworkAdapter -ErrorAction Stop |
                                Where-Object {
                                    $_.PhysicalAdapter -eq $true -and
                                    $_.AdapterType -notmatch 'Tunnel|Loopback|Virtual' -and
                                    $_.Name -notmatch 'Virtual|Bluetooth|TAP|VPN'
                                }

                                if ($netAdapters)
                                {
                                    $networkInfo = @()
                                    foreach ($adapter in $netAdapters)
                                    {
                                        $adapterName = $adapter.Name
                                        $speed = $adapter.Speed

                                        if ($speed -and $speed -gt 0)
                                        {
                                            $speedMbps = [Math]::Round($speed / 1MB, 0)
                                            $networkInfo += "$adapterName ($speedMbps Mbps)"
                                        }
                                        else
                                        {
                                            $networkInfo += $adapterName
                                        }
                                    }

                                    if ($networkInfo.Count -gt 0)
                                    {
                                        $systemInfo.NetworkAdapters = $networkInfo -join ', '
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve network adapter information: $($_.Exception.Message)"
                            }

                            # Get battery information (laptops/portable devices)
                            try
                            {
                                $batteries = Get-CimInstance -ClassName Win32_Battery -ErrorAction Stop
                                if ($batteries)
                                {
                                    $battery = $batteries | Select-Object -First 1

                                    # Battery status codes: 1=Discharging, 2=AC, 3=Fully Charged, 4=Low, 5=Critical
                                    $statusMap = @{
                                        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'
                                    }

                                    $systemInfo.BatteryStatus = $statusMap[[int]$battery.BatteryStatus]
                                    $systemInfo.BatteryChargePercent = $battery.EstimatedChargeRemaining

                                    # Try to get more accurate runtime from WMI BatteryStatus class
                                    try
                                    {
                                        $batteryStatus = Get-CimInstance -Namespace root/WMI -ClassName BatteryStatus -ErrorAction SilentlyContinue | Select-Object -First 1
                                        if ($batteryStatus -and $batteryStatus.Discharging -and $batteryStatus.DischargeRate -gt 0)
                                        {
                                            # Calculate runtime based on remaining capacity and discharge rate
                                            $runtimeHours = [Math]::Round($batteryStatus.RemainingCapacity / $batteryStatus.DischargeRate, 1)
                                            $systemInfo.BatteryEstimatedRuntime = "$runtimeHours hours"
                                        }
                                        elseif ($battery.EstimatedRunTime -and $battery.EstimatedRunTime -lt 71582788)
                                        {
                                            # Fall back to Win32_Battery if WMI method not available or not discharging
                                            $runtimeHours = [Math]::Round($battery.EstimatedRunTime / 60, 1)
                                            $systemInfo.BatteryEstimatedRuntime = "$runtimeHours hours"
                                        }
                                    }
                                    catch
                                    {
                                        Write-Verbose "Could not calculate battery runtime: $($_.Exception.Message)"
                                    }
                                }
                            }
                            catch
                            {
                                Write-Verbose "Could not retrieve battery information: $($_.Exception.Message)"
                            }
                        }
                        catch
                        {
                            Write-Warning "Failed to retrieve some system information: $($_.Exception.Message)"
                        }

                        return $systemInfo
                    }

                    if ($remoteResults)
                    {
                        # Apply privacy filter if requested
                        if ($NoPII)
                        {
                            $remoteResults.PSObject.Properties.Remove('Username')
                            $remoteResults.PSObject.Properties.Remove('ComputerName')
                            $remoteResults.PSObject.Properties.Remove('HostName')
                            $remoteResults.PSObject.Properties.Remove('Domain')
                            $remoteResults.PSObject.Properties.Remove('IPAddresses')
                            $remoteResults.PSObject.Properties.Remove('SerialNumber')
                            $remoteResults.PSObject.Properties.Remove('BIOSVersion')
                            $remoteResults.PSObject.Properties.Remove('TimeZone')
                            $remoteResults.PSObject.Properties.Remove('LastBootTime')
                            $remoteResults.PSObject.Properties.Remove('Uptime')
                        }

                        # Remove null/empty properties if requested
                        if ($NoEmptyProps)
                        {
                            $propertiesToRemove = @()
                            foreach ($prop in $remoteResults.PSObject.Properties)
                            {
                                if ($null -eq $prop.Value -or
                                    ($prop.Value -is [string] -and [string]::IsNullOrWhiteSpace($prop.Value)))
                                {
                                    $propertiesToRemove += $prop.Name
                                }
                            }

                            foreach ($propName in $propertiesToRemove)
                            {
                                $remoteResults.PSObject.Properties.Remove($propName)
                            }
                        }

                        [void]$results.Add($remoteResults)
                    }
                }
                catch
                {
                    $errorMessage = $_.Exception.Message
                    Write-Error "Failed to connect to remote computer $computer`: $errorMessage"
                }
                finally
                {
                    if ($session)
                    {
                        Remove-PSSession -Session $session -ErrorAction SilentlyContinue
                    }
                }
            }
        }
    }

    end
    {
        # Return all collected results
        return $results.ToArray()
    }
}