private/Initialize-SplitterUi.ps1

function Initialize-SplitterUi {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object]
        $Window,

        [hashtable]
        $Controls
    )

    if ($Controls) {
        $controls = $Controls
    }
    else {
        $controls = @{
            CloseWindowButton = $Window.FindControl('CloseWindowButton')
            ExitMenuItem = $Window.FindControl('ExitMenuItem')
            StatusText = $Window.FindControl('StatusText')
            FooterText = $Window.FindControl('FooterText')
            SourceIsoTextBox = $Window.FindControl('SourceIsoTextBox')
            BrowseSourceIsoButton = $Window.FindControl('BrowseSourceIsoButton')
            EditionNamesTextBox = $Window.FindControl('EditionNamesTextBox')
            WorkingRootTextBox = $Window.FindControl('WorkingRootTextBox')
            OutputRootTextBox = $Window.FindControl('OutputRootTextBox')
            OutputBaseNameTextBox = $Window.FindControl('OutputBaseNameTextBox')
            LabelPrefixTextBox = $Window.FindControl('LabelPrefixTextBox')
            SkipBootableIsoCheckBox = $Window.FindControl('SkipBootableIsoCheckBox')
            DesktopExperienceCheckBox = $Window.FindControl('DesktopExperienceCheckBox')
            ServerCoreCheckBox = $Window.FindControl('ServerCoreCheckBox')
            DiscoverImagesButton = $Window.FindControl('DiscoverImagesButton')
            RunSplitButton = $Window.FindControl('RunSplitButton')
            RunPreflightButton = $Window.FindControl('RunPreflightButton')
            ImagesListBox = $Window.FindControl('ImagesListBox')
            MediaLogTextBox = $Window.FindControl('MediaLogTextBox')
            ServiceMediaRootTextBox = $Window.FindControl('ServiceMediaRootTextBox')
            DriverPathTextBox = $Window.FindControl('DriverPathTextBox')
            PackagePathTextBox = $Window.FindControl('PackagePathTextBox')
            ServiceIndexesTextBox = $Window.FindControl('ServiceIndexesTextBox')
            UnattendPathTextBox = $Window.FindControl('UnattendPathTextBox')
            UnattendDestinationTextBox = $Window.FindControl('UnattendDestinationTextBox')
            ServiceRecurseDriversCheckBox = $Window.FindControl('ServiceRecurseDriversCheckBox')
            ServiceForceUnsignedDriversCheckBox = $Window.FindControl('ServiceForceUnsignedDriversCheckBox')
            ServiceIgnoreCheckCheckBox = $Window.FindControl('ServiceIgnoreCheckCheckBox')
            ServicePreventPendingCheckBox = $Window.FindControl('ServicePreventPendingCheckBox')
            AddDriverButton = $Window.FindControl('AddDriverButton')
            AddPackageButton = $Window.FindControl('AddPackageButton')
            AddUnattendButton = $Window.FindControl('AddUnattendButton')
            AddPayloadFileButton = $Window.FindControl('AddPayloadFileButton')
            PayloadSourceTextBox = $Window.FindControl('PayloadSourceTextBox')
            PayloadDestinationTextBox = $Window.FindControl('PayloadDestinationTextBox')
            ServicingLogTextBox = $Window.FindControl('ServicingLogTextBox')
            ServicingSelectionsListBox = $Window.FindControl('ServicingSelectionsListBox')
            BuildMediaRootTextBox = $Window.FindControl('BuildMediaRootTextBox')
            BuildOutputIsoTextBox = $Window.FindControl('BuildOutputIsoTextBox')
            BuildLabelTextBox = $Window.FindControl('BuildLabelTextBox')
            BuildSourceImageTextBox = $Window.FindControl('BuildSourceImageTextBox')
            BuildDestinationWimTextBox = $Window.FindControl('BuildDestinationWimTextBox')
            BuildIndexesTextBox = $Window.FindControl('BuildIndexesTextBox')
            BuildBootableIsoButton = $Window.FindControl('BuildBootableIsoButton')
            ExportInstallImageButton = $Window.FindControl('ExportInstallImageButton')
            BuildLogTextBox = $Window.FindControl('BuildLogTextBox')
            LogTextBox = $Window.FindControl('LogTextBox')
            ResultsListBox = $Window.FindControl('ResultsListBox')
        }
    }

    $optionalControls = @(
        'CloseWindowButton',
        'ExitMenuItem',
        'MediaLogTextBox',
        'ServiceMediaRootTextBox',
        'DriverPathTextBox',
        'PackagePathTextBox',
        'ServiceIndexesTextBox',
        'UnattendPathTextBox',
        'UnattendDestinationTextBox',
        'ServiceRecurseDriversCheckBox',
        'ServiceForceUnsignedDriversCheckBox',
        'ServiceIgnoreCheckCheckBox',
        'ServicePreventPendingCheckBox',
        'AddDriverButton',
        'AddPackageButton',
        'AddUnattendButton',
        'AddPayloadFileButton',
        'PayloadSourceTextBox',
        'PayloadDestinationTextBox',
        'ServicingLogTextBox',
        'ServicingSelectionsListBox',
        'BuildMediaRootTextBox',
        'BuildOutputIsoTextBox',
        'BuildLabelTextBox',
        'BuildSourceImageTextBox',
        'BuildDestinationWimTextBox',
        'BuildIndexesTextBox',
        'BuildBootableIsoButton',
        'ExportInstallImageButton',
        'BuildLogTextBox'
    )

    foreach ($key in $controls.Keys) {
        if ($null -eq $controls[$key]) {
            if ($key -in $optionalControls) {
                continue
            }
            throw "Missing expected UI control '$key'."
        }
    }

    $invokeGetSplitterUiPreflight = ${function:Get-SplitterUiPreflight}.GetNewClosure()
    $invokeGetSplitterUiExecutionPlan = ${function:Get-SplitterUiExecutionPlan}.GetNewClosure()
    $invokeInvokeSplitterUiSplit = ${function:Invoke-SplitterUiSplit}.GetNewClosure()
    $invokeGetInstallImagePath = ${function:Get-InstallImagePath}.GetNewClosure()
    $invokeGetOscdimgPath = ${function:Get-OscdimgPath}.GetNewClosure()
    $invokeGetSplitterUiSourceIsoPath = ${function:Get-SplitterUiSourceIsoPath}.GetNewClosure()

    $appendLog = {
        param(
            [string]
            $Message
        )

        $timestamp = Get-Date -Format 'HH:mm:ss'
        $line = "[$timestamp] $Message"

        if ([string]::IsNullOrWhiteSpace($controls.LogTextBox.Text)) {
            $controls.LogTextBox.Text = $line
            return
        }

        $controls.LogTextBox.Text = "{0}{1}{2}" -f $controls.LogTextBox.Text, [Environment]::NewLine, $line
    }.GetNewClosure()

    $appendMediaLog = {
        param(
            [string]
            $Message
        )

        if ($null -eq $controls.MediaLogTextBox) {
            $appendLog.Invoke($Message)
            return
        }

        $timestamp = Get-Date -Format 'HH:mm:ss'
        $line = "[$timestamp] $Message"

        if ([string]::IsNullOrWhiteSpace($controls.MediaLogTextBox.Text)) {
            $controls.MediaLogTextBox.Text = $line
            return
        }

        $controls.MediaLogTextBox.Text = "{0}{1}{2}" -f $controls.MediaLogTextBox.Text, [Environment]::NewLine, $line
    }.GetNewClosure()

    $appendServicingLog = {
        param(
            [string]
            $Message
        )

        if ($null -eq $controls.ServicingLogTextBox) {
            $appendLog.Invoke($Message)
            return
        }

        $timestamp = Get-Date -Format 'HH:mm:ss'
        $line = "[$timestamp] $Message"

        if ([string]::IsNullOrWhiteSpace($controls.ServicingLogTextBox.Text)) {
            $controls.ServicingLogTextBox.Text = $line
            return
        }

        $controls.ServicingLogTextBox.Text = "{0}{1}{2}" -f $controls.ServicingLogTextBox.Text, [Environment]::NewLine, $line
    }.GetNewClosure()

    $appendBuildLog = {
        param(
            [string]
            $Message
        )

        if ($null -eq $controls.BuildLogTextBox) {
            $appendLog.Invoke($Message)
            return
        }

        $timestamp = Get-Date -Format 'HH:mm:ss'
        $line = "[$timestamp] $Message"

        if ([string]::IsNullOrWhiteSpace($controls.BuildLogTextBox.Text)) {
            $controls.BuildLogTextBox.Text = $line
            return
        }

        $controls.BuildLogTextBox.Text = "{0}{1}{2}" -f $controls.BuildLogTextBox.Text, [Environment]::NewLine, $line
    }.GetNewClosure()

    $setListBoxItems = {
        param(
            [Parameter(Mandatory)]
            [object]
            $ListBox,

            [Parameter(Mandatory)]
            [object[]]
            $Items
        )

        $ListBox.Items.Clear()
        foreach ($item in $Items) {
            $ListBox.Items.Add($item) | Out-Null
        }
    }.GetNewClosure()

    $getIndexListFromText = {
        param(
            [string]
            $Text
        )

        if ([string]::IsNullOrWhiteSpace($Text)) {
            return @()
        }

        $values = $Text -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
        $parsed = [System.Collections.Generic.List[int]]::new()
        foreach ($value in $values) {
            [int] $indexValue = 0
            if (-not [int]::TryParse($value, [ref] $indexValue)) {
                throw "Index value '$value' is not a valid integer."
            }
            $parsed.Add($indexValue)
        }

        return @($parsed.ToArray())
    }.GetNewClosure()

    $appendStreamLines = {
        param(
            [Parameter(Mandatory)]
            [object[]]
            $StreamRecords,

            [Parameter(Mandatory)]
            [scriptblock]
            $AppendAction
        )

        foreach ($streamRecord in $StreamRecords) {
            if ($streamRecord -is [System.Management.Automation.VerboseRecord]) {
                $AppendAction.Invoke($streamRecord.Message)
            }
            elseif ($null -ne $streamRecord) {
                $AppendAction.Invoke($streamRecord.ToString())
            }
        }
    }.GetNewClosure()

    $addServicingSelectionItem = {
        param(
            [string]
            $Message
        )

        if ($null -eq $controls.ServicingSelectionsListBox) {
            return
        }

        $timestamp = Get-Date -Format 'HH:mm:ss'
        $controls.ServicingSelectionsListBox.Items.Add("[$timestamp] $Message") | Out-Null
    }.GetNewClosure()

    $selectFilePath = {
        param(
            [string]
            $Title,

            [string]
            $InitialPath
        )

        $options = [GliderUI.Avalonia.Platform.Storage.FilePickerOpenOptions]::new()
        $options.Title = $Title
        $options.AllowMultiple = $false

        if (-not [string]::IsNullOrWhiteSpace($InitialPath)) {
            try {
                $parentDirectory = if (Test-Path -LiteralPath $InitialPath -PathType Container) {
                    $InitialPath
                }
                else {
                    Split-Path -Path $InitialPath -Parent
                }

                if (-not [string]::IsNullOrWhiteSpace($parentDirectory) -and (Test-Path -LiteralPath $parentDirectory)) {
                    $directoryUri = [System.Uri]::new($parentDirectory)
                    $options.SuggestedStartLocation = $Window.StorageProvider.TryGetFolderFromPathAsync($directoryUri).WaitForCompleted()
                }
            }
            catch {
                # Ignore start location errors and continue with platform default picker location.
            }
        }

        $files = $Window.StorageProvider.OpenFilePickerAsync($options).WaitForCompleted()
        $selectedFile = @($files) | Select-Object -First 1
        if ($null -eq $selectedFile -or $null -eq $selectedFile.Path) {
            return $null
        }

        return [System.Uri]::UnescapeDataString($selectedFile.Path.AbsolutePath)
    }.GetNewClosure()

    $selectFolderPath = {
        param(
            [string]
            $Title,

            [string]
            $InitialPath
        )

        $options = [GliderUI.Avalonia.Platform.Storage.FolderPickerOpenOptions]::new()
        $options.Title = $Title
        $options.AllowMultiple = $false

        if (-not [string]::IsNullOrWhiteSpace($InitialPath)) {
            try {
                $startDirectory = if (Test-Path -LiteralPath $InitialPath -PathType Container) {
                    $InitialPath
                }
                else {
                    Split-Path -Path $InitialPath -Parent
                }

                if (-not [string]::IsNullOrWhiteSpace($startDirectory) -and (Test-Path -LiteralPath $startDirectory)) {
                    $directoryUri = [System.Uri]::new($startDirectory)
                    $options.SuggestedStartLocation = $Window.StorageProvider.TryGetFolderFromPathAsync($directoryUri).WaitForCompleted()
                }
            }
            catch {
                # Ignore start location errors and continue with platform default picker location.
            }
        }

        $folders = $Window.StorageProvider.OpenFolderPickerAsync($options).WaitForCompleted()
        $selectedFolder = @($folders) | Select-Object -First 1
        if ($null -eq $selectedFolder -or $null -eq $selectedFolder.Path) {
            return $null
        }

        return [System.Uri]::UnescapeDataString($selectedFolder.Path.AbsolutePath)
    }.GetNewClosure()

    $controls.WorkingRootTextBox.Text = Join-Path $env:TEMP 'SplitterWork'
    $controls.OutputRootTextBox.Text = Join-Path $env:TEMP 'SplitterOut'
    $controls.EditionNamesTextBox.Text = 'Standard'
    if ($controls.ServiceMediaRootTextBox) {
        $controls.ServiceMediaRootTextBox.Text = $controls.OutputRootTextBox.Text
    }
    if ($controls.BuildMediaRootTextBox) {
        $controls.BuildMediaRootTextBox.Text = $controls.OutputRootTextBox.Text
    }
    if ($controls.BuildOutputIsoTextBox) {
        $controls.BuildOutputIsoTextBox.Text = Join-Path $controls.OutputRootTextBox.Text 'custom.iso'
    }
    if ($controls.BuildLabelTextBox) {
        $controls.BuildLabelTextBox.Text = 'WINCUSTOM'
    }
    if ($controls.UnattendDestinationTextBox -and [string]::IsNullOrWhiteSpace($controls.UnattendDestinationTextBox.Text)) {
        $controls.UnattendDestinationTextBox.Text = 'autounattend.xml'
    }
    if ($controls.PayloadDestinationTextBox -and [string]::IsNullOrWhiteSpace($controls.PayloadDestinationTextBox.Text)) {
        $controls.PayloadDestinationTextBox.Text = 'sources\$OEM$\$1\Install\PowerShell-7.6.3-win-x64.msi'
    }

    $controls.BrowseSourceIsoButton.AddClick({
        try {
            $getSplitterUiSourceIsoPathParams = @{
                Window = $Window
                InitialPath = $controls.SourceIsoTextBox.Text
            }
            $selectedPath = & $invokeGetSplitterUiSourceIsoPath @getSplitterUiSourceIsoPathParams
            if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
                $controls.SourceIsoTextBox.Text = $selectedPath
                $appendMediaLog.Invoke("Selected source ISO: $selectedPath")
            }
            else {
                $appendMediaLog.Invoke('Source ISO selection canceled.')
            }
        }
        catch {
            $appendMediaLog.Invoke("Source ISO browse failed. $($_.Exception.Message)")
        }
    }.GetNewClosure())

    if ($controls.ExitMenuItem) {
        $controls.ExitMenuItem.AddClick({
            $Window.Close()
        }.GetNewClosure())
    }

    if ($controls.CloseWindowButton) {
        $controls.CloseWindowButton.AddClick({
            $Window.Close()
        }.GetNewClosure())
    }

    $controls.RunPreflightButton.AddClick({
        $appendMediaLog.Invoke('Running preflight checks...')

        $getSplitterUiPreflightParams = @{
            SourceIso = $controls.SourceIsoTextBox.Text
            GetOscdimgPathScript = $invokeGetOscdimgPath
        }
        $preflight = & $invokeGetSplitterUiPreflight @getSplitterUiPreflightParams

        $summary = "Preflight checks: $($preflight.PassedCount)/$($preflight.TotalCount) passed."
        $controls.StatusText.Text = $summary
        $appendMediaLog.Invoke($summary)

        foreach ($check in $preflight.Checks) {
            $prefix = if ($check.Passed) { 'PASS' } else { 'FAIL' }
            $appendMediaLog.Invoke("$prefix $($check.Name): $($check.Message)")
        }
    }.GetNewClosure())

    $controls.DiscoverImagesButton.AddClick({
        $setListBoxItems.Invoke($controls.ImagesListBox, @())

        if ([string]::IsNullOrWhiteSpace($controls.SourceIsoTextBox.Text)) {
            $appendMediaLog.Invoke('Source ISO is required before discovery.')
            return
        }

        if (-not (Test-Path -LiteralPath $controls.SourceIsoTextBox.Text)) {
            $appendMediaLog.Invoke("Source ISO does not exist: $($controls.SourceIsoTextBox.Text)")
            return
        }

        $mount = $null
        try {
            $appendMediaLog.Invoke("Mounting source ISO: $($controls.SourceIsoTextBox.Text)")

            $mountWindowsInstallMediaParams = @{
                Path = $controls.SourceIsoTextBox.Text
            }
            $mount = Mount-WindowsInstallMedia @mountWindowsInstallMediaParams

            $getInstallImagePathParams = @{
                MediaRoot = $mount.DriveRoot
            }
            $imagePath = & $invokeGetInstallImagePath @getInstallImagePathParams

            $getWindowsInstallImageParams = @{
                ImagePath = $imagePath
            }
            $images = @(Get-WindowsInstallImage @getWindowsInstallImageParams)

            $displayLines = [System.Collections.Generic.List[string]]::new()
            foreach ($image in $images) {
                $displayLine = '[{0}] {1} | {2}' -f $image.Index, $image.Name, $image.Description
                $displayLines.Add($displayLine)
            }

            $setListBoxItems.Invoke($controls.ImagesListBox, @($displayLines.ToArray()))
            $appendMediaLog.Invoke("Discovered $($images.Count) source image index(es).")
            $controls.StatusText.Text = "Discovered $($images.Count) source image index(es)."
        }
        catch {
            $appendMediaLog.Invoke("Image discovery failed. $($_.Exception.Message)")
            $controls.StatusText.Text = 'Image discovery failed.'
        }
        finally {
            if ($mount) {
                $appendMediaLog.Invoke('Dismounting source ISO...')

                $dismountWindowsInstallMediaParams = @{
                    InputObject = $mount
                }
                Dismount-WindowsInstallMedia @dismountWindowsInstallMediaParams
            }
        }
    }.GetNewClosure())

    $controls.RunSplitButton.AddClick({
        try {
            $appendLog.Invoke('Preparing execution plan...')

            $inputState = @{
                SourceIso = $controls.SourceIsoTextBox.Text
                EditionNames = $controls.EditionNamesTextBox.Text
                WorkingRoot = $controls.WorkingRootTextBox.Text
                OutputRoot = $controls.OutputRootTextBox.Text
                OutputBaseName = $controls.OutputBaseNameTextBox.Text
                LabelPrefix = $controls.LabelPrefixTextBox.Text
                SkipBootableIso = [bool] $controls.SkipBootableIsoCheckBox.IsChecked
                IncludeDesktopExperience = [bool] $controls.DesktopExperienceCheckBox.IsChecked
                IncludeServerCore = [bool] $controls.ServerCoreCheckBox.IsChecked
            }

            $getSplitterUiExecutionPlanParams = @{
                InputState = $inputState
            }
            $plan = & $invokeGetSplitterUiExecutionPlan @getSplitterUiExecutionPlanParams

            $appendLog.Invoke('Running Split-WindowsInstallMedia...')
            $appendLog.Invoke(("Editions: {0}" -f ($plan.EditionNames -join ', ')))

            $invokeSplitterUiSplitParams = @{
                SplitParams = $plan.SplitParams
            }
            $execution = & $invokeInvokeSplitterUiSplit @invokeSplitterUiSplitParams

            foreach ($verboseLine in $execution.VerboseLines) {
                $appendLog.Invoke($verboseLine)
            }

            $resultLines = [System.Collections.Generic.List[string]]::new()
            foreach ($result in $execution.Results) {
                $isoState = if ($result.IsoBuilt) { 'ISO built' } else { 'Media only' }
                $resultLine = '{0} | {1} | {2}' -f $result.Edition, $isoState, $result.OutputIso
                $resultLines.Add($resultLine)
            }

            $setListBoxItems.Invoke($controls.ResultsListBox, @($resultLines.ToArray()))

            if ($execution.Results.Count -gt 0) {
                $controls.StatusText.Text = "Completed split for $($execution.Results.Count) edition(s)."
                $appendLog.Invoke($controls.StatusText.Text)
            }
            else {
                $controls.StatusText.Text = 'Split finished with no result objects returned.'
                $appendLog.Invoke($controls.StatusText.Text)
            }
        }
        catch {
            $controls.StatusText.Text = 'Split failed.'
            $appendLog.Invoke("Split failed. $($_.Exception.Message)")
        }
    }.GetNewClosure())

    if ($controls.AddDriverButton) {
        $controls.AddDriverButton.AddClick({
            try {
                if ([string]::IsNullOrWhiteSpace($controls.ServiceMediaRootTextBox.Text)) {
                    throw 'Servicing media root is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.DriverPathTextBox.Text)) {
                    $selectedPath = $selectFolderPath.Invoke('Select Driver Folder', $controls.DriverPathTextBox.Text)
                    if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
                        $controls.DriverPathTextBox.Text = $selectedPath
                        $appendServicingLog.Invoke("Selected driver path: $selectedPath")
                        $addServicingSelectionItem.Invoke("Selected driver path: $selectedPath")
                    }
                }

                if ([string]::IsNullOrWhiteSpace($controls.DriverPathTextBox.Text)) {
                    throw 'Driver path is required.'
                }

                $indexes = $getIndexListFromText.Invoke($controls.ServiceIndexesTextBox.Text)
                $addWindowsDriverParams = @{
                    MediaRoot = $controls.ServiceMediaRootTextBox.Text
                    DriverPath = $controls.DriverPathTextBox.Text
                    Recurse = [bool] $controls.ServiceRecurseDriversCheckBox.IsChecked
                    ForceUnsigned = [bool] $controls.ServiceForceUnsignedDriversCheckBox.IsChecked
                    Verbose = $true
                }

                if ($indexes.Count -gt 0) {
                    $addWindowsDriverParams.Index = $indexes
                }

                $appendServicingLog.Invoke('Adding driver(s) to selected image indexes...')
                $driverStream = & { Add-WindowsDriver @addWindowsDriverParams } 4>&1
                $appendStreamLines.Invoke(@($driverStream), $appendServicingLog)
                $controls.StatusText.Text = 'Driver servicing completed.'
                $appendServicingLog.Invoke($controls.StatusText.Text)
                $addServicingSelectionItem.Invoke("Added drivers from $($controls.DriverPathTextBox.Text)")
            }
            catch {
                $controls.StatusText.Text = 'Driver servicing failed.'
                $appendServicingLog.Invoke("Driver servicing failed. $($_.Exception.Message)")
            }
        }.GetNewClosure())
    }

    if ($controls.AddPackageButton) {
        $controls.AddPackageButton.AddClick({
            try {
                if ([string]::IsNullOrWhiteSpace($controls.ServiceMediaRootTextBox.Text)) {
                    throw 'Servicing media root is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.PackagePathTextBox.Text)) {
                    $selectedPath = $selectFilePath.Invoke('Select Package File', $controls.PackagePathTextBox.Text)
                    if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
                        $controls.PackagePathTextBox.Text = $selectedPath
                        $appendServicingLog.Invoke("Selected package path: $selectedPath")
                        $addServicingSelectionItem.Invoke("Selected package path: $selectedPath")
                    }
                }

                if ([string]::IsNullOrWhiteSpace($controls.PackagePathTextBox.Text)) {
                    throw 'Package path is required.'
                }

                $indexes = $getIndexListFromText.Invoke($controls.ServiceIndexesTextBox.Text)
                $addWindowsPackageParams = @{
                    MediaRoot = $controls.ServiceMediaRootTextBox.Text
                    PackagePath = $controls.PackagePathTextBox.Text
                    IgnoreCheck = [bool] $controls.ServiceIgnoreCheckCheckBox.IsChecked
                    PreventPending = [bool] $controls.ServicePreventPendingCheckBox.IsChecked
                    Verbose = $true
                }

                if ($indexes.Count -gt 0) {
                    $addWindowsPackageParams.Index = $indexes
                }

                $appendServicingLog.Invoke('Adding package(s) to selected image indexes...')
                $packageStream = & { Add-WindowsPackage @addWindowsPackageParams } 4>&1
                $appendStreamLines.Invoke(@($packageStream), $appendServicingLog)
                $controls.StatusText.Text = 'Package servicing completed.'
                $appendServicingLog.Invoke($controls.StatusText.Text)
                $addServicingSelectionItem.Invoke("Added package $($controls.PackagePathTextBox.Text)")
            }
            catch {
                $controls.StatusText.Text = 'Package servicing failed.'
                $appendServicingLog.Invoke("Package servicing failed. $($_.Exception.Message)")
            }
        }.GetNewClosure())
    }

    if ($controls.AddUnattendButton) {
        $controls.AddUnattendButton.AddClick({
            try {
                if ([string]::IsNullOrWhiteSpace($controls.ServiceMediaRootTextBox.Text)) {
                    throw 'Servicing media root is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.UnattendPathTextBox.Text)) {
                    $selectedPath = $selectFilePath.Invoke('Select Unattend XML', $controls.UnattendPathTextBox.Text)
                    if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
                        $controls.UnattendPathTextBox.Text = $selectedPath
                        $appendServicingLog.Invoke("Selected unattend path: $selectedPath")
                        $addServicingSelectionItem.Invoke("Selected unattend path: $selectedPath")
                    }
                }

                if ([string]::IsNullOrWhiteSpace($controls.UnattendPathTextBox.Text)) {
                    throw 'Unattend XML path is required.'
                }

                $addUnattendFileParams = @{
                    MediaRoot = $controls.ServiceMediaRootTextBox.Text
                    UnattendPath = $controls.UnattendPathTextBox.Text
                    DestinationRelativePath = $controls.UnattendDestinationTextBox.Text
                    Verbose = $true
                }

                $appendServicingLog.Invoke('Adding unattend file...')
                $unattendStream = & { Add-UnattendFile @addUnattendFileParams } 4>&1
                $appendStreamLines.Invoke(@($unattendStream), $appendServicingLog)
                $controls.StatusText.Text = 'Unattend copy completed.'
                $appendServicingLog.Invoke($controls.StatusText.Text)
                $addServicingSelectionItem.Invoke("Added unattend $($controls.UnattendPathTextBox.Text) to $($controls.UnattendDestinationTextBox.Text)")
            }
            catch {
                $controls.StatusText.Text = 'Unattend copy failed.'
                $appendServicingLog.Invoke("Unattend copy failed. $($_.Exception.Message)")
            }
        }.GetNewClosure())
    }

    if ($controls.AddPayloadFileButton) {
        $controls.AddPayloadFileButton.AddClick({
            try {
                if ([string]::IsNullOrWhiteSpace($controls.ServiceMediaRootTextBox.Text)) {
                    throw 'Servicing media root is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.PayloadSourceTextBox.Text)) {
                    $selectedPath = $selectFilePath.Invoke('Select Payload File', $controls.PayloadSourceTextBox.Text)
                    if (-not [string]::IsNullOrWhiteSpace($selectedPath)) {
                        $controls.PayloadSourceTextBox.Text = $selectedPath
                        $appendServicingLog.Invoke("Selected payload source: $selectedPath")
                        $addServicingSelectionItem.Invoke("Selected payload source: $selectedPath")
                    }
                }

                if ([string]::IsNullOrWhiteSpace($controls.PayloadSourceTextBox.Text)) {
                    throw 'Payload source file path is required.'
                }

                $sourcePath = $controls.PayloadSourceTextBox.Text
                if (-not (Test-Path -LiteralPath $sourcePath -PathType Leaf)) {
                    throw "Payload source file does not exist: $sourcePath"
                }

                $destinationRelativePath = $controls.PayloadDestinationTextBox.Text
                if ([string]::IsNullOrWhiteSpace($destinationRelativePath)) {
                    throw 'Payload destination relative path is required.'
                }

                $destinationPath = Join-Path $controls.ServiceMediaRootTextBox.Text $destinationRelativePath
                $destinationDirectory = Split-Path -Path $destinationPath -Parent

                $newItemParams = @{
                    Path = $destinationDirectory
                    ItemType = 'Directory'
                    Force = $true
                }
                New-Item @newItemParams | Out-Null

                $copyItemParams = @{
                    LiteralPath = $sourcePath
                    Destination = $destinationPath
                    Force = $true
                }

                $appendServicingLog.Invoke('Copying payload file into media...')
                Copy-Item @copyItemParams
                $controls.StatusText.Text = 'Payload file copy completed.'
                $appendServicingLog.Invoke("Payload copied to $destinationRelativePath")
                $addServicingSelectionItem.Invoke("Added payload $(Split-Path -Path $sourcePath -Leaf) to $destinationRelativePath")
            }
            catch {
                $controls.StatusText.Text = 'Payload file copy failed.'
                $appendServicingLog.Invoke("Payload file copy failed. $($_.Exception.Message)")
            }
        }.GetNewClosure())
    }

    if ($controls.BuildBootableIsoButton) {
        $controls.BuildBootableIsoButton.AddClick({
            try {
                if ([string]::IsNullOrWhiteSpace($controls.BuildMediaRootTextBox.Text)) {
                    throw 'Build media root is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.BuildOutputIsoTextBox.Text)) {
                    throw 'Output ISO path is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.BuildLabelTextBox.Text)) {
                    throw 'ISO label is required.'
                }

                $buildBootableIsoParams = @{
                    MediaRoot = $controls.BuildMediaRootTextBox.Text
                    OutputIso = $controls.BuildOutputIsoTextBox.Text
                    Label = $controls.BuildLabelTextBox.Text
                    Verbose = $true
                }

                $appendBuildLog.Invoke('Building bootable ISO...')
                $buildIsoStream = & { Build-BootableIso @buildBootableIsoParams } 4>&1
                $appendStreamLines.Invoke(@($buildIsoStream), $appendBuildLog)
                $controls.StatusText.Text = 'Bootable ISO build completed.'
                $appendBuildLog.Invoke($controls.StatusText.Text)
            }
            catch {
                $controls.StatusText.Text = 'Bootable ISO build failed.'
                $appendBuildLog.Invoke("Bootable ISO build failed. $($_.Exception.Message)")
            }
        }.GetNewClosure())
    }

    if ($controls.ExportInstallImageButton) {
        $controls.ExportInstallImageButton.AddClick({
            try {
                if ([string]::IsNullOrWhiteSpace($controls.BuildSourceImageTextBox.Text)) {
                    throw 'Source image path is required.'
                }

                if ([string]::IsNullOrWhiteSpace($controls.BuildDestinationWimTextBox.Text)) {
                    throw 'Destination WIM path is required.'
                }

                $selectedIndexes = $getIndexListFromText.Invoke($controls.BuildIndexesTextBox.Text)
                $getWindowsInstallImageParams = @{
                    ImagePath = $controls.BuildSourceImageTextBox.Text
                }
                $images = @(Get-WindowsInstallImage @getWindowsInstallImageParams)

                if ($selectedIndexes.Count -gt 0) {
                    $images = @($images | Where-Object { $_.Index -in $selectedIndexes })
                }

                if ($images.Count -eq 0) {
                    throw 'No matching image indexes were found for export.'
                }

                $appendBuildLog.Invoke('Exporting selected install image indexes...')
                $exportWindowsInstallImageParams = @{
                    SourceImagePath = $controls.BuildSourceImageTextBox.Text
                    DestinationWim = $controls.BuildDestinationWimTextBox.Text
                    Image = $images
                    Verbose = $true
                }
                $exportStream = & { Export-WindowsInstallImage @exportWindowsInstallImageParams } 4>&1
                $appendStreamLines.Invoke(@($exportStream), $appendBuildLog)
                $controls.StatusText.Text = "Install image export completed for $($images.Count) index(es)."
                $appendBuildLog.Invoke($controls.StatusText.Text)
            }
            catch {
                $controls.StatusText.Text = 'Install image export failed.'
                $appendBuildLog.Invoke("Install image export failed. $($_.Exception.Message)")
            }
        }.GetNewClosure())
    }

    $controls.FooterText.Text = 'Preflight checks help avoid ADK, DISM, and path issues before long-running operations.'
}