# Update-MachineNumber.ps1 - Shared helper for reading and updating the
# machine number in UDC and eDNC. Dot-source from any script that needs
# machine-number operations:
#
#   . "$PSScriptRoot\lib\Update-MachineNumber.ps1"   (from Shopfloor\ scripts)
#   . "$PSScriptRoot\..\Shopfloor\lib\Update-MachineNumber.ps1" (from Standard\ scripts)
#   . "$PSScriptRoot\Update-MachineNumber.ps1"       (from lib\ scripts)
#
# Exported functions:
#   Get-CurrentMachineNumber  - returns @{ Udc = $string_or_null; Ednc = $string_or_null }
#   Update-MachineNumber      - updates both, returns @{ UdcUpdated = $bool; EdncUpdated = $bool; Errors = @() }
#
# Both handle missing files/keys gracefully (app not installed = skip, not error).

$script:UdcSettingsPath = 'C:\ProgramData\UDC\udc_settings.json'
$script:UdcExePath      = 'C:\Program Files\UDC\UDC.exe'
$script:EdncRegPath     = 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General'

function Get-CurrentMachineNumber {
    <#
    .SYNOPSIS
        Reads the current machine number from UDC settings JSON and eDNC registry.
    .OUTPUTS
        Hashtable with keys Udc ($string or $null) and Ednc ($string or $null).
    #>
    $result = @{ Udc = $null; Ednc = $null }

    if (Test-Path $script:UdcSettingsPath) {
        try {
            $json = Get-Content $script:UdcSettingsPath -Raw | ConvertFrom-Json
            $result.Udc = $json.GeneralSettings.MachineNumber
        } catch {}
    }

    if (Test-Path $script:EdncRegPath) {
        try {
            $result.Ednc = (Get-ItemProperty -Path $script:EdncRegPath -Name MachineNo -ErrorAction Stop).MachineNo
        } catch {}
    }

    return $result
}

