EnhancedFileManagerAO.psm1

#Region '.\Private\Check-DiskSpace.ps1' -1

function Check-DiskSpace {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path,
        [Parameter(Mandatory = $true)]
        [int]$RequiredSpaceGB
    )

    begin {
        Write-EnhancedLog -Message "Starting Check-DiskSpace function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    process {
        try {
            $drive = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -eq ([System.IO.Path]::GetPathRoot($Path)) }
            if ($drive -and ($drive.Free -lt ($RequiredSpaceGB * 1GB))) {
                Write-EnhancedLog -Message "Not enough disk space on drive $($drive.Root). Required: $RequiredSpaceGB GB, Available: $([math]::round($drive.Free / 1GB, 2)) GB." -Level "ERROR"
                throw "Not enough disk space on drive $($drive.Root)."
            } else {
                Write-EnhancedLog -Message "Sufficient disk space available on drive $($drive.Root)." -Level "INFO"
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred during disk space check: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    end {
        Write-EnhancedLog -Message "Check-DiskSpace function execution completed." -Level "Notice"
    }
}
#EndRegion '.\Private\Check-DiskSpace.ps1' 35
#Region '.\Private\Handle-RobocopyExitCode.ps1' -1

function Handle-RobocopyExitCode {
    <#
    .SYNOPSIS
    Handles the exit code from a Robocopy operation.
 
    .DESCRIPTION
    The Handle-RobocopyExitCode function interprets the exit code returned by a Robocopy operation and logs an appropriate message. The exit codes provide information about the success or failure of the operation and any additional conditions encountered.
 
    .PARAMETER ExitCode
    The exit code returned by Robocopy.
 
    .EXAMPLE
    Handle-RobocopyExitCode -ExitCode 0
    Logs a message indicating no files were copied, no files were mismatched, and no failures were encountered.
 
    .EXAMPLE
    Handle-RobocopyExitCode -ExitCode 1
    Logs a message indicating all files were copied successfully.
 
    .NOTES
    This function is typically used internally after executing a Robocopy command to handle and log the exit status.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [int]$ExitCode
    )

    begin {
        Write-EnhancedLog -Message "Starting Handle-RobocopyExitCode function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    process {
        try {
            switch ($ExitCode) {
                0 { Write-EnhancedLog -Message "No files were copied. No files were mismatched. No failures were encountered." -Level "INFO" }
                1 { Write-EnhancedLog -Message "All files were copied successfully." -Level "INFO" }
                2 { Write-EnhancedLog -Message "There are some additional files in the destination directory that are not present in the source directory. No files were copied." -Level "INFO" }
                3 { Write-EnhancedLog -Message "Some files were copied. Additional files were present. No failure was encountered." -Level "INFO" }
                4 { Write-EnhancedLog -Message "Some files were mismatched. No files were copied." -Level "INFO" }
                5 { Write-EnhancedLog -Message "Some files were copied. Some files were mismatched. No failure was encountered." -Level "INFO" }
                6 { Write-EnhancedLog -Message "Additional files and mismatched files exist. No files were copied." -Level "INFO" }
                7 { Write-EnhancedLog -Message "Files were copied, a file mismatch was present, and additional files were present." -Level "INFO" }
                8 { Write-EnhancedLog -Message "Several files did not copy." -Level "ERROR" }
                default { Write-EnhancedLog -Message "Robocopy failed with exit code $ExitCode" -Level "ERROR" }
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while handling the Robocopy exit code: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    end {
        Write-EnhancedLog -Message "Handle-RobocopyExitCode function execution completed." -Level "Notice"
    }
}
#EndRegion '.\Private\Handle-RobocopyExitCode.ps1' 61
#Region '.\Private\Test-Directory.ps1' -1

function Test-Directory {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path
    )

    begin {
        Write-EnhancedLog -Message "Starting Test-Directory function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    process {
        try {
            if (-Not (Test-Path -Path $Path -PathType Container)) {
                Write-EnhancedLog -Message "The path '$Path' is not a valid directory." -Level "ERROR"
                throw "The path '$Path' is not a valid directory."
            } else {
                Write-EnhancedLog -Message "The path '$Path' is a valid directory." -Level "INFO"
            }
        } catch {
            Write-EnhancedLog -Message "An error occurred during directory validation: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    end {
        Write-EnhancedLog -Message "Test-Directory function execution completed." -Level "Notice"
    }
}
#EndRegion '.\Private\Test-Directory.ps1' 32
#Region '.\Private\Test-Robocopy.ps1' -1


function Test-Robocopy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]$RobocopyPath = "C:\Windows\System32\Robocopy.exe"
    )

    begin {
        Write-EnhancedLog -Message "Starting Test-Robocopy function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    process {
        try {
            if (-Not (Test-Path -Path $RobocopyPath)) {
                Write-EnhancedLog -Message "Robocopy.exe is not available at the specified path: $RobocopyPath" -Level "ERROR"
                throw "Robocopy.exe is not available at the specified path: $RobocopyPath"
            }
            else {
                Write-EnhancedLog -Message "Robocopy.exe is available at the specified path: $RobocopyPath" -Level "INFO"
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred during Robocopy availability check: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    end {
        Write-EnhancedLog -Message "Test-Robocopy function execution completed." -Level "Notice"
    }
}
#EndRegion '.\Private\Test-Robocopy.ps1' 35
#Region '.\Public\Copy-FilesWithRobocopy.ps1' -1

function Copy-FilesWithRobocopy {
    <#
    .SYNOPSIS
    Copies files from a source directory to a destination directory using Robocopy and verifies the operation.
 
    .DESCRIPTION
    The Copy-FilesWithRobocopy function copies files from a source directory to a destination directory based on a specified file pattern using Robocopy. It validates the source and destination directories, checks disk space, logs the Robocopy process, and verifies the copy operation. It also handles locked files by finding and killing the locking processes.
 
    .PARAMETER Source
    The source directory from which files will be copied.
 
    .PARAMETER Destination
    The destination directory to which files will be copied.
 
    .PARAMETER FilePattern
    The file pattern to match files that should be copied. If not provided, defaults to '*'.
 
    .PARAMETER RetryCount
    The number of retries if a copy fails. Default is 2.
 
    .PARAMETER WaitTime
    The wait time between retries in seconds. Default is 5.
 
    .PARAMETER RequiredSpaceGB
    The required free space in gigabytes at the destination. Default is 10 GB.
 
    .PARAMETER Exclude
    The directories or files to exclude from the copy operation.
 
    .EXAMPLE
    Copy-FilesWithRobocopy -Source "C:\Source" -Destination "C:\Destination" -FilePattern "*.txt"
    Copies all .txt files from C:\Source to C:\Destination.
 
    .EXAMPLE
    "*.txt", "*.log" | Copy-FilesWithRobocopy -Source "C:\Source" -Destination "C:\Destination"
    Copies all .txt and .log files from C:\Source to C:\Destination using pipeline input for the file patterns.
 
    .EXAMPLE
    Copy-FilesWithRobocopy -Source "C:\Source" -Destination "C:\Destination" -Exclude ".git"
    Copies files from C:\Source to C:\Destination excluding the .git folder.
 
    .NOTES
    This function relies on the following private functions:
    - Check-DiskSpace.ps1
    - Handle-RobocopyExitCode.ps1
    - Test-Directory.ps1
    - Test-Robocopy.ps1
    - Verify-CopyOperation.ps1
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Source,
        [Parameter(Mandatory = $true)]
        [string]$Destination,
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]$FilePattern = '*',  # Default to '*' if not provided
        [Parameter(Mandatory = $false)]
        [int]$RetryCount = 2,
        [Parameter(Mandatory = $false)]
        [int]$WaitTime = 5,
        [Parameter(Mandatory = $false)]
        [int]$RequiredSpaceGB = 10, # Example value for required space
        [Parameter(Mandatory = $false)]
        [string[]]$Exclude
    )

    begin {
        Write-EnhancedLog -Message "Starting Copy-FilesWithRobocopy function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Validate Robocopy, source, and destination directories
        try {
            Test-Robocopy
            Test-Directory -Path $Source
            Write-EnhancedLog -Message "Validated source directory: $Source" -Level "INFO"

            Test-Directory -Path $Destination
            Write-EnhancedLog -Message "Validated destination directory: $Destination" -Level "INFO"
        }
        catch {
            Write-EnhancedLog -Message "Critical error during validation of source or destination directories or Robocopy validation." -Level "CRITICAL"
            Handle-Error -ErrorRecord $_
            throw $_
        }

        # Check disk space
        try {
            Check-DiskSpace -Path $Destination -RequiredSpaceGB $RequiredSpaceGB
        }
        catch {
            Write-EnhancedLog -Message "Critical error during disk space validation." -Level "CRITICAL"
            Handle-Error -ErrorRecord $_
            throw $_
        }

        # Prepare Robocopy log file path
        $timestamp = Get-Date -Format "yyyyMMddHHmmss"
        $logFilePath = "$env:TEMP\RobocopyLog_$timestamp.log"
        Write-EnhancedLog -Message "Robocopy log file will be saved to: $logFilePath" -Level "INFO"
    }

    process {
        try {
            $robocopyPath = "C:\Windows\System32\Robocopy.exe"
            $robocopyArgs = @(
                "`"$Source`"",
                "`"$Destination`"",
                $FilePattern,
                "/E",
                "/R:$RetryCount",
                "/W:$WaitTime",
                "/LOG:`"$logFilePath`""
            )

            # Add exclude arguments if provided
            if ($Exclude) {
                $excludeDirs = $Exclude | ForEach-Object { "/XD `"$($_)`"" }
                $excludeFiles = $Exclude | ForEach-Object { "/XF `"$($_)`"" }
                $robocopyArgs = $robocopyArgs + $excludeDirs + $excludeFiles

                # Log what is being excluded
                foreach ($item in $Exclude) {
                    Write-EnhancedLog -Message "Excluding: $item" -Level "INFO"
                }
            }

            Write-EnhancedLog -Message "Starting Robocopy process with arguments: $robocopyArgs" -Level "INFO"

            # Splatting Start-Process parameters
            $startProcessParams = @{
                FilePath     = $robocopyPath
                ArgumentList = $robocopyArgs
                NoNewWindow  = $true
                Wait         = $true
                PassThru     = $true
            }

            $process = Start-Process @startProcessParams

            if ($process.ExitCode -ne 0) {
                Write-EnhancedLog -Message "Robocopy process failed with exit code: $($process.ExitCode)" -Level "CRITICAL"
            }

            Handle-RobocopyExitCode -ExitCode $process.ExitCode
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while copying files with Robocopy: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_

            # Check if the error is due to a file being used by another process
            if ($_.Exception -match "because it is being used by another process") {
                Write-EnhancedLog -Message "Attempting to find and kill the process locking the file." -Level "WARNING"
                try {
                    # Find the process locking the file
                    # $lockedFile = $_.Exception.Message -match "'(.+?)'" | Out-Null
                    # $lockedFile = $matches[1]


                    $lockingProcesses = Get-LockingProcess -FilePath $LockedFile -HandlePath "C:\ProgramData\SystemTools\handle64.exe"
                    $lockedfile = $lockingProcesses.FilePath

                    # Kill the processes locking the file
                    Kill-LockingProcesses -LockedFile $lockedFile

                    # Retry the Robocopy operation
                    Write-EnhancedLog -Message "Retrying Robocopy operation after killing the locking process." -Level "INFO"
                    $process = Start-Process @startProcessParams

                    if ($process.ExitCode -ne 0) {
                        Write-EnhancedLog -Message "Robocopy process failed again with exit code: $($process.ExitCode)" -Level "CRITICAL"
                    }

                    Handle-RobocopyExitCode -ExitCode $process.ExitCode
                    Write-EnhancedLog -Message "Copy operation retried and succeeded." -Level "INFO"
                }
                catch {
                    Write-EnhancedLog -Message "Failed to find or kill the process locking the file: $lockedFile" -Level "ERROR"
                    Handle-Error -ErrorRecord $_
                    throw $_
                }
            }
            else {
                throw $_
            }
        }
    }

    end {
        Write-EnhancedLog -Message "Verifying copied files..." -Level "Notice"

        # Call Verify-CopyOperation to ensure the files were copied correctly
        Verify-CopyOperation -SourcePath $Source -DestinationPath $Destination

        Write-EnhancedLog -Message "Copy-FilesWithRobocopy function execution completed." -Level "Notice"
    }
}

# # Example usage
# $sourcePath = "C:\Source"
# $destinationPath = "C:\Destination"

# Copy-FilesWithRobocopy -Source $sourcePath -Destination $destinationPath
#EndRegion '.\Public\Copy-FilesWithRobocopy.ps1' 205
#Region '.\Public\Copy-FileToPublicAndTemp.ps1' -1

function Copy-FileToPublicAndTemp {
    <#
    .SYNOPSIS
    Copies a specified file to the public desktop and C:\temp, making it available to all users and in the temp directory.
 
    .DESCRIPTION
    This function copies a specified file to the public desktop and C:\temp, ensuring it is available to all users and also in the temp directory. Enhanced logging is used for feedback and error handling.
 
    .PARAMETER SourceFilePath
    The path of the file to be copied.
 
    .EXAMPLE
    Copy-FileToPublicAndTemp -SourceFilePath "C:\Path\To\fcremove.exe"
 
    This example copies the file "fcremove.exe" to the public desktop and C:\temp, making it available to all users and in the temp directory.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$SourceFilePath
    )

    begin {
        Write-EnhancedLog -Message 'Starting Copy-FileToPublicAndTemp function' -Level 'INFO'
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    process {
        try {
            if (-not (Test-Path -Path $SourceFilePath)) {
                Write-EnhancedLog -Message "Source file not found: $SourceFilePath" -Level "ERROR"
                throw "Source file not found: $SourceFilePath"
            }

            # Define the destination paths
            $publicDesktopPath = 'C:\Users\Public\Desktop'
            $tempPath = 'C:\temp'

            # Ensure the public desktop directory exists
            $publicDesktopParams = @{
                Path      = $publicDesktopPath
                ItemType  = 'Directory'
                Force     = $true
                ErrorAction = 'Stop'
            }
            if (-not (Test-Path -Path $publicDesktopPath)) {
                Write-EnhancedLog -Message "Public desktop path not found. Creating directory." -Level "INFO"
                New-Item @publicDesktopParams | Out-Null
            }

            # Ensure the temp directory exists
            $tempParams = @{
                Path      = $tempPath
                ItemType  = 'Directory'
                Force     = $true
                ErrorAction = 'Stop'
            }
            if (-not (Test-Path -Path $tempPath)) {
                Write-EnhancedLog -Message "Temp path not found. Creating directory." -Level "INFO"
                New-Item @tempParams | Out-Null
            }

            # Copy the file to the public desktop
            $destinationFilePathPublic = Join-Path -Path $publicDesktopPath -ChildPath (Split-Path -Leaf $SourceFilePath)
            $copyParamsPublic = @{
                Path        = $SourceFilePath
                Destination = $destinationFilePathPublic
                Force       = $true
                ErrorAction = 'Stop'
            }
            Write-EnhancedLog -Message "Copying file to: $destinationFilePathPublic" -Level "INFO"
            Copy-Item @copyParamsPublic
            Write-EnhancedLog -Message "File copied to: $destinationFilePathPublic" -Level "INFO"

            # Copy the file to the temp directory
            $destinationFilePathTemp = Join-Path -Path $tempPath -ChildPath (Split-Path -Leaf $SourceFilePath)
            $copyParamsTemp = @{
                Path        = $SourceFilePath
                Destination = $destinationFilePathTemp
                Force       = $true
                ErrorAction = 'Stop'
            }
            Write-EnhancedLog -Message "Copying file to: $destinationFilePathTemp" -Level "INFO"
            Copy-Item @copyParamsTemp
            Write-EnhancedLog -Message "File copied to: $destinationFilePathTemp" -Level "INFO"

        } catch {
            Write-EnhancedLog -Message "An error occurred while copying the file: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    end {
        Write-EnhancedLog -Message 'Copy-FileToPublicAndTemp function completed' -Level 'INFO'
    }
}
#EndRegion '.\Public\Copy-FileToPublicAndTemp.ps1' 98
#Region '.\Public\Download-Handle.ps1' -1

function Download-Handle {
    <#
    .SYNOPSIS
    Downloads and extracts Handle64.exe from the official Sysinternals Handle package.
 
    .DESCRIPTION
    The Download-Handle function downloads the Handle package from the official Sysinternals website, extracts Handle64.exe, and places it in the specified target folder.
 
    .PARAMETER TargetFolder
    The target folder where Handle64.exe will be stored.
 
    .EXAMPLE
    $params = @{
        TargetFolder = "C:\ProgramData\SystemTools"
    }
    Download-Handle @params
    Downloads Handle64.exe to the specified target folder.
    #>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$TargetFolder
    )

    Begin {
        Write-EnhancedLog -Message "Starting Download-Handle function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        # Ensure the target folder exists
        Ensure-TargetFolderExists -TargetFolder $TargetFolder
        Write-EnhancedLog -Message "Removing existing Handle from target folder: $TargetFolder" -Level "INFO"
        
        Remove-ExistingHandle -TargetFolder $TargetFolder
    }

    Process {
        try {
            # Define the URL for Handle download
            $url = "https://download.sysinternals.com/files/Handle.zip"
            # Full path for the downloaded file
            $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
            $zipPath = Join-Path -Path $TargetFolder -ChildPath "Handle_$timestamp.zip"

            # Download the Handle.zip file with retry logic
            Write-EnhancedLog -Message "Downloading Handle.zip from: $url to: $zipPath" -Level "INFO"
            
            $downloadParams = @{
                Source      = $url
                Destination = $zipPath
                MaxRetries  = 3
            }
            Start-FileDownloadWithRetry @downloadParams

            # Extract Handle64.exe from the zip file
            Write-EnhancedLog -Message "Extracting Handle.zip to: $TargetFolder\Handle" -Level "INFO"
            Expand-Archive -Path $zipPath -DestinationPath "$TargetFolder\Handle" -Force

            # Specific extraction of Handle64.exe
            $extractedFolderPath = Join-Path -Path $TargetFolder -ChildPath "Handle"
            $Handle64Path = Join-Path -Path $extractedFolderPath -ChildPath "Handle64.exe"
            $finalPath = Join-Path -Path $TargetFolder -ChildPath "Handle64.exe"

            # Move Handle64.exe to the desired location
            if (Test-Path -Path $Handle64Path) {
                Write-EnhancedLog -Message "Moving Handle64.exe from: $Handle64Path to: $finalPath" -Level "INFO"
                Move-Item -Path $Handle64Path -Destination $finalPath

                # Remove the downloaded zip file and extracted folder
                Write-EnhancedLog -Message "Removing downloaded zip file and extracted folder" -Level "INFO"
                Remove-Item -Path $zipPath -Force
                Remove-Item -Path $extractedFolderPath -Recurse -Force

                Write-EnhancedLog -Message "Handle64.exe has been successfully downloaded and moved to: $finalPath" -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "Handle64.exe not found in the extracted files." -Level "ERROR"
                throw "Handle64.exe not found after extraction."
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred in Download-Handle function: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Download-Handle function" -Level "Notice"
    }
}

# Example usage
# $params = @{
# TargetFolder = "C:\ProgramData\SystemTools"
# }
# Download-Handle @params
#EndRegion '.\Public\Download-Handle.ps1' 98
#Region '.\Public\Find-LockingProcesses-Archive.ps1' -1

# function Find-LockingProcesses {
# <#
# .SYNOPSIS
# Finds processes that are locking a specified file.

# .DESCRIPTION
# The Find-LockingProcesses function identifies processes that are locking a specified file by checking the modules loaded by each process.

# .PARAMETER LockedFile
# The path to the locked file.

# .EXAMPLE
# $lockingProcesses = Find-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt"
# Finds processes locking the specified file and returns the processes.

# .NOTES
# This function relies on the Get-Process cmdlet and its ability to enumerate loaded modules.
# #>

# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$LockedFile
# )

# Begin {
# Write-EnhancedLog -Message "Starting Find-LockingProcesses function" -Level "Notice"
# Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
# }

# Process {
# try {
# Write-EnhancedLog -Message "Finding processes locking file: $LockedFile" -Level "INFO"
# $lockingProcesses = Get-Process | Where-Object { $_.Modules | Where-Object { $_.FileName -eq $LockedFile } }
# if ($lockingProcesses) {
# foreach ($process in $lockingProcesses) {
# Write-EnhancedLog -Message "Process locking the file: $($process.ProcessName) (ID: $($process.Id))" -Level "INFO"
# }
# } else {
# Write-EnhancedLog -Message "No processes found locking the file: $LockedFile" -Level "INFO"
# }
# return $lockingProcesses
# }
# catch {
# Write-EnhancedLog -Message "An error occurred while finding locking processes: $($_.Exception.Message)" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Find-LockingProcesses function" -Level "Notice"
# }
# }

# # Example usage
# # $lockingProcesses = Find-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt"
#EndRegion '.\Public\Find-LockingProcesses-Archive.ps1' 57
#Region '.\Public\Get-LockingProcess.ps1' -1

function Get-LockingProcess {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$FilePath,

        [Parameter(Mandatory = $true)]
        [string]$HandlePath  # Path to handle64.exe
    )

    Begin {
        Write-EnhancedLog -Message "Starting Get-LockingProcess function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters

        $DownloadHandleParams = @{
            TargetFolder = "C:\ProgramData\SystemTools"
        }
        Download-Handle @DownloadHandleParams
        


        # Validate the HandlePath
        if (-not (Test-Path -Path $HandlePath)) {
            Write-EnhancedLog -Message "Handle executable not found at path: $HandlePath" -Level "ERROR"
            throw "Handle executable not found at path: $HandlePath"
        }

        Write-EnhancedLog -Message "Handle executable found at path: $HandlePath" -Level "INFO"
    }

    Process {
        try {
            Write-EnhancedLog -Message "Identifying processes locking file: $FilePath using Handle" -Level "INFO"
    
            # Run Handle and suppress copyright and EULA lines
            $handleOutput = &"$HandlePath" $FilePath 2>&1 | Where-Object {
                $_ -notmatch "Sysinternals|Copyright|Handle viewer|EULA" -and $_.Trim() -ne ""
            }
            $processes = @()
    
            if ($handleOutput) {
                Write-EnhancedLog -Message "Processing output from Handle" -Level "INFO"
    
                # Refine the parsing logic to ignore invalid lines
                foreach ($line in $handleOutput) {
                    if ($line -match '^\s*([^\s]+)\s+pid:\s*(\d+)\s+type:\s*\w+\s*(.*)$') {
                        $processName = $matches[1]
                        $processId = $matches[2]
    
                        # Add to the processes array
                        $processes += [PSCustomObject]@{
                            ProcessId   = $processId
                            ProcessName = $processName
                            FilePath    = $FilePath
                        }
    
                        Write-EnhancedLog -Message "Found locking process: ID = $processId, Name = $processName" -Level "INFO"
                    }
                }
            }
            else {
                Write-EnhancedLog -Message "No output received from Handle. No locking processes found for file: $FilePath" -Level "WARNING"
            }
    
            return $processes
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while identifying locking processes: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
    
    

    End {
        Write-EnhancedLog -Message "Exiting Get-LockingProcess function" -Level "Notice"
    }
}
#EndRegion '.\Public\Get-LockingProcess.ps1' 80
#Region '.\Public\Kill-LockingProcesses.ps1' -1

function Stop-ProcessTree {
    param (
        [int]$ParentId
    )

    # Get all child processes of the parent
    $childProcesses = Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ParentId }

    # Recursively stop all child processes
    foreach ($childProcess in $childProcesses) {
        Stop-ProcessTree -ParentId $childProcess.ProcessId
    }

    # Finally, stop the parent process
    Stop-Process -Id $ParentId -Force
}

# Usage example:
# Stop-ProcessTree -ParentId <ProcessId>


function Kill-LockingProcesses {
    <#
    .SYNOPSIS
    Kills processes that are locking a specified file.
 
    .DESCRIPTION
    The Kill-LockingProcesses function finds processes that are locking a specified file and terminates them forcefully.
 
    .PARAMETER LockedFile
    The path to the locked file.
 
    .EXAMPLE
    Kill-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt"
    Finds and kills processes locking the specified file.
 
    .NOTES
    This function relies on the Find-LockingProcesses function to identify locking processes.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$LockedFile
    )

    Begin {
        Write-EnhancedLog -Message "Starting Kill-LockingProcesses function" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        try {
            # Manage-LockingProcesses -FilePath $FilePath -HandlePath $HandlePath
            Manage-LockingProcesses -FilePath $LockedFile -HandlePath "C:\ProgramData\SystemTools\handle64.exe"
    
            Write-EnhancedLog -Message "Getting processes locking file: $LockedFile" -Level "INFO"
            $lockingProcesses = Get-LockingProcess -FilePath $LockedFile -HandlePath "C:\ProgramData\SystemTools\handle64.exe"
        
            if ($lockingProcesses) {
                foreach ($process in $lockingProcesses) {
                    $retryCount = 0
                    $processKilled = $false
    
                    while (-not $processKilled -and $retryCount -lt 3) {
                        try {
                            Write-EnhancedLog -Message "Attempting to kill process $($process.ProcessName) (ID: $($process.ProcessId)) locking the file $LockedFile using taskkill.exe" -Level "INFO"
    
                            # Try killing the process using taskkill.exe (with /T for tree)
                            & "C:\Windows\System32\taskkill.exe" /PID $process.ProcessId /F /T
    
                            Write-EnhancedLog -Message "Successfully killed process $($process.ProcessName) (ID: $($process.ProcessId)) using taskkill.exe" -Level "INFO"
                            $processKilled = $true
                        }
                        catch {
                            Write-EnhancedLog -Message "Failed to kill process $($process.ProcessName) (ID: $($process.ProcessId)) using taskkill.exe - $($_.Exception.Message)" -Level "WARNING"
    
                            # Retry using Stop-ProcessTree as fallback
                            if ($retryCount -eq 2) {
                                Write-EnhancedLog -Message "Attempting to kill process $($process.ProcessName) (ID: $($process.ProcessId)) using Stop-ProcessTree" -Level "WARNING"
                                Stop-ProcessTree -ParentId $process.ProcessId
                                Write-EnhancedLog -Message "Successfully killed process $($process.ProcessName) (ID: $($process.ProcessId)) using Stop-ProcessTree" -Level "INFO"
                                $processKilled = $true
                            }
                        }
    
                        # Wait for 5 seconds before retrying
                        if (-not $processKilled) {
                            Write-EnhancedLog -Message "Retrying to kill process $($process.ProcessName) (ID: $($process.ProcessId)) in 5 seconds..." -Level "INFO"
                            Start-Sleep -Seconds 5
                            $retryCount++
                        }
                    }
    
                    if (-not $processKilled) {
                        Write-EnhancedLog -Message "Failed to kill process $($process.ProcessName) (ID: $($process.ProcessId)) after 3 attempts." -Level "CRITICAL"
                    }
                }
            }
            else {
                Write-EnhancedLog -Message "No processes found locking the file: $LockedFile" -Level "INFO"
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while killing locking processes: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }
    
    

    End {
        Write-EnhancedLog -Message "Exiting Kill-LockingProcesses function" -Level "Notice"
    }
}

# Example usage
# Kill-LockingProcesses -LockedFile "C:\Path\To\LockedFile.txt"
#EndRegion '.\Public\Kill-LockingProcesses.ps1' 119
#Region '.\Public\Manage-LockingProcesses.ps1' -1

function Show-LockingProcessWarningForm {
    param (
        [array]$Processes,
        [string]$FilePath
    )
    
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing

    $form = New-Object system.windows.forms.Form
    $form.Text = "Processes Locking File"
    $form.Size = New-Object System.Drawing.Size(400, 300)
    $form.StartPosition = "CenterScreen"
    
    # Create a label for warning
    $label = New-Object system.windows.forms.Label
    $label.Text = "The following processes are locking the file: $FilePath. Please close them to proceed."
    $label.Size = New-Object System.Drawing.Size(360, 40)
    $label.Location = New-Object System.Drawing.Point(20, 20)
    $form.Controls.Add($label)

    # Create a listbox to show locking processes
    $listBox = New-Object system.windows.forms.ListBox
    $listBox.Size = New-Object System.Drawing.Size(350, 100)
    $listBox.Location = New-Object System.Drawing.Point(20, 70)
    $Processes | ForEach-Object { $listBox.Items.Add("Process: $($_.ProcessName) (ID: $($_.ProcessId))") }
    $form.Controls.Add($listBox)

    # Create a refresh button
    $refreshButton = New-Object system.windows.forms.Button
    $refreshButton.Text = "Refresh"
    $refreshButton.Location = New-Object System.Drawing.Point(220, 200)
    $refreshButton.Add_Click({
        $form.DialogResult = [System.Windows.Forms.DialogResult]::Retry
        $form.Close()
    })
    $form.Controls.Add($refreshButton)

    # Create a cancel button
    $cancelButton = New-Object system.windows.forms.Button
    $cancelButton.Text = "Cancel"
    $cancelButton.Location = New-Object System.Drawing.Point(120, 200)
    $cancelButton.Add_Click({
        $form.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
        $form.Close()
    })
    $form.Controls.Add($cancelButton)

    # Show the form
    $form.Topmost = $true
    return $form.ShowDialog()
}


function Manage-LockingProcesses {
    param (
        [string]$FilePath,
        [string]$HandlePath
    )

    try {
        Write-EnhancedLog -Message "Starting Manage-LockingProcesses function" -Level "NOTICE"

        # Loop until no locking processes are found
        do {
            # Get locking processes and ensure it's treated as an array
            $lockingProcesses = @(Get-LockingProcess -FilePath $FilePath -HandlePath $HandlePath)

            if ($lockingProcesses.Count -gt 0) {
                # Log and show locking processes
                Write-EnhancedLog -Message "Processes found locking the file: $FilePath" -Level "WARNING"
                $lockingProcesses | ForEach-Object {
                    Write-EnhancedLog -Message "Process: $($_.ProcessName) (ID: $($_.ProcessId))" -Level "WARNING"
                }

                # Show the Windows Form with process warning
                $result = Show-LockingProcessWarningForm -Processes $lockingProcesses -FilePath $FilePath

                # If user cancels, stop the script
                if ($result -eq [System.Windows.Forms.DialogResult]::Cancel) {
                    Write-EnhancedLog -Message "User canceled the process management." -Level "ERROR"
                    throw "User canceled the process management."
                }

            }
            else {
                Write-EnhancedLog -Message "No processes are locking the file: $FilePath" -Level "INFO"
                break
            }

            # Sleep before the next check
            Start-Sleep -Seconds 5

        } until ($lockingProcesses.Count -eq 0)

    }
    catch {
        Handle-Error -ErrorRecord $_
    }
    finally {
        Write-EnhancedLog -Message "Exiting Manage-LockingProcesses function" -Level "NOTICE"
    }
}



# Manage-LockingProcesses -FilePath $FilePath -HandlePath $HandlePath
#EndRegion '.\Public\Manage-LockingProcesses.ps1' 108
#Region '.\Public\Remove-EnhancedItem.ps1' -1

# function Remove-EnhancedItem {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$Path,

# [Parameter(Mandatory = $false)]
# [switch]$ForceKillProcesses, # Option to force kill processes locking the files

# [Parameter(Mandatory = $false)]
# [int]$MaxRetries = 3, # Maximum number of retries

# [Parameter(Mandatory = $false)]
# [int]$RetryInterval = 5 # Interval between retries in seconds
# )

# Begin {
# Write-EnhancedLog -Message "Starting Remove-EnhancedItem function for path: $Path" -Level "Notice"
# Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
# }

# Process {
# # Validate before removal
# $validationResultsBefore = Validate-PathExistsWithLogging -Paths $Path

# if ($validationResultsBefore.TotalValidatedFiles -gt 0) {
# $retryCount = 0
# $removedSuccessfully = $false

# while (-not $removedSuccessfully -and $retryCount -lt $MaxRetries) {
# try {
# Write-EnhancedLog -Message "Attempting to remove item: $Path (Attempt $($retryCount + 1) of $MaxRetries)" -Level "INFO"
# Remove-Item -Path $Path -Recurse -Force
# $removedSuccessfully = $true
# Write-EnhancedLog -Message "Successfully removed item: $Path" -Level "INFO"
# }
# catch {
# Write-EnhancedLog -Message "Error encountered while trying to remove item: $Path - $($_.Exception.Message)" -Level "ERROR"

# # Check if the error is due to the file being locked by another process
# if ($_.Exception.Message -match "being used by another process") {
# Write-EnhancedLog -Message "Identifying processes locking the file or directory..." -Level "WARNING"

# # Identify processes locking the file using Sysinternals Handle or similar tool
# $lockingProcessesParams = @{
# FilePath = $Path
# HandlePath = "C:\ProgramData\SystemTools\handle64.exe"
# }
# $lockingProcesses = Get-LockingProcess @lockingProcessesParams

# if ($lockingProcesses) {
# Write-EnhancedLog -Message "Processes locking the file or directory:" -Level "INFO"
# $lockingProcesses | ForEach-Object {
# Write-EnhancedLog -Message "Process ID: $($_.ProcessId), Process Name: $($_.ProcessName)" -Level "INFO"
# }

# # Optionally force kill processes
# if ($ForceKillProcesses) {
# $lockingProcesses | ForEach-Object {
# try {
# Write-EnhancedLog -Message "Attempting to kill process ID: $($_.ProcessId), Process Name: $($_.ProcessName)" -Level "WARNING"
# Stop-Process -Id $_.ProcessId -Force
# Write-EnhancedLog -Message "Successfully killed process ID: $($_.ProcessId), Process Name: $($_.ProcessName)" -Level "INFO"
# }
# catch {
# Write-EnhancedLog -Message "Failed to kill process ID: $($_.ProcessId), Process Name: $($_.ProcessName) - $($_.Exception.Message)" -Level "ERROR"
# }
# }
# }

# # Add a short delay before retrying to allow processes to terminate
# Start-Sleep -Seconds 2
# }
# else {
# Write-EnhancedLog -Message "No locking processes identified." -Level "WARNING"
# }
# }

# # Increment retry count and wait before retrying
# $retryCount++
# if ($retryCount -lt $MaxRetries) {
# Write-EnhancedLog -Message "Retrying removal in $RetryInterval seconds..." -Level "INFO"
# Start-Sleep -Seconds $RetryInterval
# }
# }
# }

# # Validate after removal
# $validationResultsAfter = Validate-PathExistsWithLogging -Paths $Path

# if ($removedSuccessfully -and $validationResultsAfter.TotalValidatedFiles -eq 0) {
# Write-EnhancedLog -Message "Item $Path successfully removed." -Level "CRITICAL"
# }
# else {
# Write-EnhancedLog -Message "Failed to remove item: $Path after $MaxRetries attempts." -Level "CRITICAL"
# throw "Failed to remove item: $Path after $MaxRetries attempts."
# }
# }
# else {
# Write-EnhancedLog -Message "Item $Path does not exist. No action taken." -Level "WARNING"
# }
# }

# End {
# Write-EnhancedLog -Message "Exiting Remove-EnhancedItem function" -Level "Notice"
# }
# }




function Remove-EnhancedItem {
    <#
    .SYNOPSIS
    Removes a file or directory with enhanced logging and retry mechanisms.
 
    .DESCRIPTION
    This function attempts to remove the specified file or directory, with the option to forcefully kill processes that may be locking the file. It retries removal for a specified number of attempts if the file is locked, with a customizable delay between retries.
 
    .PARAMETER Path
    The full path of the file or directory to be removed.
 
    .PARAMETER ForceKillProcesses
    A switch that, when enabled, forces the termination of any processes that are locking the specified file or directory.
 
    .PARAMETER MaxRetries
    The maximum number of times the function will attempt to remove the item if it fails due to a locking process or other error. The default is 3 retries.
 
    .PARAMETER RetryInterval
    The number of seconds to wait between retries. The default is 5 seconds.
 
    .EXAMPLE
    Remove-EnhancedItem -Path "C:\temp\open.txt"
 
    Attempts to remove the file at C:\temp\open.txt, with the default retry mechanism if the file is locked.
 
    .EXAMPLE
    Remove-EnhancedItem -Path "C:\temp\open.txt" -ForceKillProcesses
 
    Attempts to remove the file at C:\temp\open.txt, forcefully terminating any locking processes if necessary.
 
    .EXAMPLE
    Remove-EnhancedItem -Path "C:\temp\open.txt" -MaxRetries 5 -RetryInterval 10
 
    Attempts to remove the file at C:\temp\open.txt, with a maximum of 5 retries and a 10-second wait between retries.
 
    .NOTES
    Author: Your Name
    Date: YYYY-MM-DD
    Version: 1.0
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Path,

        [Parameter(Mandatory = $false)]
        [switch]$ForceKillProcesses, # Option to force kill processes locking the files

        [Parameter(Mandatory = $false)]
        [int]$MaxRetries = 3, # Maximum number of retries

        [Parameter(Mandatory = $false)]
        [int]$RetryInterval = 5 # Interval between retries in seconds
    )

    Begin {
        Write-EnhancedLog -Message "Starting Remove-EnhancedItem function for path: $Path" -Level "Notice"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    Process {
        $retryCount = 0
        $removedSuccessfully = $false
    
        while (-not $removedSuccessfully -and $retryCount -lt $MaxRetries) {
            try {
                Write-EnhancedLog -Message "Attempting to remove item: $Path (Attempt $($retryCount + 1) of $MaxRetries)" -Level "INFO"
                Remove-Item -Path $Path -Recurse -Force
    
                # Verify the removal
                if (-not (Test-Path -Path $Path)) {
                    Write-EnhancedLog -Message "Successfully removed item: $Path" -Level "INFO"
                    $removedSuccessfully = $true
                }
                else {
                    Write-EnhancedLog -Message "Item $Path still exists after removal attempt." -Level "WARNING"


                    Write-EnhancedLog -Message "Error encountered while trying to remove item: $Path - $($_.Exception.Message)" -Level "ERROR"
    
                    # Use -like for simpler matching of the error message
                    # if ($_.Exception.Message -like "*being used by another process*") {
                    Write-EnhancedLog -Message "Identifying processes locking the file or directory..." -Level "WARNING"
        
                    # This will call Manage-LockingProcesses to attempt to identify and stop processes
                    Manage-LockingProcesses -FilePath $Path -HandlePath "C:\ProgramData\SystemTools\handle64.exe"
        
                    # Identify processes locking the file using Sysinternals Handle or similar tool
                    $lockingProcessesParams = @{
                        FilePath   = $Path
                        HandlePath = "C:\ProgramData\SystemTools\handle64.exe"
                    }
                    $lockingProcesses = Get-LockingProcess @lockingProcessesParams
        
                    if ($lockingProcesses) {
                        Write-EnhancedLog -Message "Processes locking the file or directory:" -Level "INFO"
                        $lockingProcesses | ForEach-Object {
                            Write-EnhancedLog -Message "Process ID: $($_.ProcessId), Process Name: $($_.ProcessName)" -Level "INFO"
                        }
        
                        if ($ForceKillProcesses) {
                            $lockingProcesses | ForEach-Object {
                                try {
                                    Write-EnhancedLog -Message "Attempting to kill process ID: $($_.ProcessId), Process Name: $($_.ProcessName)" -Level "WARNING"
                                    & "C:\Windows\System32\taskkill.exe" /PID $_.ProcessId /F /T
                                    Write-EnhancedLog -Message "Successfully killed process ID: $($_.ProcessId), Process Name: $($_.ProcessName)" -Level "INFO"
                                }
                                catch {
                                    Write-EnhancedLog -Message "Failed to kill process ID: $($_.ProcessId), Process Name: $($_.ProcessName) - $($_.Exception.Message)" -Level "ERROR"
                                }
                            }
                            Start-Sleep -Seconds 5 # Delay to allow processes to terminate
                        }
                    }
                    else {
                        Write-EnhancedLog -Message "No locking processes identified." -Level "WARNING"
                    }

                }
            }


            catch {
                Write-EnhancedLog -Message "Error encountered while trying to remove item: $Path - $($_.Exception.Message)" -Level "ERROR"
                Write-EnhancedLog -Message "Identifying processes locking the file or directory..." -Level "WARNING"
                # This will call Manage-LockingProcesses to attempt to identify and stop processes
                Manage-LockingProcesses -FilePath $Path -HandlePath "C:\ProgramData\SystemTools\handle64.exe"
                Handle-Error -ErrorRecord $_
                throw
            }

            # catch {
             
            # }
            # else {
            # Write-EnhancedLog -Message "Unexpected error occurred: $($_.Exception.Message)" -Level "ERROR"
            # }
            # }
    
            # Retry if the item wasn't removed
            if (-not $removedSuccessfully) {
                $retryCount++
                if ($retryCount -lt $MaxRetries) {
                    Write-EnhancedLog -Message "Retrying removal in $RetryInterval seconds (Attempt $($retryCount + 1) of $MaxRetries)..." -Level "INFO"
                    Start-Sleep -Seconds $RetryInterval
                }
                else {
                    Write-EnhancedLog -Message "Failed to remove item: $Path after $MaxRetries attempts." -Level "CRITICAL"
                    throw "Failed to remove item: $Path after $MaxRetries attempts."
                }
            }
        }
    }
    
    
    
    

    End {
        Write-EnhancedLog -Message "Exiting Remove-EnhancedItem function" -Level "Notice"
    }
}
#EndRegion '.\Public\Remove-EnhancedItem.ps1' 275
#Region '.\Public\Remove-ExistingHandle.ps1' -1

function Remove-ExistingHandle {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$TargetFolder
    )

    # Full path for Handle64.exe
    $Handle64Path = Join-Path -Path $TargetFolder -ChildPath "Handle64.exe"

    try {
        # Check if Handle64.exe exists
        if (Test-Path -Path $Handle64Path) {
            Write-EnhancedLog -Message "Removing existing Handle64.exe from: $TargetFolder" -Level "INFO"
            # Remove Handle64.exe
            Remove-Item -Path $Handle64Path -Force
            Write-EnhancedLog -Message "Handle64.exe has been removed from: $TargetFolder" -Level "INFO"
        }
        else {
            Write-EnhancedLog -Message "No Handle64.exe file found in: $TargetFolder" -Level "INFO"
        }
    }
    catch {
        # Handle any errors during the removal
        Write-EnhancedLog -Message "An error occurred while trying to remove Handle64.exe: $($_.Exception.Message)" -Level "ERROR"
        Handle-Error -ErrorRecord $_
    }
}
#EndRegion '.\Public\Remove-ExistingHandle.ps1' 29
#Region '.\Public\Validate-FileExists.ps1' -1

function Validate-FileExists {
    param (
        [Parameter(Mandatory = $true)]
        [string]$FilePath
    )

    if (-Not (Test-Path -Path $FilePath)) {
        Write-EnhancedLog -Message "File '$FilePath' does not exist." -Level "ERROR"
        throw "File '$FilePath' does not exist."
    }
}


#EndRegion '.\Public\Validate-FileExists.ps1' 14
#Region '.\Public\Verify-CopyOperation.ps1' -1

function Verify-CopyOperation {
    <#
    .SYNOPSIS
    Verifies that files have been correctly copied between a source and destination directory.
 
    .DESCRIPTION
    The Verify-CopyOperation function compares the contents of a source directory with a destination directory to ensure that all files and directories have been successfully copied.
    It reports missing files, extra files, and provides detailed information about any discrepancies found during the verification process.
 
    .PARAMETER SourcePath
    The path to the source directory whose contents are being copied and need verification.
 
    .PARAMETER DestinationPath
    The path to the destination directory where the files from the source have been copied.
 
    .EXAMPLE
    Verify-CopyOperation -SourcePath "C:\Source" -DestinationPath "C:\Destination"
    Verifies the copied contents between C:\Source and C:\Destination, checking for any discrepancies such as missing or extra files.
 
    .OUTPUTS
    Custom objects detailing missing, extra, or mismatched files between the source and destination.
 
    .NOTES
    The function uses recursion to verify subdirectories and outputs the results to the console.
    Any discrepancies between the source and destination directories are logged for analysis.
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the source directory path.")]
        [string]$SourcePath,

        [Parameter(Mandatory = $true, HelpMessage = "Provide the destination directory path.")]
        [string]$DestinationPath
    )

    begin {
        Write-EnhancedLog -Message "Verifying copy operation..." -Level "Notice"
        Log-Params -Params @{
            SourcePath = $SourcePath
            DestinationPath = $DestinationPath
        }

        $sourceItems = Get-ChildItem -Path $SourcePath -Recurse -File
        $destinationItems = Get-ChildItem -Path $DestinationPath -Recurse -File

        # Use a generic list for better performance compared to using an array with +=
        $verificationResults = [System.Collections.Generic.List[PSCustomObject]]::new()
    }

    process {
        try {
            foreach ($item in $sourceItems) {
                $relativePath = $item.FullName.Substring($SourcePath.Length)
                $correspondingPath = Join-Path -Path $DestinationPath -ChildPath $relativePath

                if (-not (Test-Path -Path $correspondingPath)) {
                    $verificationResults.Add([PSCustomObject]@{
                            Status       = "Missing"
                            SourcePath   = $item.FullName
                            ExpectedPath = $correspondingPath
                            FileSize     = $item.Length
                            LastModified = $item.LastWriteTime
                        })
                }
                else {
                    # Compare file sizes and timestamps
                    $destItem = Get-Item -Path $correspondingPath
                    if ($item.Length -ne $destItem.Length -or $item.LastWriteTime -ne $destItem.LastWriteTime) {
                        $verificationResults.Add([PSCustomObject]@{
                                Status       = "Mismatch"
                                SourcePath   = $item.FullName
                                ExpectedPath = $correspondingPath
                                SourceSize   = $item.Length
                                DestinationSize = $destItem.Length
                                SourceModified  = $item.LastWriteTime
                                DestinationModified = $destItem.LastWriteTime
                            })
                    }
                }
            }

            foreach ($item in $destinationItems) {
                $relativePath = $item.FullName.Substring($DestinationPath.Length)
                $correspondingPath = Join-Path -Path $SourcePath -ChildPath $relativePath

                if (-not (Test-Path -Path $correspondingPath)) {
                    $verificationResults.Add([PSCustomObject]@{
                            Status       = "Extra"
                            ActualPath   = $item.FullName
                            FileSize     = $item.Length
                            LastModified = $item.LastWriteTime
                        })
                }
            }
        }
        catch {
            Write-EnhancedLog -Message "Error during verification process: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    end {
        if ($verificationResults.Count -gt 0) {
            Write-EnhancedLog -Message "Discrepancies found. See detailed log." -Level "WARNING"
            $verificationResults | Format-Table -AutoSize | Out-String | ForEach-Object { 
                Write-EnhancedLog -Message $_ -Level "INFO" 
            }

            # Uncomment when troubleshooting
            # $verificationResults | Out-GridView
        }
        else {
            Write-EnhancedLog -Message "All items verified successfully. No discrepancies found." -Level "Notice"
        }

        Write-EnhancedLog -Message ("Total items in source: $SourcePath " + $sourceItems.Count) -Level "INFO"
        Write-EnhancedLog -Message ("Total items in destination: $DestinationPath " + $destinationItems.Count) -Level "INFO"

        # Return the verification results for further processing
        return $verificationResults
    }
}
#EndRegion '.\Public\Verify-CopyOperation.ps1' 124