# Admin not required for data collection - WinRM setup will be skipped if not admin
<#
.SYNOPSIS
    Complete PC asset collection and database update via hybrid proxy/dashboard architecture.
    
.DESCRIPTION
    This script performs comprehensive PC asset management:
    1. Collects system information (hostname, serial, manufacturer, user, etc.)
    2. Determines PC type (Engineer/Shopfloor/Standard) 
    3. For shopfloor PCs: Collects machine assignment and network/comm configurations
    4. Gets Dell warranty information via proxy server (bypasses network restrictions)
    5. Stores all collected data in database via dashboard API
    
.NOTES
    Author: System Administrator
    Date: 2025
    Version: 1.0
    Architecture: PowerShell -> Proxy (warranty) -> Dashboard API (storage)
#>

param(
    [Parameter(Mandatory=$false)]
    [string]$ProxyURL = "http://10.48.130.158/vendor-api-proxy.php",
    
    [Parameter(Mandatory=$false)]
    [string]$DashboardURL = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp",
    
    [Parameter(Mandatory=$false)]
    [switch]$SkipWarranty = $true,  # DEFAULT TO TRUE - Skip warranty lookups
    
    [Parameter(Mandatory=$false)]
    [switch]$TestConnections = $false
)

# =============================================================================
# SSL/TLS Certificate Bypass for HTTPS connections
# =============================================================================
# This allows connections to servers with self-signed or untrusted certificates
try {
    # For PowerShell 5.x - use .NET callback
    if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy').Type) {
        Add-Type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
    }
}
"@
    }
    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
} catch {
    # Silently continue if already set
}

# Set TLS 1.2 as minimum (required for modern HTTPS)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# Import shopfloor configuration functions
. "$PSScriptRoot\Get-ShopfloorConfig.ps1"

# Setup logging
$script:LogFile = "$PSScriptRoot\logs\Update-PC-CompleteAsset-$(Get-Date -Format 'yyyy-MM-dd').log"
$script:LogDir = "$PSScriptRoot\logs"

function Write-Log {
    param(
        [string]$Message,
        [string]$Level = "INFO",
        [string]$ForegroundColor = "White"
    )

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] [$Level] $Message"

    # Write to console
    Write-Host $Message -ForegroundColor $ForegroundColor

    # Write to log file
    try {
        if (-not (Test-Path $script:LogDir)) {
            New-Item -ItemType Directory -Path $script:LogDir -Force | Out-Null
        }
        Add-Content -Path $script:LogFile -Value $logMessage -ErrorAction SilentlyContinue
    } catch {
        # Silently continue if logging fails
    }
}