function Update-MachineNumber {
    <#
    .SYNOPSIS
        Updates UDC + eDNC machine number in one step. Stops UDC before writing,
        relaunches after.
    .PARAMETER NewNumber
        The new machine number (digits only - caller validates).
    .PARAMETER Site
        Site name passed to UDC.exe -site argument. Defaults to 'West Jefferson'.
    .OUTPUTS
        Hashtable: @{ UdcUpdated = $bool; EdncUpdated = $bool; Errors = @() }
    #>
    param(
        [Parameter(Mandatory)]
        [string]$NewNumber,

        [string]$Site = 'West Jefferson'
    )

    $out = @{ UdcUpdated = $false; EdncUpdated = $false; Errors = @(); RegImported = $null }

    # --- If UDC or eDNC is still at placeholder 9999, try to pull the
    #     per-machine .reg backup from the SFLD share and restore all
    #     the eFocas/PPDCS/Hssb config. The tech-typed $NewNumber is still
    #     written last (below), so the restore never clobbers it. ---
    $current = Get-CurrentMachineNumber
    $isPlaceholder = (($current.Udc -in @('9999', $null, '')) -or ($current.Ednc -in @('9999', $null, '')))

    if ($isPlaceholder -and $NewNumber -ne '9999') {
        $sharePath = $null
        $siteCfgPath = 'C:\Enrollment\site-config.json'
        if (Test-Path $siteCfgPath) {
            try {
                $cfg = Get-Content $siteCfgPath -Raw | ConvertFrom-Json
                $sharePath = $cfg.pcProfiles.'Standard-Machine'.ntlarsBackupSharePath
            } catch {}
        }

        if ($sharePath) {
            try {
                . (Join-Path $PSScriptRoot 'Restore-EDncReg.ps1')
                $mounted = Mount-SFLDShare -SharePath $sharePath -DriveLetter 'V:'
                if ($mounted) {
                    try {
                        $out.RegImported = Import-EDncRegBackup -SourceRoot 'V:\' -MachineNumber $NewNumber
                    } finally {
                        & net use V: /delete /y 2>$null | Out-Null
                    }
                } else {
                    Write-Host "  Update-MachineNumber: SFLD share unreachable - skipping restore."
                }
            } catch {
                $out.Errors += "ntlars restore failed: $_"
            }
        }

        # --- UDC settings JSON restore: pull udc_settings_<NewNumber>.json
        #     from the SFLD UDC settings_backups share. At imaging time
        #     00-PreInstall-MachineApps.ps1 pulls this from the local
        #     C:\Enrollment mirror, but a 9999-placeholder PC has no real
        #     pre-stage. Once the tech sets the real number we have SFLD
        #     creds, so go direct to the canonical share. ---
        $udcSettingsSharePath = $null
        if ($cfg) {
            try { $udcSettingsSharePath = $cfg.pcProfiles.'Standard-Machine'.udcSettingsSharePath } catch {}
        }
        if ($udcSettingsSharePath) {
            try {
                $mountedUdcSet = Mount-SFLDShare -SharePath $udcSettingsSharePath -DriveLetter 'X:'
                if ($mountedUdcSet) {
                    try {
                        $srcSettings = Join-Path 'X:\' "udc_settings_$NewNumber.json"
                        if (Test-Path -LiteralPath $srcSettings) {
                            Get-Process UDC -ErrorAction SilentlyContinue | ForEach-Object {
                                try { $_.Kill(); $_.WaitForExit(5000) | Out-Null } catch {}
                            }
                            Start-Sleep -Milliseconds 500
                            $localUdcDir = 'C:\ProgramData\UDC'
                            if (-not (Test-Path $localUdcDir)) { New-Item -ItemType Directory -Path $localUdcDir -Force | Out-Null }
                            Copy-Item -LiteralPath $srcSettings -Destination $script:UdcSettingsPath -Force -ErrorAction Stop
                            $out.UdcSettingsRestored = $true
                            Write-Host "  Update-MachineNumber: UDC settings restored from $srcSettings"
                        } else {
                            Write-Host "  Update-MachineNumber: no udc_settings_$NewNumber.json on settings_backups share"
                        }
                    } finally {
                        & net use X: /delete /y 2>$null | Out-Null
                    }
                } else {
                    Write-Host "  Update-MachineNumber: UDC settings_backups share unreachable - skipping settings restore."
                }
            } catch {
                $out.Errors += "UDC settings restore failed: $_"
            }
        }

        # --- UDC data restore: pull CurrentData.json + ArchivedData/ from
        #     the per-bay backup at <udcBackupSharePath>\<NewNumber>\.
        #     One-shot: after successful restore, the live backup at the root
        #     is moved into <NewNumber>\migrated\<timestamp>\ so it can't be
        #     replayed on subsequent reboots or reused for a future PC at the
        #     same bay. The 'isPlaceholder' guard above ensures this whole
        #     block only ever fires once per PC's lifetime (placeholder->real
        #     transition). ---
        $udcSharePath = $null
        if ($cfg) {
            try { $udcSharePath = $cfg.pcProfiles.'Standard-Machine'.udcBackupSharePath } catch {}
        }
        if ($udcSharePath) {
            try {
                $mountedUdc = Mount-SFLDShare -SharePath $udcSharePath -DriveLetter 'W:'
                if ($mountedUdc) {
                    try {
                        $bayDir   = Join-Path 'W:\' $NewNumber
                        $srcCur   = Join-Path $bayDir 'CurrentData.json'
                        $srcArc   = Join-Path $bayDir 'ArchivedData'
                        if (Test-Path -LiteralPath $srcCur) {
                            Write-Host "  Update-MachineNumber: UDC backup found at $bayDir - restoring."

                            # Stop UDC pre-emptively so CurrentData.json isn't locked
                            Get-Process UDC -ErrorAction SilentlyContinue | ForEach-Object {
                                try { $_.Kill(); $_.WaitForExit(5000) | Out-Null } catch {}
                            }
                            Start-Sleep -Milliseconds 500

                            $localUdcDir = 'C:\ProgramData\UDC'
                            if (-not (Test-Path $localUdcDir)) { New-Item -ItemType Directory -Path $localUdcDir -Force | Out-Null }

                            $localArc = Join-Path $localUdcDir 'ArchivedData'

                            # Copy
                            Copy-Item -LiteralPath $srcCur -Destination (Join-Path $localUdcDir 'CurrentData.json') -Force -ErrorAction Stop
                            if (Test-Path -LiteralPath $srcArc) {
                                if (Test-Path -LiteralPath $localArc) { Remove-Item -LiteralPath $localArc -Recurse -Force -ErrorAction SilentlyContinue }
                                Copy-Item -LiteralPath $srcArc -Destination $localArc -Recurse -Force -ErrorAction Stop
                            }

                            # Move live backup -> migrated/<timestamp>/  (one-shot consumption)
                            $stamp     = (Get-Date -Format 'yyyy-MM-ddTHH-mm-ssZ')
                            $migDir    = Join-Path $bayDir 'migrated'
                            $migStamp  = Join-Path $migDir $stamp
                            if (-not (Test-Path -LiteralPath $migDir))   { New-Item -ItemType Directory -Path $migDir -Force | Out-Null }
                            if (-not (Test-Path -LiteralPath $migStamp)) { New-Item -ItemType Directory -Path $migStamp -Force | Out-Null }

                            Move-Item -LiteralPath $srcCur -Destination (Join-Path $migStamp 'CurrentData.json') -Force -ErrorAction Stop
                            if (Test-Path -LiteralPath $srcArc) {
                                Move-Item -LiteralPath $srcArc -Destination (Join-Path $migStamp 'ArchivedData') -Force -ErrorAction Stop
                            }
                            $bakManifest = Join-Path $bayDir 'backup.manifest.json'
                            if (Test-Path -LiteralPath $bakManifest) {
                                Move-Item -LiteralPath $bakManifest -Destination (Join-Path $migStamp 'backup.manifest.json') -Force -ErrorAction SilentlyContinue
                            }

                            # Audit manifest in migrated/<stamp>/
                            $localArcInfo = if (Test-Path $localArc) { Get-ChildItem $localArc -Recurse -File -ErrorAction SilentlyContinue } else { @() }
                            $restoreManifest = [ordered]@{
                                RestoredAt          = (Get-Date -Format 'o')
                                DestinationHostname = $env:COMPUTERNAME
                                DestinationUser     = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
                                MachineNumber       = $NewNumber
                                CurrentDataBytes    = (Get-Item (Join-Path $localUdcDir 'CurrentData.json')).Length
                                ArchivedDataFiles    = $localArcInfo.Count
                                ArchivedDataBytes    = ($localArcInfo | Measure-Object Length -Sum).Sum
                                RestoredVia         = 'Update-MachineNumber.ps1'
                            }
                            $restoreManifest | ConvertTo-Json | Set-Content -Path (Join-Path $migStamp 'restore.manifest.json') -Encoding UTF8

                            $out.UdcRestored = $true
                            Write-Host "  Update-MachineNumber: UDC restore OK (consumed -> migrated\$stamp\)"
                        } else {
                            Write-Host "  Update-MachineNumber: no UDC backup at $bayDir (fresh PC, no prior data)"
                        }
                    } finally {
                        & net use W: /delete /y 2>$null | Out-Null
                    }
                } else {
                    Write-Host "  Update-MachineNumber: UDC backup share unreachable - skipping UDC restore."
                }
            } catch {
                $out.Errors += "UDC restore failed: $_"
            }
        }
    }

    # --- Stop UDC before editing its JSON (avoid stale shutdown write) ---
    Get-Process UDC -ErrorAction SilentlyContinue | ForEach-Object {
        try { $_.Kill(); $_.WaitForExit(5000) | Out-Null } catch {}
    }
    Start-Sleep -Seconds 1

    # --- Update UDC settings JSON ---
    if (Test-Path $script:UdcSettingsPath) {
        try {
            $json = Get-Content $script:UdcSettingsPath -Raw | ConvertFrom-Json
            $json.GeneralSettings.MachineNumber = $NewNumber
            $json | ConvertTo-Json -Depth 99 | Set-Content -Path $script:UdcSettingsPath -Encoding UTF8
            $out.UdcUpdated = $true
        } catch {
            $out.Errors += "UDC update failed: $_"
        }
    }

    # --- Update eDNC registry ---
    if (Test-Path $script:EdncRegPath) {
        try {
            Set-ItemProperty -Path $script:EdncRegPath -Name MachineNo -Value $NewNumber -Type String -Force
            $out.EdncUpdated = $true
        } catch {
            $out.Errors += "eDNC update failed: $_"
        }
    }

    # --- Relaunch UDC with new args ---
    if ((Test-Path $script:UdcExePath) -and $out.UdcUpdated) {
        try {
            # UDC.exe arg signature: quoted site name (with space), then
            # dash-prefixed machine number. Example: UDC.exe "West Jefferson" -7605
            Start-Process -FilePath $script:UdcExePath -ArgumentList @("`"$Site`"", "-$NewNumber")
        } catch {
            $out.Errors += "UDC relaunch failed: $_"
        }
    }

    # --- Update MTConnect Devices.xml (per-variant) + restart agent services ---
    # MTConnect deploys via the GE-Enforce manifest engine + the
    # Install_MTConnect_ExisDir_BatConvert.ps1 wrapper. Devices.xml on disk
    # has the machine number embedded in the <Device name= uuid= id=> attrs.
    # When the tech changes 9999 -> real number, those attrs need to follow,
    # OR MTConnect data will keep reporting under the old/wrong identity.
    #
    # We do an inline substitution rather than re-running the full wrapper
    # because the tech may not have credential delegation to the SFLD share
    # from their interactive session.
    $out.MTConnectUpdated = @()
    # Drive this off "is the service installed?" rather than file existence:
    # Fanuc and Okuma both deploy to C:\MTConnect\Agent\, distinguishable only
    # by the registered service name (NTFS is case-insensitive so the two
    # devices.xml / Devices.xml entries collapse to the same file). Without
    # this filter, the Okuma branch on an Okuma PC sees the file already
    # rewritten by the (no-op) Fanuc branch and skips the service restart.
    $mtcVariants = @(
        @{ Service='MTConnect Agent Fanuc';  Path='C:\MTConnect\Agent\devices.xml'        },
        @{ Service='MTConnect Agent Okuma';  Path='C:\MTConnect\Agent\Devices.xml'        },
        @{ Service='MTConnect eDNC Agent';   Path='C:\MTConnect_eDNC\Agent\Devices.xml'   },
        @{ Service='Makino MTConnect Agent'; Path='C:\Makino-MTConnect\Agent\Devices.xml' }
    )
    foreach ($v in $mtcVariants) {
        $svc = Get-Service -Name $v.Service -ErrorAction SilentlyContinue
        if (-not $svc) { continue }
        if (-not (Test-Path -LiteralPath $v.Path)) { continue }
        try {
            $content = Get-Content -LiteralPath $v.Path -Raw -ErrorAction Stop
            # Find the Device root's name= attr to discover the OLD identifier
            if ($content -notmatch '<Device[^>]+name="([^"]+)"') { continue }
            $oldName = $matches[1]
            # Most variants encode machine number as the trailing digit run:
            #   Fanuc/Makino:  name="9999"            -> trailing 9999
            #   OKUMA:         name="loc9999"         -> trailing 9999, prefix 'loc'
            #   eDNC:          name="eDNC_OKUMA9999"  -> trailing 9999, prefix 'eDNC_OKUMA'
            if ($oldName -notmatch '^(.*?)(\d+)$') { continue }
            $prefix = $matches[1]
            $oldDigits = $matches[2]
            if ($oldDigits -eq $NewNumber) { continue }  # already correct
            $oldFull = "$prefix$oldDigits"
            $newFull = "$prefix$NewNumber"
            $oldEsc = [regex]::Escape($oldFull)
            # Quoted attr value: name="X" / uuid="X" / id="X"
            $content = $content -replace ('"' + $oldEsc + '"'), ('"' + $newFull + '"')
            # OKUMA-style with serial after dot: uuid="locX.<serial>"
            $content = $content -replace ('"' + $oldEsc + '\.'), ('"' + $newFull + '.')
            Set-Content -LiteralPath $v.Path -Value $content -NoNewline -ErrorAction Stop
            $out.MTConnectUpdated += "$($v.Path) ($oldFull -> $newFull)"
            try { Restart-Service -Name $v.Service -Force -ErrorAction Stop }
            catch { $out.Errors += "Restart $($v.Service) failed: $_" }
        } catch {
            $out.Errors += "MTConnect Devices.xml update failed for $($v.Path): $_"
        }
    }

    return $out
}
