public/New-PSProfileSnapshot.ps1

<#
    .SYNOPSIS
        Creates a new snapshot of the current user's profile.
    .DESCRIPTION
        Creates a new snapshot of the current user's profile for comparison at next run.
        Snapshots capture both the profile content and its SHA256 hash, and can include
        optional metadata for organization and documentation.
    .PARAMETER Name
        Optional custom name for the snapshot. If not provided, a timestamp-based name
        is automatically generated. Examples: "before-major-change", "stable-config", "pre-update"
    .PARAMETER Notes
        Optional notes or description for the snapshot. Useful for documenting why
        the snapshot was created or what it represents.
    .INPUTS
        None
    .OUTPUTS
        System.Management.Automation.PSCustomObject
        Returns an object with properties: SnapshotName, GUID, HashFile, BackupFile, MetadataFile
    .EXAMPLE
        New-PSProfileSnapshot

        Creates a snapshot with an auto-generated timestamp-based name:
            - Microsoft.VSCode_profile.ps1_83580fb6-fc89-4756-8a58-b1f4d3f3b7ff.hash
            - Microsoft.VSCode_profile.ps1_83580fb6-fc89-4756-8a58-b1f4d3f3b7ff.backup
    .EXAMPLE
        New-PSProfileSnapshot -Name "before-major-change" -Notes "Created before updating PowerShell modules"

        Creates a named snapshot with metadata stored in a JSON file:
            - Microsoft.VSCode_profile.ps1_83580fb6-fc89-4756-8a58-b1f4d3f3b7ff.metadata
    .NOTES
        All snapshots are stored in the same directory as the profile file.
        Use Get-PSProfileSnapshot to list all available snapshots.
#>


function New-PSProfileSnapshot {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Name,

        [Parameter(Mandatory = $false)]
        [string]$Notes
    )

    $guid = [guid]::NewGuid()
    $profileHashFile = "$($profile)_$($guid).hash"
    $profileBackupFile = "$($profile)_$($guid).backup"
    $metadataFile = "$($profile)_$($guid).metadata"

    try {
        $Hash = Get-FileHash -Path $profile -Algorithm SHA256 -ErrorAction Stop | Select-Object -ExpandProperty Hash 
    }
    catch {
        Write-Warning "⚠️ Could not calculate hash for profile."
        throw $_.Exception.Message
    }

    try {
        $Hash | Out-File -FilePath $profileHashFile -Encoding ASCII -Force -ErrorAction Stop
        Write-Host "✅ Profile snapshot hash saved successfully to '$profileHashFile'." -ForegroundColor Green
    }
    catch {
        Write-Error "❌ Could not write snapshot hash to file."
        throw $_.Exception.Message
    }

    try {
        Copy-Item -Path $profile -Destination $profileBackupFile -Force -ErrorAction Stop
        Write-Host "✅ Profile snapshot saved successfully to '$profileBackupFile'." -ForegroundColor Green
    }
    catch {
        Write-Error "❌ Could not save profile snapshot to '$profileBackupFile'."
        throw $_.Exception.Message
    }

    # If a custom name or notes are provided, create metadata file
    if ($PSBoundParameters.ContainsKey('Name') -or $PSBoundParameters.ContainsKey('Notes')) {
        $snapshotName = $Name ?? (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        
        $metadata = @{
            Name       = $snapshotName
            GUID       = $guid.ToString()
            CreatedAt  = (Get-Date -Format "yyyy-MM-dd HH:mm:ss K")
            Notes      = $Notes ?? ""
        }

        try {
            $metadata | ConvertTo-Json | Out-File -FilePath $metadataFile -Encoding UTF8 -Force -ErrorAction Stop
            Write-Host "✅ Snapshot metadata saved to '$metadataFile'." -ForegroundColor Green
        }
        catch {
            Write-Warning "⚠️ Could not save snapshot metadata, but snapshot files were created successfully."
        }
    }

    # Get file size in KB format for consistency with Get-PSProfileSnapshot
    $backupFileSizeKB = "$([math]::Round((Get-Item -Path $profileBackupFile).Length / 1KB, 2)) KB"
    
    # Get hash content for consistency with Get-PSProfileSnapshot
    $hashContent = Get-Content -Path $profileHashFile -ErrorAction Stop
    
    return [PSCustomObject]@{
        Name        = $Name ?? (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        GUID        = $guid
        Path        = $profileBackupFile
        Hash        = $hashContent
        FileSize    = $backupFileSizeKB
        CreatedTime = (Get-Item -Path $profileBackupFile).CreationTime
        Notes       = $Notes ?? $null
    }
}