function Reset-WinRMConfiguration {
    <#
    .SYNOPSIS
        Resets and configures WinRM for HTTP remote management
    .DESCRIPTION
        Removes existing WinRM listeners and configures fresh HTTP listener on port 5985
    #>

    Write-Log "  Resetting WinRM configuration..." -Level "INFO" -ForegroundColor Yellow

    try {
        # First, check and fix network profile if set to Public
        Write-Log "    Checking network profile..." -Level "INFO" -ForegroundColor Gray
        $profiles = Get-NetConnectionProfile

        foreach ($profile in $profiles) {
            Write-Log "      Interface '$($profile.Name)': $($profile.NetworkCategory)" -Level "INFO" -ForegroundColor Gray
        }

        # Fix #1: Set machine network interfaces (192.168.x.x) to Private
        # These shouldn't try to detect domain - they're for machine communication
        Write-Log "    Checking for machine network interfaces..." -Level "INFO" -ForegroundColor Gray
        $adapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
        foreach ($adapter in $adapters) {
            $ipConfig = Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
            if ($ipConfig.IPAddress -match '^192\.168\.') {
                Write-Log "      Found machine network: $($adapter.Name) ($($ipConfig.IPAddress))" -Level "INFO" -ForegroundColor Cyan
                $profile = Get-NetConnectionProfile -InterfaceIndex $adapter.ifIndex -ErrorAction SilentlyContinue
                if ($profile -and $profile.NetworkCategory -ne 'Private') {
                    Write-Log "      Setting machine network '$($adapter.Name)' to Private..." -Level "INFO" -ForegroundColor Gray
                    Set-NetConnectionProfile -InterfaceIndex $adapter.ifIndex -NetworkCategory Private -ErrorAction SilentlyContinue
                    Write-Log "      [OK] Machine network set to Private" -Level "SUCCESS" -ForegroundColor Green
                }
            }
        }

        # Fix #5: Check and repair machine account trust relationship
        Write-Log "    Checking domain trust relationship..." -Level "INFO" -ForegroundColor Gray
        $secureChannel = Test-ComputerSecureChannel -ErrorAction SilentlyContinue
        if ($secureChannel) {
            Write-Log "      [OK] Domain trust relationship is healthy" -Level "SUCCESS" -ForegroundColor Green
        } else {
            Write-Log "      [WARN] Domain trust may be broken, attempting repair..." -Level "WARN" -ForegroundColor Yellow
            try {
                # This requires domain admin creds or machine account to still be somewhat valid
                $repairResult = Test-ComputerSecureChannel -Repair -ErrorAction SilentlyContinue
                if ($repairResult) {
                    Write-Log "      [OK] Domain trust repaired successfully" -Level "SUCCESS" -ForegroundColor Green
                } else {
                    Write-Log "      [WARN] Could not auto-repair trust - may need manual rejoin to domain" -Level "WARN" -ForegroundColor Yellow
                }
            } catch {
                Write-Log "      [WARN] Trust repair failed: $($_.Exception.Message)" -Level "WARN" -ForegroundColor Yellow
            }
        }

        # Now handle remaining Public profiles on corporate network
        $publicProfiles = Get-NetConnectionProfile | Where-Object { $_.NetworkCategory -eq 'Public' }

        if ($publicProfiles) {
            Write-Log "    Found Public network profile(s), attempting to fix..." -Level "INFO" -ForegroundColor Yellow

            # Restart NLA service to re-detect domain
            Write-Log "      Restarting NLA service to detect domain..." -Level "INFO" -ForegroundColor Gray
            Restart-Service NlaSvc -Force -ErrorAction SilentlyContinue
            Start-Sleep -Seconds 5

            # Re-check profiles after NLA restart
            $publicProfiles = Get-NetConnectionProfile | Where-Object { $_.NetworkCategory -eq 'Public' }

            foreach ($profile in $publicProfiles) {
                # Check if this is a corporate network (not 192.168.x.x)
                $adapter = Get-NetAdapter -InterfaceIndex $profile.InterfaceIndex -ErrorAction SilentlyContinue
                $ipConfig = Get-NetIPAddress -InterfaceIndex $profile.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue

                if ($ipConfig.IPAddress -notmatch '^192\.168\.') {
                    Write-Log "      Corporate interface '$($profile.Name)' still Public after NLA restart" -Level "WARN" -ForegroundColor Yellow
                }

                # Set to Private as fallback (can't manually set Domain)
                Write-Log "      Setting '$($profile.Name)' to Private..." -Level "INFO" -ForegroundColor Gray
                Set-NetConnectionProfile -InterfaceIndex $profile.InterfaceIndex -NetworkCategory Private -ErrorAction SilentlyContinue
                Write-Log "      [OK] '$($profile.Name)' set to Private" -Level "SUCCESS" -ForegroundColor Green
            }
        } else {
            Write-Log "    [OK] All network profiles are Private/Domain" -Level "SUCCESS" -ForegroundColor Green
        }

        # Stop WinRM service first
        Write-Log "    Stopping WinRM service..." -Level "INFO" -ForegroundColor Gray
        Stop-Service WinRM -Force -ErrorAction SilentlyContinue
        Start-Sleep -Seconds 2
        Write-Log "    WinRM service stopped" -Level "INFO" -ForegroundColor Gray

        # Delete all existing listeners
        Write-Log "    Removing existing WinRM listeners..." -Level "INFO" -ForegroundColor Gray
        Remove-Item -Path WSMan:\localhost\Listener\* -Recurse -Force -ErrorAction SilentlyContinue
        Write-Log "    Existing listeners removed" -Level "INFO" -ForegroundColor Gray

        # Start WinRM service
        Write-Log "    Starting WinRM service..." -Level "INFO" -ForegroundColor Gray
        Start-Service WinRM -ErrorAction Stop
        Set-Service WinRM -StartupType Automatic
        Write-Log "    WinRM service started and set to Automatic" -Level "INFO" -ForegroundColor Gray

        # Run quick config to set defaults
        Write-Log "    Running WinRM quickconfig..." -Level "INFO" -ForegroundColor Gray
        $quickConfigResult = winrm quickconfig -quiet 2>&1
        Write-Log "    WinRM quickconfig completed" -Level "INFO" -ForegroundColor Gray

        # Create HTTP listener on all addresses
        Write-Log "    Creating HTTP listener on port 5985..." -Level "INFO" -ForegroundColor Gray
        $existingHttp = Get-ChildItem WSMan:\localhost\Listener -ErrorAction SilentlyContinue |
            Where-Object { $_.Keys -contains "Transport=HTTP" }

        if (-not $existingHttp) {
            New-Item -Path WSMan:\localhost\Listener -Transport HTTP -Address * -Force | Out-Null
            Write-Log "    HTTP listener created" -Level "INFO" -ForegroundColor Gray
        } else {
            Write-Log "    HTTP listener already exists" -Level "INFO" -ForegroundColor Gray
        }

        # Configure WinRM settings
        Write-Log "    Configuring WinRM authentication settings..." -Level "INFO" -ForegroundColor Gray
        Set-Item WSMan:\localhost\Service\Auth\Basic -Value $false
        Set-Item WSMan:\localhost\Service\Auth\Negotiate -Value $true
        Set-Item WSMan:\localhost\Service\Auth\Kerberos -Value $true
        Set-Item WSMan:\localhost\Service\Auth\CredSSP -Value $false
        Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $false
        Write-Log "    Auth: Basic=false, Negotiate=true, Kerberos=true, CredSSP=false" -Level "INFO" -ForegroundColor Gray

        # Set MaxMemoryPerShellMB for better performance
        Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB -Value 1024 -ErrorAction SilentlyContinue
        Write-Log "    MaxMemoryPerShellMB set to 1024" -Level "INFO" -ForegroundColor Gray

        # Enable LocalAccountTokenFilterPolicy for remote admin access
        Write-Log "    Enabling LocalAccountTokenFilterPolicy..." -Level "INFO" -ForegroundColor Gray
        $regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
        Set-ItemProperty -Path $regPath -Name "LocalAccountTokenFilterPolicy" -Value 1 -Type DWord -Force -ErrorAction SilentlyContinue
        Write-Log "    LocalAccountTokenFilterPolicy enabled" -Level "INFO" -ForegroundColor Gray

        # Grant WinRM access to Remote Management Users and Administrators
        Write-Log "    Configuring WinRM security descriptor..." -Level "INFO" -ForegroundColor Gray
        try {
            # Get current SDDL
            $sddl = (Get-Item WSMan:\localhost\Service\RootSDDL).Value
            Write-Log "    Current SDDL: $sddl" -Level "INFO" -ForegroundColor Gray

            # Enable PSRemoting to set proper permissions
            Enable-PSRemoting -Force -SkipNetworkProfileCheck 2>&1 | Out-Null
            Write-Log "    PSRemoting enabled" -Level "INFO" -ForegroundColor Gray
        } catch {
            Write-Log "    Note: Could not update SDDL directly, using Enable-PSRemoting" -Level "INFO" -ForegroundColor Gray
        }

        # Restart WinRM to apply all changes
        Write-Log "    Restarting WinRM service to apply changes..." -Level "INFO" -ForegroundColor Gray
        Restart-Service WinRM -Force
        Start-Sleep -Seconds 2
        Write-Log "    WinRM service restarted" -Level "INFO" -ForegroundColor Gray

        # Configure firewall rule
        Write-Log "    Configuring firewall rule..." -Level "INFO" -ForegroundColor Gray
        $ruleName = "Windows Remote Management (HTTP-In)"
        $existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue

        if (-not $existingRule) {
            New-NetFirewallRule -DisplayName $ruleName `
                -Name "WINRM-HTTP-In-TCP" `
                -Profile Domain,Private `
                -LocalPort 5985 `
                -Protocol TCP `
                -Direction Inbound `
                -Action Allow `
                -Enabled True | Out-Null
            Write-Log "    Firewall rule '$ruleName' created" -Level "INFO" -ForegroundColor Gray
        } else {
            Enable-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
            Write-Log "    Firewall rule '$ruleName' enabled" -Level "INFO" -ForegroundColor Gray
        }

        # Verify listener is working
        Write-Log "    Verifying WinRM listener..." -Level "INFO" -ForegroundColor Gray
        $listeners = winrm enumerate winrm/config/listener 2>&1

        if ($listeners -match "Transport = HTTP" -and $listeners -match "Port = 5985") {
            Write-Log "    [OK] WinRM HTTP listener configured on port 5985" -Level "SUCCESS" -ForegroundColor Green

            # Show listener details
            $portCheck = netstat -an | Select-String ":5985.*LISTENING"
            if ($portCheck) {
                Write-Log "    [OK] Port 5985 is listening" -Level "SUCCESS" -ForegroundColor Green
            }

            return $true
        } else {
            Write-Log "    [WARN] WinRM listener may not be configured correctly" -Level "WARN" -ForegroundColor Yellow
            return $false
        }
    }
    catch {
        Write-Log "    [FAIL] Error configuring WinRM: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red
        return $false
    }
}

function Add-WinRMAdminGroup {
    <#
    .SYNOPSIS
        Adds the WinRM management security group to local Administrators and Remote Management Users
    .DESCRIPTION
        Adds logon\g03078610 to both local Administrators and Remote Management Users groups
        to enable WinRM remote management
    #>
    param(
        [string]$GroupToAdd = "logon\g03078610"
    )

    Write-Log "  Configuring WinRM access groups..." -Level "INFO" -ForegroundColor Yellow
    Write-Log "    Target group: $GroupToAdd" -Level "INFO" -ForegroundColor Gray

    $overallSuccess = $true

    # Add to Administrators group
    try {
        Write-Log "    Checking local Administrators group..." -Level "INFO" -ForegroundColor Gray
        $adminGroup = [ADSI]"WinNT://./Administrators,group"
        $members = @($adminGroup.Invoke("Members")) | ForEach-Object {
            $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
        }

        Write-Log "    Current Administrators members: $($members -join ', ')" -Level "INFO" -ForegroundColor Gray

        $groupName = $GroupToAdd.Split('\')[-1]

        if ($members -contains $groupName) {
            Write-Log "    [OK] $GroupToAdd is already in Administrators" -Level "SUCCESS" -ForegroundColor Green
        } else {
            Write-Log "    Adding $GroupToAdd to Administrators..." -Level "INFO" -ForegroundColor Yellow
            $result = net localgroup Administrators $GroupToAdd /add 2>&1

            if ($LASTEXITCODE -eq 0 -or $result -match "already a member") {
                Write-Log "    [OK] Added $GroupToAdd to Administrators" -Level "SUCCESS" -ForegroundColor Green
            } else {
                Write-Log "    [FAIL] Failed to add to Administrators: $result" -Level "ERROR" -ForegroundColor Red
                $overallSuccess = $false
            }
        }
    }
    catch {
        Write-Log "    [FAIL] Error with Administrators group: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red
        $overallSuccess = $false
    }

    # Add to Remote Management Users group
    try {
        Write-Log "    Checking Remote Management Users group..." -Level "INFO" -ForegroundColor Gray
        $rmGroup = [ADSI]"WinNT://./Remote Management Users,group"
        $rmMembers = @($rmGroup.Invoke("Members")) | ForEach-Object {
            $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
        }

        Write-Log "    Current Remote Management Users members: $($rmMembers -join ', ')" -Level "INFO" -ForegroundColor Gray

        $groupName = $GroupToAdd.Split('\')[-1]

        if ($rmMembers -contains $groupName) {
            Write-Log "    [OK] $GroupToAdd is already in Remote Management Users" -Level "SUCCESS" -ForegroundColor Green
        } else {
            Write-Log "    Adding $GroupToAdd to Remote Management Users..." -Level "INFO" -ForegroundColor Yellow
            $result = net localgroup "Remote Management Users" $GroupToAdd /add 2>&1

            if ($LASTEXITCODE -eq 0 -or $result -match "already a member") {
                Write-Log "    [OK] Added $GroupToAdd to Remote Management Users" -Level "SUCCESS" -ForegroundColor Green
            } else {
                Write-Log "    [FAIL] Failed to add to Remote Management Users: $result" -Level "ERROR" -ForegroundColor Red
                $overallSuccess = $false
            }
        }
    }
    catch {
        Write-Log "    [FAIL] Error with Remote Management Users group: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red
        $overallSuccess = $false
    }

    return $overallSuccess
}

function Get-DashboardURL {
    param([string]$ProvidedURL)
    
    if (-not [string]::IsNullOrEmpty($ProvidedURL)) {
        Write-Host "    Using provided URL: $ProvidedURL" -ForegroundColor Gray
        return $ProvidedURL
    }
    
    # Check environment variable
    $envURL = [Environment]::GetEnvironmentVariable("ASSET_DASHBOARD_URL", "User")
    if (-not [string]::IsNullOrEmpty($envURL)) {
        Write-Host "    Using environment variable URL: $envURL" -ForegroundColor Gray
        return $envURL
    }
    
    # Check for config file
    $configPath = "$PSScriptRoot\dashboard-config.json"
    if (Test-Path $configPath) {
        try {
            $config = Get-Content $configPath | ConvertFrom-Json
            if ($config.DashboardURL) {
                return $config.DashboardURL
            }
        }
        catch {
            Write-Verbose "Could not read dashboard config file: $_"
        }
    }
    
    # Auto-discovery with verbose logging
    Write-Host "    Starting dashboard URL auto-discovery..." -ForegroundColor Yellow
    $candidates = @(
        "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp",
        "http://192.168.122.151:8080/api.asp",
        "http://localhost:8080/api.asp",
        "http://10.48.130.197/dashboard-v2/api.php",
        "http://localhost/test/dashboard/api.php",
        "http://127.0.0.1/test/dashboard/api.php",
        "http://10.48.130.197/api.php",
        "http://localhost/api.php",
        "http://127.0.0.1/api.php"
    )
    
    foreach ($url in $candidates) {
        try {
            Write-Host "      Testing: $url" -ForegroundColor Gray
            $testResponse = Invoke-RestMethod -Uri "$url?action=getDashboardData" -Method Get -TimeoutSec 5 -ErrorAction Stop
            if ($testResponse.success) {
                Write-Host "      [SUCCESS] Dashboard found at: $url" -ForegroundColor Green
                return $url
            } else {
                Write-Host "      [FAIL] Dashboard responded but not successful" -ForegroundColor Yellow
            }
        }
        catch {
            Write-Host "      [FAIL] Cannot reach: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
    
    # If auto-discovery fails, default to the expected URL
    $defaultUrl = "https://tsgwp00525.rd.ds.ge.com/shopdb/api.asp"
    Write-Host "    [FALLBACK] Using default URL: $defaultUrl" -ForegroundColor Yellow
    return $defaultUrl
}

function Test-ProxyConnection {
    param([string]$ProxyURL)
    
    try {
        Write-Host "Testing proxy connection..." -ForegroundColor Yellow
        
        $uri = "$ProxyURL" + "?vendor=dell&action=test-config"
        $response = Invoke-RestMethod -Uri $uri -Method Get -TimeoutSec 15
        
        if ($response.success) {
            Write-Host "[OK] Proxy server accessible and Dell API configured" -ForegroundColor Green
            return $true
        } else {
            Write-Host "[FAIL] Proxy issue: $($response.error)" -ForegroundColor Red
            return $false
        }
    }
    catch {
        Write-Host "[FAIL] Cannot reach proxy: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

function Test-DashboardConnection {
    param([string]$DashboardURL)
    
    try {
        Write-Host "Testing dashboard connection..." -ForegroundColor Yellow
        
        $response = Invoke-RestMethod -Uri "$DashboardURL?action=getDashboardData" -Method Get -TimeoutSec 10
        if ($response.success) {
            Write-Host "[OK] Dashboard API accessible" -ForegroundColor Green
            return $true
        } else {
            Write-Host "[FAIL] Dashboard not responding properly" -ForegroundColor Red
            return $false
        }
    }
    catch {
        Write-Host "[FAIL] Cannot reach dashboard: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

function Test-AppsFolder {
    return Test-Path "C:\Apps"
}

function Test-VDriveAccess {
    try {
        $vDrive = Get-PSDrive -Name V -ErrorAction SilentlyContinue
        if ($vDrive) {
            $null = Get-ChildItem V:\ -ErrorAction Stop | Select-Object -First 1
            return $true
        }
        return $false
    }
    catch {
        return $false
    }
}

function Test-WindowsLTSC {
    try {
        $os = Get-CimInstance -ClassName CIM_OperatingSystem
        $osCaption = $os.Caption
        
        if ($osCaption -match "LTSC|LTSB") {
            return $true
        }
        
        $productName = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName -ErrorAction SilentlyContinue).ProductName
        if ($productName -match "LTSC|LTSB") {
            return $true
        }
        
        return $false
    }
    catch {
        return $false
    }
}

function Get-TrackedApplications {
    try {
        $csvPath = "$PSScriptRoot\applications.csv"
        if (-not (Test-Path $csvPath)) {
            Write-Warning "Applications CSV not found at $csvPath - using fallback patterns"
            return @(
                @{ app_name = "UDC"; search_patterns = "Universal Data Collection"; enabled = $true }
                @{ app_name = "PPDCS"; search_patterns = "PPDCS"; enabled = $true }
                @{ app_name = "Oracle"; search_patterns = "Oracle.*Database"; enabled = $true }
                @{ app_name = "Tanium"; search_patterns = "Tanium"; enabled = $true }
                @{ app_name = "eDNC"; search_patterns = "eDNC"; enabled = $true }
            )
        }
        
        $trackedApps = @()
        $csvContent = Import-Csv -Path $csvPath
        
        foreach ($row in $csvContent) {
            if ($row.enabled -eq "1") {
                $trackedApps += @{
                    app_name = $row.app_name
                    app_id = $row.app_id
                    search_patterns = $row.search_patterns
                    description = $row.description
                }
            }
        }
        
        Write-Host "    Loaded $($trackedApps.Count) enabled applications from CSV" -ForegroundColor Cyan
        return $trackedApps
    }
    catch {
        Write-Warning "Failed to read applications CSV: $($_.Exception.Message) - using fallback patterns"
        return @(
            @{ app_name = "UDC"; app_id = "2"; search_patterns = "Universal Data Collection"; enabled = $true }
            @{ app_name = "PPDCS"; app_id = "4"; search_patterns = "PPDCS"; enabled = $true }
            @{ app_name = "Oracle"; app_id = "7"; search_patterns = "Oracle.*Database"; enabled = $true }
            @{ app_name = "Tanium"; app_id = "30"; search_patterns = "Tanium"; enabled = $true }
            @{ app_name = "eDNC"; app_id = ""; search_patterns = "eDNC"; enabled = $true }
        )
    }
}

function Get-GEMachineNumber {
    param([string]$Hostname)
    
    # Check GE Aircraft Engines registry paths for MachineNo
    $gePaths = @(
        "HKLM:\Software\GE Aircraft Engines\DNC\General",
        "HKLM:\Software\WOW6432Node\GE Aircraft Engines\DNC\General"
    )
    
    foreach ($gePath in $gePaths) {
        if (Test-Path $gePath) {
            try {
                $machineNo = Get-ItemProperty -Path $gePath -Name "MachineNo" -ErrorAction Stop
                if ($machineNo.MachineNo) {
                    return $machineNo.MachineNo
                }
            }
            catch {
                # Continue to next path
            }
        }
    }
    
    # Check if hostname indicates a specific GE machine
    if ($Hostname -match '[HG](\d{3})') {
        $machineNum = $Matches[1]
        return "M$machineNum"
    }
    
    return $null
}

function Get-PCType {
    param(
        [bool]$HasAppsFolder,
        [bool]$HasVDriveAccess
    )

    # Check if on logon.ds.ge.com domain (Shopfloor PCs)
    try {
        $domain = (Get-WmiObject Win32_ComputerSystem).Domain
        Write-Host "    Domain detected: $domain" -ForegroundColor Gray
        if ($domain -eq "logon.ds.ge.com") {
            Write-Host "    [OK] Shopfloor domain detected" -ForegroundColor Green

            # Check for specific machine type applications
            $installedApps = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
                                              "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
                             Where-Object { $_.DisplayName } |
                             Select-Object -ExpandProperty DisplayName

            # ================================================================
            # PC Type Detection based on installed software
            # Priority: CMM > Wax Trace > Keyence > EAS1000 > Genspect > Heat Treat > Shopfloor
            # ================================================================

            # CMM Detection: PC-DMIS, goCMM, DODA
            $hasPcDmis = $installedApps -match "PC-DMIS|PCDMIS"
            $hasGoCMM = $installedApps -match "^goCMM"
            $hasDODA = $installedApps -match "Dovetail Digital Analysis|DODA"

            # Also check common PC-DMIS installation paths
            if (-not $hasPcDmis) {
                $pcDmisPaths = @(
                    "C:\Program Files\Hexagon\PC-DMIS*",
                    "C:\Program Files (x86)\Hexagon\PC-DMIS*",
                    "C:\Program Files\WAI\PC-DMIS*",
                    "C:\Program Files (x86)\WAI\PC-DMIS*",
                    "C:\ProgramData\Hexagon\PC-DMIS*"
                )
                foreach ($dmisPath in $pcDmisPaths) {
                    if (Test-Path $dmisPath) { $hasPcDmis = $true; break }
                }
            }

            if ($hasPcDmis -or $hasGoCMM -or $hasDODA) {
                $detected = @()
                if ($hasPcDmis) { $detected += "PC-DMIS" }
                if ($hasGoCMM) { $detected += "goCMM" }
                if ($hasDODA) { $detected += "DODA" }
                Write-Host "    [OK] CMM software detected ($($detected -join ', ')) - CMM PC" -ForegroundColor Cyan
                return "CMM"
            }

            # Wax Trace Detection: FormTracePak, FormStatusMonitor
            $hasFormTracePak = $installedApps -match "FormTracePak|Formtracepak|Form Trace|FormTrace"
            $hasFormStatusMonitor = $installedApps -match "FormStatusMonitor"

            # Check file path fallback for FormTracePak
            if (-not $hasFormTracePak) {
                $ftPaths = @("C:\Program Files\MitutoyoApp*", "C:\Program Files (x86)\MitutoyoApp*")
                foreach ($ftPath in $ftPaths) {
                    if (Test-Path $ftPath) { $hasFormTracePak = $true; break }
                }
            }

            if ($hasFormTracePak -or $hasFormStatusMonitor) {
                $detected = @()
                if ($hasFormTracePak) { $detected += "FormTracePak" }
                if ($hasFormStatusMonitor) { $detected += "FormStatusMonitor" }
                Write-Host "    [OK] Wax Trace software detected ($($detected -join ', ')) - Wax Trace PC" -ForegroundColor Cyan
                return "Wax Trace"
            }

            # Keyence Detection: VR Series, Keyence VR USB Driver
            $hasKeyence = $installedApps -match "VR-3000|VR-5000|VR-6000|KEYENCE VR"

            if ($hasKeyence) {
                Write-Host "    [OK] Keyence VR Series detected - Keyence PC" -ForegroundColor Cyan
                return "Keyence"
            }

            # EAS1000 Detection: GageCal, NI Software (National Instruments)
            $hasGageCal = $installedApps -match "^GageCal"
            $hasNISoftware = $installedApps -match "^NI-|National Instruments|NI System|NI Measurement|NI LabVIEW"

            if ($hasGageCal -or $hasNISoftware) {
                $detected = @()
                if ($hasGageCal) { $detected += "GageCal" }
                if ($hasNISoftware) { $detected += "NI Software" }
                Write-Host "    [OK] EAS1000 software detected ($($detected -join ', ')) - EAS1000 PC" -ForegroundColor Cyan
                return "EAS1000"
            }

            # Genspect detection (could be Keyence or EAS1000 - default to Measuring)
            $hasGenspect = $installedApps -match "^Genspect"
            if ($hasGenspect) {
                Write-Host "    [OK] Genspect detected - Measuring Tool PC" -ForegroundColor Cyan
                return "Genspect"
            }

            # Heat Treat Detection: HeatTreat application
            $hasHeatTreat = $installedApps -match "^HeatTreat"
            if ($hasHeatTreat) {
                Write-Host "    [OK] HeatTreat software detected - Heat Treat PC" -ForegroundColor Cyan
                return "Heat Treat"
            }

            # Part Marker Detection: Part marking software
            $hasPartMarker = $installedApps -match "Part\s*Mark|PartMark|Telesis|MECCO|Pryor|Gravotech|SIC Marking"
            if ($hasPartMarker) {
                Write-Host "    [OK] Part Marker software detected - Part Marker PC" -ForegroundColor Cyan
                return "Part Marker"
            }

            return "Shopfloor"
        }
    } catch {
        Write-Host "    [WARN] Could not detect domain: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    if ($HasAppsFolder -and $HasVDriveAccess) {
        return "Engineer"
    }
    else {
        return "Standard"
    }
}

function Collect-SystemInfo {
    Write-Host "Collecting comprehensive system information..." -ForegroundColor Green
    
    $systemInfo = @{}
    
    # Basic system info
    $systemInfo.Hostname = $env:COMPUTERNAME
    
    try {
        $computerSystem = Get-CimInstance -Class CIM_ComputerSystem -ErrorAction Stop
        $bios = Get-CimInstance -Class CIM_BIOSElement -ErrorAction Stop
        $os = Get-CimInstance -Class CIM_OperatingSystem -ErrorAction Stop
        
        $systemInfo.Manufacturer = $computerSystem.Manufacturer
        $systemInfo.Model = $computerSystem.Model
        $systemInfo.SerialNumber = $bios.SerialNumber
        $systemInfo.ServiceTag = $bios.SerialNumber  # Often same as serial for Dell
        $systemInfo.TotalPhysicalMemory = [Math]::Round($computerSystem.TotalPhysicalMemory / 1GB, 2)
        $systemInfo.DomainRole = $computerSystem.DomainRole
        
        # OS Information
        $systemInfo.OSVersion = $os.Caption
        $systemInfo.LastBootUpTime = $os.LastBootUpTime
        $systemInfo.CurrentTimeZone = (Get-TimeZone).Id
        
        # Logged-in user
        if ($computerSystem.UserName) {
            $systemInfo.LoggedInUser = $computerSystem.UserName.Split('\')[-1]
        } else {
            $systemInfo.LoggedInUser = "No user logged in"
        }
        
    }
    catch {
        Write-Warning "Failed to retrieve WMI information: $_"
        $systemInfo.Manufacturer = "Unknown"
        $systemInfo.Model = "Unknown"
        $systemInfo.SerialNumber = "Unknown"
        $systemInfo.ServiceTag = "Unknown"
        $systemInfo.LoggedInUser = "Unknown"
        $systemInfo.OSVersion = "Unknown"
    }
    
    # PC Type determination
    $hasApps = Test-AppsFolder
    $hasVDrive = Test-VDriveAccess

    $systemInfo.PCType = Get-PCType -HasAppsFolder $hasApps -HasVDriveAccess $hasVDrive
    
    # Add application detection for shopfloor PCs
    if ($systemInfo.PCType -eq "Shopfloor") {
        Write-Host "  Detecting shopfloor applications (UDC/CLM)..." -ForegroundColor Yellow
        $systemInfo.InstalledApplications = Get-InstalledApplications
    } else {
        Write-Host "  Skipping application detection (PC Type: $($systemInfo.PCType))" -ForegroundColor Gray
        $systemInfo.InstalledApplications = @()
    }
    
    # GE Machine Number
    $systemInfo.MachineNo = Get-GEMachineNumber -Hostname $systemInfo.Hostname
    
    # Collect installed applications for logging and database tracking
    Write-Host "  Collecting installed applications..." -ForegroundColor Yellow
    try {
        $installedApps = @()
        $installedApps += Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {$_.DisplayName}
        $installedApps += Get-ItemProperty HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {$_.DisplayName}
        
        $filteredApps = $installedApps | Select-Object DisplayName, DisplayVersion | Sort-Object DisplayName -Unique
        
        Write-Host "    Found $($filteredApps.Count) installed applications:" -ForegroundColor Gray
        foreach ($app in $filteredApps) {
            $version = if ($app.DisplayVersion) { " (v$($app.DisplayVersion))" } else { "" }
            Write-Host "      - $($app.DisplayName)$version" -ForegroundColor Gray
        }
        
        # Prepare application data for database tracking (specific apps only)
        $trackedApps = @()
        $csvTrackedApps = Get-TrackedApplications

        # Track which appids we've already added to avoid duplicates
        $seenAppIds = @{}

        foreach ($app in $filteredApps) {
            $appName = $app.DisplayName
            $appVersion = $app.DisplayVersion

            # Check against CSV-defined applications
            foreach ($csvApp in $csvTrackedApps) {
                # Skip if no app_id defined in CSV
                if (-not $csvApp.app_id -or $csvApp.app_id -eq "") { continue }

                if ($appName -match $csvApp.search_patterns) {
                    $appId = [int]$csvApp.app_id

                    # Skip if we've already tracked this appid
                    if ($seenAppIds.ContainsKey($appId)) {
                        Write-Host "      Skipping duplicate: $($csvApp.app_name) (ID:$appId) = $appName" -ForegroundColor DarkGray
                        break
                    }

                    $seenAppIds[$appId] = $true
                    $trackedApps += @{
                        appid = $appId                   # Database appid
                        appname = $csvApp.app_name       # Short name from CSV (e.g., "Tanium")
                        version = $appVersion            # Detected version
                        displayname = $appName           # Full registry name for reference
                    }
                    Write-Host "      Matched: $($csvApp.app_name) (ID:$appId) = $appName v$appVersion" -ForegroundColor Cyan
                    break  # Found a match, no need to check other patterns
                }
            }
        }

        $systemInfo.TrackedApplications = $trackedApps
        Write-Host "    Found $($trackedApps.Count) tracked applications for database" -ForegroundColor Cyan
    }
    catch {
        Write-Host "    [WARN] Failed to collect installed applications: $($_.Exception.Message)" -ForegroundColor Yellow
        $systemInfo.TrackedApplications = @()
    }

    # Collect running processes (for log analysis)
    Write-Host "  Running processes:" -ForegroundColor Yellow
    try {
        $processes = Get-Process | Select-Object -ExpandProperty Name | Sort-Object -Unique
        Write-Host "    $($processes -join ', ')" -ForegroundColor Gray
    }
    catch {
        Write-Host "    [WARN] Failed to collect running processes: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    # Display collected info
    Write-Host "  System Details:" -ForegroundColor Cyan
    Write-Host "    Hostname: $($systemInfo.Hostname)"
    Write-Host "    Manufacturer: $($systemInfo.Manufacturer)"
    Write-Host "    Model: $($systemInfo.Model)"
    Write-Host "    Serial: $($systemInfo.SerialNumber)"
    Write-Host "    PC Type: $($systemInfo.PCType)"
    Write-Host "    User: $($systemInfo.LoggedInUser)"
    if ($systemInfo.MachineNo) {
        Write-Host "    Machine No: $($systemInfo.MachineNo)"
    }
    Write-Host "    Memory: $($systemInfo.TotalPhysicalMemory) GB"
    Write-Host "    OS: $($systemInfo.OSVersion)"
    
    return $systemInfo
}

function Collect-ShopfloorInfo {
    param([hashtable]$SystemInfo)
    
    if ($SystemInfo.PCType -ne "Shopfloor") {
        return $null
    }
    
    Write-Host ""
    Write-Host "Collecting shopfloor-specific configurations..." -ForegroundColor Yellow
    
    try {
        $shopfloorConfigs = Get-ShopfloorConfigurations
        
        Write-Host "  Shopfloor Configuration Summary:" -ForegroundColor Cyan
        Write-Host "    Network Interfaces: $($shopfloorConfigs.NetworkInterfaces.Count)"
        Write-Host "    Communication Configs: $($shopfloorConfigs.CommConfigs.Count)"
        Write-Host "    DNC Config: $(if ($shopfloorConfigs.DNCConfig) { 'Yes' } else { 'No' })"
        
        return $shopfloorConfigs
    }
    catch {
        Write-Warning "Failed to collect shopfloor configurations: $_"
        return $null
    }
}

function Get-DefaultPrinterFQDN {
    try {
        Write-Host "  Collecting default printer information..." -ForegroundColor Yellow
        $defaultPrinter = Get-WmiObject -Query "SELECT * FROM Win32_Printer WHERE Default=$true" -ErrorAction Stop

        if ($defaultPrinter -and $defaultPrinter.PortName) {
            $portName = $defaultPrinter.PortName
            Write-Host "    Default Printer: $($defaultPrinter.Name)" -ForegroundColor Gray
            Write-Host "    Port Name: $portName" -ForegroundColor Gray

            # Check if this is a network printer (not a local/virtual printer)
            # Network printer ports are: IP addresses, FQDNs, or UNC paths
            # Skip local printer ports: USB*, LPT*, COM*, PORTPROMPT:, FILE:, NUL:, etc.
            $localPorts = @('USB', 'LPT', 'COM', 'PORTPROMPT:', 'FILE:', 'NUL:', 'XPS', 'PDF')
            $isLocalPrinter = $false

            foreach ($localPort in $localPorts) {
                if ($portName -like "$localPort*") {
                    $isLocalPrinter = $true
                    break
                }
            }

            if ($isLocalPrinter) {
                Write-Host "    [SKIP] Local/virtual printer detected (port: $portName) - not sending to database" -ForegroundColor Yellow
                return $null
            }

            # This appears to be a network printer (IP, FQDN, or UNC path)
            Write-Host "    [OK] Network printer detected - will send to database" -ForegroundColor Green
            return $portName
        } else {
            Write-Host "    No default printer found or no port available" -ForegroundColor Yellow
            return $null
        }
    }
    catch {
        Write-Warning "Failed to get default printer information: $_"
        return $null
    }
}

function Send-PrinterMappingToDashboard {
    param(
        [string]$Hostname,
        [string]$PrinterFQDN,
        [string]$DashboardURL
    )
    
    if ([string]::IsNullOrEmpty($PrinterFQDN)) {
        Write-Host "  No printer FQDN to send - skipping printer mapping" -ForegroundColor Gray
        return $true
    }
    
    try {
        Write-Host "  Sending printer mapping to dashboard..." -ForegroundColor Yellow
        Write-Host "    Hostname: $Hostname" -ForegroundColor Gray
        Write-Host "    Printer FQDN: $PrinterFQDN" -ForegroundColor Gray
        
        $postData = @{
            action = 'updatePrinterMapping'
            hostname = $Hostname
            printerFQDN = $PrinterFQDN
        }
        
        $headers = @{
            'Content-Type' = 'application/x-www-form-urlencoded'
        }
        
        $response = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $postData -Headers $headers -TimeoutSec 30
        
        # Debug: Show raw response structure
        Write-Host "    DEBUG Response: $($response | ConvertTo-Json -Compress)" -ForegroundColor Magenta

        if ($response.success) {
            Write-Host "    [OK] Printer mapping updated successfully!" -ForegroundColor Green
            Write-Host "      Printer ID: $($response.data.printerId)" -ForegroundColor Gray
            Write-Host "      Machines Updated: $($response.data.machinesUpdated)" -ForegroundColor Gray
            Write-Host "      Match Method: $($response.data.matchMethod)" -ForegroundColor Gray
            return $true
        } else {
            Write-Host "    [WARN] Printer mapping failed: $($response.message)" -ForegroundColor Yellow
            Write-Host "    DEBUG Error Response: $($response | ConvertTo-Json)" -ForegroundColor Red
            return $false
        }
    }
    catch {
        Write-Host "    [WARN] Error sending printer mapping: $($_.Exception.Message)" -ForegroundColor Yellow
        return $false
    }
}

function Send-InstalledAppsToDashboard {
    param(
        [string]$Hostname,
        [array]$TrackedApps,
        [string]$DashboardURL
    )
    
    if (!$TrackedApps -or $TrackedApps.Count -eq 0) {
        Write-Host "  No tracked applications to send - skipping app mapping" -ForegroundColor Gray
        return $true
    }
    
    try {
        Write-Host "  Sending tracked applications to dashboard..." -ForegroundColor Yellow
        Write-Host "    Hostname: $Hostname" -ForegroundColor Gray
        Write-Host "    Tracked Apps: $($TrackedApps.Count)" -ForegroundColor Gray

        # Debug: Show each app with version
        foreach ($app in $TrackedApps) {
            Write-Host "      -> appid=$($app.appid), appname='$($app.appname)', version='$($app.version)'" -ForegroundColor Magenta
        }

        $appsJson = $TrackedApps | ConvertTo-Json -Compress
        Write-Host "    DEBUG JSON: $appsJson" -ForegroundColor Magenta

        $postData = @{
            action = 'updateInstalledApps'
            hostname = $Hostname
            installedApps = $appsJson
        }
        
        $headers = @{
            'Content-Type' = 'application/x-www-form-urlencoded'
        }
        
        $response = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $postData -Headers $headers -TimeoutSec 30
        
        if ($response.success) {
            Write-Host "    [OK] Installed applications updated successfully!" -ForegroundColor Green
            Write-Host "      Apps Processed: $($response.appsProcessed)" -ForegroundColor Gray
            Write-Host "      Machine ID: $($response.machineid)" -ForegroundColor Gray
            if ($response.debugError) {
                Write-Host "      DEBUG ERROR: $($response.debugError)" -ForegroundColor Red
            }
            return $true
        } else {
            Write-Host "    [WARN] Application mapping failed: $($response.message)" -ForegroundColor Yellow
            return $false
        }
    }
    catch {
        Write-Host "    [WARN] Error sending application mapping: $($_.Exception.Message)" -ForegroundColor Yellow
        return $false
    }
}

function Get-WarrantyFromProxy {
    param(
        [string]$ServiceTag,
        [string]$ProxyURL
    )
    
    if ([string]::IsNullOrEmpty($ServiceTag) -or $ServiceTag -eq "Unknown") {
        Write-Host "  No valid service tag - skipping warranty lookup" -ForegroundColor Gray
        return $null
    }
    
    try {
        Write-Host "Getting warranty data from proxy server..." -ForegroundColor Yellow
        
        $uri = "$ProxyURL" + "?vendor=dell&action=warranty&servicetag=$ServiceTag"
        $response = Invoke-RestMethod -Uri $uri -Method Get -TimeoutSec 30
        
        if ($response.success -and $response.warranties -and $response.warranties.Count -gt 0) {
            $warranty = $response.warranties[0]
            
            Write-Host "  [OK] Warranty data retrieved:" -ForegroundColor Green
            Write-Host "    Status: $($warranty.warrantyStatus)"
            Write-Host "    End Date: $($warranty.warrantyEndDate)"
            Write-Host "    Days Remaining: $($warranty.daysRemaining)"
            Write-Host "    Service Level: $($warranty.serviceLevel)"
            
            return $warranty
        } else {
            Write-Host "  [WARN] No warranty data found for service tag: $ServiceTag" -ForegroundColor Yellow
            return $null
        }
    }
    catch {
        Write-Host "  [WARN] Error getting warranty from proxy: $($_.Exception.Message)" -ForegroundColor Yellow
        return $null
    }
}

function Send-CompleteDataToDashboard {
    param(
        [hashtable]$SystemInfo,
        [object]$ShopfloorInfo,
        [object]$WarrantyData,
        [string]$DashboardURL
    )
    
    try {
        Write-Host "Sending complete asset data to dashboard..." -ForegroundColor Yellow
        Write-Host "  Dashboard URL: $DashboardURL" -ForegroundColor Gray
        
        # Prepare comprehensive data package
        $postData = @{
            action = 'updateCompleteAsset'
            
            # Basic system information
            hostname = $SystemInfo.Hostname
            serialNumber = $SystemInfo.SerialNumber
            serviceTag = $SystemInfo.ServiceTag
            manufacturer = $SystemInfo.Manufacturer
            model = $SystemInfo.Model
            pcType = $SystemInfo.PCType
            loggedInUser = $SystemInfo.LoggedInUser
            machineNo = $SystemInfo.MachineNo
            osVersion = $SystemInfo.OSVersion
            totalPhysicalMemory = $SystemInfo.TotalPhysicalMemory
            domainRole = $SystemInfo.DomainRole
            currentTimeZone = $SystemInfo.CurrentTimeZone
            # Format lastBootUpTime as MySQL datetime (YYYY-MM-DD HH:MM:SS)
            lastBootUpTime = if ($SystemInfo.LastBootUpTime) {
                $SystemInfo.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss")
            } else { $null }
        }
        
        # Add warranty data if available
        if ($WarrantyData) {
            $postData.warrantyEndDate = $WarrantyData.warrantyEndDate
            $postData.warrantyStatus = $WarrantyData.warrantyStatus
            $postData.warrantyServiceLevel = $WarrantyData.serviceLevel
            $postData.warrantyDaysRemaining = $WarrantyData.daysRemaining
        }
        
        # Add shopfloor data if available
        if ($ShopfloorInfo) {
            Write-Host "  ShopfloorInfo object contains DNCConfig: $(if ($ShopfloorInfo.DNCConfig) { 'YES' } else { 'NO' })" -ForegroundColor Gray
            Write-Host "  ShopfloorInfo object contains GERegistryInfo: $(if ($ShopfloorInfo.GERegistryInfo) { 'YES' } else { 'NO' })" -ForegroundColor Gray
            
            $postData.networkInterfaces = $ShopfloorInfo.NetworkInterfaces | ConvertTo-Json -Compress
            $postData.commConfigs = $ShopfloorInfo.CommConfigs | ConvertTo-Json -Compress
            
            if ($ShopfloorInfo.DNCConfig) {
                $postData.dncConfig = $ShopfloorInfo.DNCConfig | ConvertTo-Json -Compress
                Write-Host "  Sending DNC Config: $(($ShopfloorInfo.DNCConfig | ConvertTo-Json -Compress).Substring(0, 100))..." -ForegroundColor Cyan
            } else {
                Write-Host "  No DNC Config to send (ShopfloorInfo.DNCConfig is null or empty)" -ForegroundColor Yellow
            }
            
            # Add GE Aircraft Engines registry information
            if ($ShopfloorInfo.GERegistryInfo) {
                $geInfo = $ShopfloorInfo.GERegistryInfo
                $postData.dncDualPathEnabled = if ($geInfo.DualPathEnabled -ne $null) { $geInfo.DualPathEnabled } else { $null }
                $postData.dncPath1Name = $geInfo.Path1Name
                $postData.dncPath2Name = $geInfo.Path2Name
                $postData.dncGeRegistry32Bit = $geInfo.Registry32Bit
                $postData.dncGeRegistry64Bit = $geInfo.Registry64Bit
                $postData.dncGeRegistryNotes = $geInfo.RegistryNotes | ConvertTo-Json -Compress
                
                Write-Host "  Sending GE Registry Info:" -ForegroundColor Cyan
                Write-Host "    32-bit: $($geInfo.Registry32Bit), 64-bit: $($geInfo.Registry64Bit)" -ForegroundColor Cyan
                Write-Host "    DualPath: $(if ($geInfo.DualPathEnabled -ne $null) { $geInfo.DualPathEnabled } else { 'Not Found' })" -ForegroundColor Cyan
                if ($geInfo.Path1Name) { Write-Host "    Path1Name: $($geInfo.Path1Name)" -ForegroundColor Cyan }
                if ($geInfo.Path2Name) { Write-Host "    Path2Name: $($geInfo.Path2Name)" -ForegroundColor Cyan }
            } else {
                Write-Host "  No GE Registry Info to send" -ForegroundColor Yellow
            }
        } else {
            Write-Host "  No ShopfloorInfo available" -ForegroundColor Yellow
        }
        
        # Add installed applications data for shopfloor PCs
        if ($SystemInfo.InstalledApplications -and $SystemInfo.InstalledApplications.Count -gt 0) {
            $postData.installedApplications = $SystemInfo.InstalledApplications | ConvertTo-Json -Compress
            Write-Host "  Sending installed applications data:" -ForegroundColor Cyan
            
            $activeApps = $SystemInfo.InstalledApplications | Where-Object { $_.IsActive -eq $true }
            if ($activeApps.Count -gt 0) {
                foreach ($app in $activeApps) {
                    Write-Host "    - $($app.AppName) (AppID: $($app.AppID)) - ACTIVE (PID: $($app.ProcessID))" -ForegroundColor Green
                }
            } else {
                Write-Host "    - No active applications detected" -ForegroundColor Gray
            }
        } else {
            Write-Host "  No installed applications to send" -ForegroundColor Gray
        }
        
        # Send to dashboard API
        $headers = @{
            'Content-Type' = 'application/x-www-form-urlencoded'
        }
        
        $response = Invoke-RestMethod -Uri $DashboardURL -Method Post -Body $postData -Headers $headers -TimeoutSec 30
        
        if ($response.success) {
            Write-Host "  [OK] Complete asset data stored in database!" -ForegroundColor Green
            $data = $response.data
            Write-Host "    PCID: $(if($data.pcid) { $data.pcid } else { 'Unknown' })"
            Write-Host "    Updated/Created: $(if($data.operation) { $data.operation } else { 'Unknown' })"
            Write-Host "    Records affected: $(if($data.recordsAffected) { $data.recordsAffected } else { 'Unknown' })"
            return $true
        } else {
            Write-Host "  [FAIL] Dashboard could not store data: $($response.error)" -ForegroundColor Red
            if ($response.data -and $response.data.debug) {
                Write-Host "    Debug - DNC Config Received: $($response.data.debug.dncConfigReceived)" -ForegroundColor Yellow
            } elseif ($response.debug) {
                Write-Host "    Debug - DNC Config Received: $($response.debug.dncConfigReceived)" -ForegroundColor Yellow
            }
            return $false
        }
    }
    catch {
        Write-Host "  [FAIL] Error sending data to dashboard: $($_.Exception.Message)" -ForegroundColor Red
        
        # Try to get more details from the response
        if ($_.Exception -is [System.Net.WebException]) {
            $response = $_.Exception.Response
            if ($response) {
                $responseStream = $response.GetResponseStream()
                $reader = New-Object System.IO.StreamReader($responseStream)
                $responseBody = $reader.ReadToEnd()
                $reader.Close()
                
                Write-Host "  Server Response:" -ForegroundColor Yellow
                try {
                    $errorData = $responseBody | ConvertFrom-Json
                    if ($errorData.details) {
                        Write-Host "    Error: $($errorData.details.message)" -ForegroundColor Red
                        Write-Host "    File: $($errorData.details.file):$($errorData.details.line)" -ForegroundColor Gray
                    } else {
                        Write-Host "    $responseBody" -ForegroundColor Gray
                    }
                } catch {
                    Write-Host "    $responseBody" -ForegroundColor Gray
                }
            }
        }
        
        return $false
    }
}

# Main execution
try {
    # Log startup
    Write-Log "========================================" -Level "INFO" -ForegroundColor Green
    Write-Log "Complete PC Asset Collection & Storage" -Level "INFO" -ForegroundColor Green
    Write-Log "========================================" -Level "INFO" -ForegroundColor Green
    Write-Log "Computer: $env:COMPUTERNAME" -Level "INFO" -ForegroundColor White
    Write-Log "Log file: $script:LogFile" -Level "INFO" -ForegroundColor Gray

    # Get dashboard URL
    $dashboardURL = Get-DashboardURL -ProvidedURL $DashboardURL
    Write-Log "Dashboard: $dashboardURL" -Level "INFO" -ForegroundColor Gray
    Write-Log "Note: Warranty lookups disabled (handled by dashboard)" -Level "INFO" -ForegroundColor Gray
    Write-Log "" -Level "INFO" -ForegroundColor White
    
    # Test connections if requested
    if ($TestConnections) {
        Write-Host "=== CONNECTION TESTS ===" -ForegroundColor Cyan
        
        # Only test dashboard connection since proxy is not accessible from PCs
        Write-Host "Skipping proxy test (not accessible from client PCs)" -ForegroundColor Gray
        $dashboardOK = Test-DashboardConnection -DashboardURL $dashboardURL
        
        Write-Host ""
        if ($dashboardOK) {
            Write-Host "[OK] Dashboard connection working!" -ForegroundColor Green
            exit 0
        } else {
            Write-Host "[FAIL] Dashboard connection failed" -ForegroundColor Red
            exit 1
        }
    }
    
    # Step 1: Collect system information
    Write-Host "=== STEP 1: COLLECT SYSTEM INFO ===" -ForegroundColor Cyan
    $systemInfo = Collect-SystemInfo
    
    # Step 2: Collect shopfloor-specific info (if applicable)
    Write-Host ""
    Write-Host "=== STEP 2: COLLECT SHOPFLOOR INFO ===" -ForegroundColor Cyan
    $shopfloorInfo = Collect-ShopfloorInfo -SystemInfo $systemInfo
    
    # Step 3: Get warranty data from proxy (if not skipped and Dell system)
    $warrantyData = $null
    $isDellSystem = $systemInfo.Manufacturer -match "Dell"
    
    Write-Host ""
    Write-Host "=== STEP 3: WARRANTY DATA ===" -ForegroundColor Cyan
    Write-Host "Warranty lookups disabled - Dashboard will handle warranty updates" -ForegroundColor Yellow
    Write-Host "PCs cannot reach proxy server from this network" -ForegroundColor Gray
    
    # Step 4: Send all data to dashboard for storage
    Write-Host ""
    Write-Host "=== STEP 4: STORE IN DATABASE ===" -ForegroundColor Cyan
    $storeSuccess = Send-CompleteDataToDashboard -SystemInfo $systemInfo -ShopfloorInfo $shopfloorInfo -WarrantyData $warrantyData -DashboardURL $dashboardURL
    
    if (-not $storeSuccess) {
        Write-Error "Failed to store asset data in database"
        exit 1
    }
    
    # Step 5: Collect and send default printer mapping
    Write-Host ""
    Write-Host "=== STEP 5: PRINTER MAPPING ===" -ForegroundColor Cyan
    $defaultPrinterFQDN = Get-DefaultPrinterFQDN
    $printerMappingSuccess = Send-PrinterMappingToDashboard -Hostname $systemInfo.Hostname -PrinterFQDN $defaultPrinterFQDN -DashboardURL $dashboardURL
    
    # Step 6: Send tracked applications to database
    Write-Host ""
    Write-Host "=== STEP 6: APPLICATION MAPPING ===" -ForegroundColor Cyan
    $appMappingSuccess = Send-InstalledAppsToDashboard -Hostname $systemInfo.Hostname -TrackedApps $systemInfo.TrackedApplications -DashboardURL $dashboardURL

    # Check if running as admin for Steps 7 & 8
    $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")

    # Step 7: Reset and configure WinRM for remote management (requires admin)
    Write-Log "" -Level "INFO" -ForegroundColor White
    Write-Log "=== STEP 7: WINRM CONFIGURATION ===" -Level "INFO" -ForegroundColor Cyan
    if ($isAdmin) {
        $winrmSuccess = Reset-WinRMConfiguration
    } else {
        Write-Log "  [SKIP] Not running as admin - WinRM configuration skipped" -Level "WARN" -ForegroundColor Yellow
        $winrmSuccess = $false
    }

    # Step 8: Add WinRM management group to local Administrators (requires admin)
    Write-Log "" -Level "INFO" -ForegroundColor White
    Write-Log "=== STEP 8: WINRM ADMIN GROUP ===" -Level "INFO" -ForegroundColor Cyan
    if ($isAdmin) {
        $adminGroupSuccess = Add-WinRMAdminGroup
    } else {
        Write-Log "  [SKIP] Not running as admin - Admin group setup skipped" -Level "WARN" -ForegroundColor Yellow
        $adminGroupSuccess = $false
    }

    # Final Summary
    Write-Log "" -Level "INFO" -ForegroundColor White
    Write-Log "=== COMPLETE ASSET UPDATE SUCCESS ===" -Level "INFO" -ForegroundColor Green
    Write-Log "Computer: $($systemInfo.Hostname)" -Level "INFO" -ForegroundColor White
    Write-Log "Type: $($systemInfo.PCType)" -Level "INFO" -ForegroundColor White
    Write-Log "Serial: $($systemInfo.SerialNumber)" -Level "INFO" -ForegroundColor White
    if ($systemInfo.MachineNo) {
        Write-Log "Machine: $($systemInfo.MachineNo)" -Level "INFO" -ForegroundColor White
    }
    Write-Log "" -Level "INFO" -ForegroundColor White
    Write-Log "Data Collected & Stored:" -Level "INFO" -ForegroundColor Cyan
    Write-Log "[OK] Basic system information" -Level "SUCCESS" -ForegroundColor Green
    if ($defaultPrinterFQDN) {
        Write-Log "[OK] Default printer mapping ($defaultPrinterFQDN)" -Level "SUCCESS" -ForegroundColor Green
    } else {
        Write-Log "[--] Default printer mapping (no printer found)" -Level "INFO" -ForegroundColor Gray
    }
    if ($systemInfo.TrackedApplications -and $systemInfo.TrackedApplications.Count -gt 0) {
        Write-Log "[OK] Application mapping ($($systemInfo.TrackedApplications.Count) tracked apps)" -Level "SUCCESS" -ForegroundColor Green
    } else {
        Write-Log "[--] Application mapping (no tracked apps found)" -Level "INFO" -ForegroundColor Gray
    }

    if ($shopfloorInfo) {
        Write-Log "[OK] Shopfloor configurations ($($shopfloorInfo.NetworkInterfaces.Count) network, $($shopfloorInfo.CommConfigs.Count) comm)" -Level "SUCCESS" -ForegroundColor Green
    }

    if ($warrantyData) {
        Write-Log "[OK] Warranty information ($($warrantyData.warrantyStatus), $($warrantyData.daysRemaining) days)" -Level "SUCCESS" -ForegroundColor Green
    } elseif (-not $SkipWarranty -and $isDellSystem) {
        Write-Log "[WARN] Warranty information (lookup failed)" -Level "WARN" -ForegroundColor Yellow
    }

    if ($winrmSuccess) {
        Write-Log "[OK] WinRM HTTP listener (port 5985)" -Level "SUCCESS" -ForegroundColor Green
        Write-Log "    Note: If remote access still fails, a reboot may be required" -Level "INFO" -ForegroundColor Gray
    } else {
        Write-Log "[WARN] WinRM configuration (may need manual setup)" -Level "WARN" -ForegroundColor Yellow
    }

    if ($adminGroupSuccess) {
        Write-Log "[OK] WinRM admin group (logon\g03078610)" -Level "SUCCESS" -ForegroundColor Green
    } else {
        Write-Log "[WARN] WinRM admin group (failed to add)" -Level "WARN" -ForegroundColor Yellow
    }

    Write-Log "" -Level "INFO" -ForegroundColor White
    Write-Log "[OK] Complete PC asset collection finished!" -Level "SUCCESS" -ForegroundColor Green
    Write-Log "All data stored in database via dashboard API." -Level "INFO" -ForegroundColor Gray
    Write-Log "Log file: $script:LogFile" -Level "INFO" -ForegroundColor Gray

} catch {
    Write-Log "Complete asset collection failed: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red
    Write-Error "Complete asset collection failed: $($_.Exception.Message)"
    exit 1
}