Private/Replacement/Invoke-CgrSameNameSnapshotReplacement.ps1

function Invoke-CgrSameNameSnapshotReplacement {
    <#
    .SYNOPSIS
    Replaces a repository at the same name with a clean Snapshot while preserving the original as an archive.

    .DESCRIPTION
    Revalidates approved source evidence, renames the original repository to the
    reviewed archive name, verifies the archived source, creates a fresh repository
    at the original name, proves distinct replacement identity, publishes and
    verifies the clean Snapshot, then restores supported settings and protection.
    Partial failure produces recovery evidence rather than hiding mutation state.

    .NOTES
    Same-name replacement is the highest-risk Snapshot path. Exact confirmation and
    ShouldProcess are completed by Copy-GitHubRepository before entry. The archived
    original is intentionally retained and is never silently overwritten or deleted.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The public Copy-GitHubRepository command performs ShouldProcess and exact same-name confirmation before calling this orchestration boundary.')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [psobject] $Plan,
        [Parameter(Mandatory)] [psobject] $SourceRepository,
        [ValidateNotNullOrEmpty()] [string] $HostName = 'github.com',
        [string] $ReportPath
    )

    $completedSteps = [System.Collections.Generic.List[object]]::new()
    $archive = $null
    $destination = $null
    $snapshot = $null
    $repositoryIdentity = $null
    $sourceState = Get-CgrObjectProperty -InputObject $Plan -Name 'SourceState'
    $sourceRepositoryId = Get-CgrObjectProperty -InputObject $SourceRepository -Name 'Id'
    $sourceRepositoryNodeId = Get-CgrObjectProperty -InputObject $SourceRepository -Name 'NodeId'
    $failureStage = 'ValidateApprovedSourceState'

    try {
        Assert-CgrApprovedSourceState -Repository $SourceRepository -SourceState $sourceState | Out-Null

        $failureStage = 'PreserveSourceAsArchive'
        $archive = Rename-CgrGitHubRepository -SourceRepository $SourceRepository -ArchiveRepository $Plan.ArchiveRepository -HostName $HostName
        $completedSteps.Add([pscustomobject] @{ Order = 1; Name = 'PreserveSourceAsArchive'; MutatedGitHub = $true; Verified = $true })

        $failureStage = 'VerifyArchivedSource'
        Assert-CgrApprovedSourceState -Repository $archive -SourceState $sourceState -AllowRepositoryNameChange | Out-Null
        $completedSteps.Add([pscustomobject] @{ Order = 2; Name = 'VerifyArchivedSource'; MutatedGitHub = $false; Verified = $true })

        $failureStage = 'CreateReplacementRepository'
        $destination = New-CgrGitHubRepository -Repository $Plan.DestinationRepository -Visibility $Plan.DestinationVisibility -HostName $HostName
        $completedSteps.Add([pscustomobject] @{ Order = 3; Name = 'CreateReplacementRepository'; MutatedGitHub = $true; Verified = $true })

        $failureStage = 'VerifyReplacementRepositoryIdentity'
        $repositoryIdentity = Assert-CgrReplacementRepositoryIdentity -SourceRepository $SourceRepository -ArchiveRepository $archive -ReplacementRepository $destination
        $completedSteps.Add([pscustomobject] @{ Order = 4; Name = 'VerifyReplacementRepositoryIdentity'; MutatedGitHub = $false; Verified = $true })

        $failureStage = 'CopySnapshot'
        $snapshot = @(Copy-CgrRepositorySnapshot -SourceRepository $archive -DestinationRepository $destination -BranchName $Plan.SourceDefaultBranch -CommitMessage $Plan.CommitMessage -ApprovedSourceState $sourceState)[-1]
        $completedSteps.Add([pscustomobject] @{ Order = 5; Name = 'CopySnapshot'; MutatedGitHub = $true; Verified = $snapshot.Verified })

        $failureStage = 'ReloadReplacement'
        $verifiedDestination = Get-CgrRepository -Repository $destination.FullName -HostName $HostName
        $failureStage = 'VerifySnapshot'
        $verification = Invoke-CgrActivityStage -Name 'VerifyDestinationContent' -Message 'Verify destination content' -Action {
            Invoke-CgrRepositorySnapshotVerification -SourceRepository $archive -DestinationRepository $verifiedDestination -ApprovedSourceState $sourceState
        }
        $completedSteps.Add([pscustomobject] @{ Order = 6; Name = 'VerifySnapshot'; MutatedGitHub = $false; Verified = $verification.IsSuccessful })

        $failureStage = 'RestoreSupportedSettings'
        $settings = Invoke-CgrActivityStage -Name 'RestoreSupportedSettings' -Message 'Restore supported repository settings' -Action {
            if (-not $verification.IsSuccessful) {
                return [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.SettingsRestoreResult'; Repository = $destination.FullName; Restored = @(); Skipped = @('SnapshotVerificationFailed'); Unsupported = @(); IsSuccessful = $false }
            }
            if ($Plan.SkipSettings) {
                return [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.SettingsRestoreResult'; Repository = $destination.FullName; Restored = @(); Skipped = @('AllSettings'); Unsupported = @(); IsSuccessful = $true }
            }
            Set-CgrGitHubRepositorySetting -SourceRepository $SourceRepository -DestinationRepository $verifiedDestination -HostName $HostName
        }
        $completedSteps.Add([pscustomobject] @{ Order = 7; Name = 'RestoreSupportedSettings'; MutatedGitHub = -not $Plan.SkipSettings; Verified = $settings.IsSuccessful })

        $failureStage = 'RestoreRepositoryProtection'
        $planProtection = Get-CgrObjectProperty -InputObject $Plan -Name 'Protection'
        $protection = Invoke-CgrActivityStage -Name 'RestoreRepositoryProtection' -Message 'Restore transferable repository protection' -Action {
            if (-not $verification.IsSuccessful) {
                return [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.RepositoryProtectionRestoreResult'; Repository = $destination.FullName; Status = 'Failed'; Restored = @(); Skipped = @('SnapshotVerificationFailed'); IsSuccessful = $false; IsComplete = $false }
            }
            if ($Plan.SkipSettings) {
                return [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.RepositoryProtectionRestoreResult'; Repository = $destination.FullName; Status = 'Skipped'; Restored = @(); Skipped = @('AllSettings'); IsSuccessful = $true; IsComplete = $true }
            }
            if ($null -eq $planProtection) {
                return Set-CgrRepositoryProtectionConfiguration -SourceRepository $SourceRepository -DestinationRepository $verifiedDestination -HostName $HostName
            }
            if ((Get-CgrObjectProperty -InputObject $planProtection -Name 'Status') -eq 'Captured') {
                return Set-CgrRepositoryProtectionConfiguration -SourceRepository $SourceRepository -DestinationRepository $verifiedDestination -SourceConfiguration (Get-CgrObjectProperty -InputObject $planProtection -Name 'Configuration') -HostName $HostName
            }

            $planningStatus = [string] (Get-CgrObjectProperty -InputObject $planProtection -Name 'Status')
            [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.RepositoryProtectionRestoreResult'; Repository = $destination.FullName; Status = 'Unsupported'; Restored = @(); Skipped = @("ProtectionPlanning:$planningStatus"); IsSuccessful = $true; IsComplete = $false }
        }
        $completedSteps.Add([pscustomobject] @{ Order = 8; Name = 'RestoreRepositoryProtection'; MutatedGitHub = [bool] ($verification.IsSuccessful -and -not $Plan.SkipSettings); Verified = $protection.IsSuccessful })

        $sourceCommitSha = Get-CgrObjectProperty -InputObject $sourceState -Name 'CommitSha'
        $sourceTreeSha = Get-CgrObjectProperty -InputObject $sourceState -Name 'TreeSha'
        $snapshotTreeSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'TreeSha'
        if ($null -eq $snapshotTreeSha) { $snapshotTreeSha = Get-CgrObjectProperty -InputObject $verification -Name 'DestinationTree' }
        $archiveRepositoryNodeId = Get-CgrObjectProperty -InputObject $archive -Name 'NodeId'
        $destinationRepositoryNodeId = Get-CgrObjectProperty -InputObject $verifiedDestination -Name 'NodeId'
        $provenance = [pscustomobject] @{
            PSTypeName = 'CopyGitHubRepo.SnapshotPublicationProvenance'
            RecordedAtUtc = [DateTimeOffset]::UtcNow.ToString('o')
            ContentMode = 'Snapshot'
            SourceRepository = $Plan.SourceRepository
            SourceRepositoryId = $repositoryIdentity.SourceRepositoryId
            SourceRepositoryNodeId = $sourceRepositoryNodeId
            SourceDefaultBranch = Get-CgrObjectProperty -InputObject $sourceState -Name 'DefaultBranch'
            SourceCommitSha = $sourceCommitSha
            SourceTreeSha = $sourceTreeSha
            PlannedSourceState = $sourceState
            CopiedSourceCommitSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'SourceCommitSha'
            CopiedSourceTreeSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'TreeSha'
            ArchiveRepository = $archive.FullName
            ArchiveRepositoryId = $repositoryIdentity.ArchiveRepositoryId
            ArchiveRepositoryNodeId = $archiveRepositoryNodeId
            DestinationRepository = $verifiedDestination.FullName
            DestinationRepositoryId = $repositoryIdentity.ReplacementRepositoryId
            DestinationRepositoryNodeId = $destinationRepositoryNodeId
            DestinationRootCommitSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'CommitSha'
            DestinationTreeSha = $snapshotTreeSha
            VerificationSuccessful = [bool] $verification.IsSuccessful
        }

        $executionResult = [pscustomobject] @{
            PSTypeName = 'CopyGitHubRepo.MigrationExecutionResult'
            SchemaVersion = 1
            Status = if (-not $verification.IsSuccessful) { 'SnapshotVerificationFailed' } elseif ($Plan.SkipSettings) { 'SameNameSnapshotVerifiedSettingsSkipped' } elseif ($settings.IsSuccessful -and $protection.IsSuccessful) { 'SameNameReplacementCompleted' } elseif (-not $settings.IsSuccessful) { 'SettingsRestoreFailed' } else { 'ProtectionRestoreFailed' }
            SourceRepository = $Plan.SourceRepository
            ApprovedSourceState = $sourceState
            ActualCopiedSourceState = [pscustomobject] @{ CommitSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'SourceCommitSha'; TreeSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'TreeSha' }
            SourceRepositoryId = $repositoryIdentity.SourceRepositoryId
            SourceRepositoryNodeId = $sourceRepositoryNodeId
            OriginalRepository = $Plan.SourceRepository
            OriginalRepositoryId = $repositoryIdentity.SourceRepositoryId
            OriginalRepositoryNodeId = $sourceRepositoryNodeId
            ArchiveRepository = $archive.FullName
            ArchiveRepositoryId = $repositoryIdentity.ArchiveRepositoryId
            ArchiveRepositoryNodeId = $archiveRepositoryNodeId
            ArchivedOriginalIdentityPreserved = $repositoryIdentity.ArchiveRepositoryId -eq $repositoryIdentity.SourceRepositoryId
            DestinationRepository = $destination.FullName
            DestinationRepositoryId = $repositoryIdentity.ReplacementRepositoryId
            DestinationRepositoryNodeId = $destinationRepositoryNodeId
            ReplacementDestinationRepository = $destination.FullName
            ReplacementDestinationRepositoryId = $repositoryIdentity.ReplacementRepositoryId
            ReplacementDestinationRepositoryNodeId = $destinationRepositoryNodeId
            ReplacementHasDistinctIdentity = $repositoryIdentity.ReplacementRepositoryId -ne $repositoryIdentity.SourceRepositoryId
            SourceVisibility = $Plan.SourceVisibility
            DestinationVisibility = $Plan.DestinationVisibility
            DestinationHtmlUrl = $destination.HtmlUrl
            DestinationBranch = Get-CgrObjectProperty -InputObject $snapshot -Name 'BranchName'
            SnapshotCommitSha = Get-CgrObjectProperty -InputObject $snapshot -Name 'CommitSha'
            SourceTreeSha = $sourceTreeSha
            Provenance = $provenance
            IsVerified = $verification.IsSuccessful
            Verification = $verification
            Settings = $settings
            Protection = $protection
            Plan = $Plan
            CompletedSteps = @($completedSteps)
            StoppedBeforeSettingsRestore = -not $verification.IsSuccessful
            SettingsRestored = [bool] ($verification.IsSuccessful -and -not $Plan.SkipSettings -and $settings.IsSuccessful)
            ProtectionRestored = [bool] ($verification.IsSuccessful -and -not $Plan.SkipSettings -and $protection.IsSuccessful -and $protection.IsComplete)
        }

        if ($ReportPath) { $failureStage = 'WriteCompletionReport'; Write-CgrMigrationExecutionReport -Result $executionResult -Path $ReportPath | Out-Null }
        return $executionResult
    }
    catch {
        $recoveryReportPath = $null
        try {
            $recoveryReportPath = Write-CgrSameNameRecoveryReport -Plan $Plan -SourceRepositoryId $sourceRepositoryId -ArchiveRepository $archive -DestinationRepository $destination -FailureStage $failureStage -ErrorRecord $_ -CompletedSteps @($completedSteps) -SourceCommitSha $(if ($sourceState) { Get-CgrObjectProperty -InputObject $sourceState -Name 'CommitSha' } else { $null }) -SourceTreeSha $(if ($sourceState) { Get-CgrObjectProperty -InputObject $sourceState -Name 'TreeSha' } else { $null }) -DestinationRootCommitSha $(if ($snapshot) { Get-CgrObjectProperty -InputObject $snapshot -Name 'CommitSha' } else { $null }) -DestinationTreeSha $(if ($snapshot) { Get-CgrObjectProperty -InputObject $snapshot -Name 'TreeSha' } else { $null }) -PreferredReportPath $ReportPath
        }
        catch { Write-Warning "Same-name replacement failed after mutation began, and the recovery report could not be written. Recovery reporting error: $($_.Exception.Message)" }
        if ($recoveryReportPath) { Write-Warning "Same-name replacement failed. Recovery report: $recoveryReportPath" }
        throw
    }
}