Public/New-LocalRepository.ps1

# Helper function to detect OneDrive paths
function Test-OneDrivePath {
    [CmdletBinding()]
    param([string]$Path)
    
    try {
        # Normalize the path
        $normalizedPath = [System.IO.Path]::GetFullPath($Path)
        
        # Get OneDrive paths from registry
        $oneDriveRegKeys = @(
            "HKCU:\Software\Microsoft\OneDrive\Accounts\*",
            "HKCU:\Environment"
        )
        
        $oneDrivePaths = @()
        
        # Get OneDrive folder paths from registry
        foreach ($regKey in $oneDriveRegKeys) {
            try {
                if ($regKey -eq "HKCU:\Environment") {
                    # Check for OneDrive environment variable
                    $oneDriveEnv = Get-ItemProperty -Path $regKey -Name "OneDrive" -ErrorAction SilentlyContinue
                    if ($oneDriveEnv -and $oneDriveEnv.OneDrive) {
                        $oneDrivePaths += $oneDriveEnv.OneDrive
                    }
                } else {
                    # Check OneDrive account registry entries
                    $accounts = Get-ItemProperty -Path $regKey -ErrorAction SilentlyContinue
                    foreach ($account in $accounts) {
                        if ($account.UserFolder) {
                            $oneDrivePaths += $account.UserFolder
                        }
                    }
                }
            } catch {
                # Continue if registry access fails
                continue
            }
        }
        
        # Also check common OneDrive locations
        $commonOneDrivePaths = @(
            "$env:USERPROFILE\OneDrive",
            "$env:USERPROFILE\OneDrive - Microsoft"
        )
        $oneDrivePaths += $commonOneDrivePaths
        
        # Check if the path starts with any OneDrive path
        foreach ($oneDrivePath in ($oneDrivePaths | Where-Object { $_ })) {
            $normalizedOneDrivePath = [System.IO.Path]::GetFullPath($oneDrivePath)
            if ($normalizedPath.StartsWith($normalizedOneDrivePath, [System.StringComparison]::OrdinalIgnoreCase)) {
                return $true
            }
        }
        
        return $false
    } catch {
        Write-Verbose "Error detecting OneDrive path: $($_.Exception.Message)"
        return $false
    }
}

# Helper function to set OneDrive folder to Always Available Offline
function Set-OneDriveAlwaysAvailable {
    [CmdletBinding()]
    param([string]$Path)
    
    try {
        Write-Host " [ONEDRIVE] Setting folder to Always Available Offline..." -ForegroundColor Gray
        
        # Use attrib command to set the folder to always be available offline
        # The +P attribute sets the folder to be pinned (always available offline)
        $attribResult = & attrib +P "$Path" /S /D 2>&1
        
        if ($LASTEXITCODE -eq 0) {
            Write-Host " [OK] OneDrive folder set to Always Available Offline" -ForegroundColor Green
        } else {
            # Try alternative method using PowerShell and COM
            try {
                Write-Host " - Trying alternative method..." -ForegroundColor Gray
                
                # Use PowerShell to set the pinned state via file attributes
                $folder = Get-Item -Path $Path
                $folder.Attributes = $folder.Attributes -bor [System.IO.FileAttributes]::ReparsePoint
                
                Write-Host " [OK] OneDrive folder configured for offline availability" -ForegroundColor Green
            } catch {
                Write-Warning "Could not set OneDrive folder to Always Available Offline: $($_.Exception.Message)"
                Write-Host " - You can manually set this in File Explorer: Right-click folder → Always keep on this device" -ForegroundColor Gray
            }
        }
    } catch {
        Write-Warning "Failed to configure OneDrive offline availability: $($_.Exception.Message)"
        Write-Host " - You can manually set this in File Explorer: Right-click folder → Always keep on this device" -ForegroundColor Gray
    }
}

function New-LocalRepository {
    <#
    .SYNOPSIS
        Create and setup a local NuGet repository directory.
 
    .DESCRIPTION
        Creates a local repository directory structure for storing NuPkg files.
        Optionally initializes it with NuGet repository structure and copies packages from a source.
 
    .PARAMETER Path
        Path where the local repository should be created. Defaults to C:\LocalNuGetRepo.
 
    .PARAMETER RepoName
        Name for the PowerShell repository registration. Defaults to 'LocalNuGetRepo'.
 
    .PARAMETER SourcePath
        Optional source path containing NuPkg files to copy to the new repository.
 
    .PARAMETER Initialize
        Initialize the repository with proper NuGet structure using nuget.exe.
 
    .PARAMETER Force
        Force creation even if the directory already exists.
 
    .PARAMETER DisableOneDriveOffline
        Skip setting OneDrive folders to Always Available Offline even if OneDrive path is detected.
 
    .EXAMPLE
        New-LocalRepository
         
        Creates a basic repository at C:\LocalNuGetRepo and registers it as 'LocalNuGetRepo'.
 
    .EXAMPLE
        New-LocalRepository -Path "C:\MyRepo" -RepoName "MyCustomRepo" -SourcePath "C:\Packages" -Initialize
         
        Creates a repository, copies packages from source, initializes NuGet structure, and registers as 'MyCustomRepo'.
 
    .EXAMPLE
        New-LocalRepository -Path "C:\Users\User\OneDrive\MyRepo" -DisableOneDriveOffline
         
        Creates a repository in OneDrive but skips setting it to Always Available Offline.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Position = 0)]
        [string]$Path = $script:DefaultRepositoryPath,
        
        [Parameter(Position = 1)]
        [string]$RepoName = "LocalNuGetRepo",
        
        [Parameter()]
        [ValidateScript({
            if ($_ -and -not (Test-Path -Path $_ -PathType Container)) {
                throw "Source path '$_' does not exist or is not a directory."
            }
            $true
        })]
        [string]$SourcePath,
        
        [Parameter()]
        [switch]$Initialize,
        
        [Parameter()]
        [switch]$Force,
        
        [Parameter()]
        [switch]$DisableOneDriveOffline
    )

    try {
        Write-Host "Creating local repository: $Path" -ForegroundColor Cyan
        
        # Detect if this is a OneDrive path
        $isOneDrivePath = Test-OneDrivePath -Path $Path
        if ($isOneDrivePath) {
            Write-Host " [DETECTED] OneDrive path detected" -ForegroundColor Yellow
        }
        
        # Check if directory exists
        if (Test-Path -Path $Path) {
            Write-Host " [EXISTS] Directory already exists, using existing directory" -ForegroundColor Yellow
        } else {
            if ($PSCmdlet.ShouldProcess($Path, "Create local repository directory")) {
                # Create the repository directory
                New-Item -Path $Path -ItemType Directory -Force | Out-Null
                Write-Host " [OK] Repository directory created" -ForegroundColor Green
            }
        }
        
        # Set OneDrive folder to Always Available Offline if detected
        if ($isOneDrivePath -and (Test-Path -Path $Path) -and -not $DisableOneDriveOffline) {
            if ($PSCmdlet.ShouldProcess($Path, "Set OneDrive folder to Always Available Offline")) {
                Set-OneDriveAlwaysAvailable -Path $Path
            }
        } elseif ($isOneDrivePath -and $DisableOneDriveOffline) {
            Write-Host " [SKIP] OneDrive offline setting disabled by parameter" -ForegroundColor Yellow
        }
        
        if ($PSCmdlet.ShouldProcess($Path, "Setup local repository")) {
            
            # Copy packages from source if specified
            if ($SourcePath) {
                Write-Host " [COPY] Copying packages from source..." -ForegroundColor Gray
                $sourcePackages = Get-ChildItem -Path $SourcePath -Filter "*.nupkg" -File
                
                if ($sourcePackages) {
                    foreach ($package in $sourcePackages) {
                        $destPath = Join-Path -Path $Path -ChildPath $package.Name
                        Copy-Item -Path $package.FullName -Destination $destPath -Force
                        Write-Host " - Copied: $($package.Name)" -ForegroundColor Gray
                    }
                    Write-Host " [OK] Copied $($sourcePackages.Count) packages" -ForegroundColor Green
                } else {
                    Write-Warning "No NuPkg files found in source path: $SourcePath"
                }
            }
            
            # Initialize NuGet structure if requested
            if ($Initialize) {
                Write-Host " [INIT] Initializing NuGet repository structure..." -ForegroundColor Gray
                
                # Try to find nuget.exe
                $nugetPath = $null
                $nugetLocations = @(
                    "nuget.exe",
                    "C:\Program Files\NuGet\nuget.exe",
                    "$env:ProgramFiles\Microsoft Visual Studio\*\*\Common7\IDE\CommonExtensions\Microsoft\NuGet\nuget.exe",
                    "$env:LOCALAPPDATA\Microsoft\VisualStudio\*\Extensions\*\Tools\nuget.exe"
                )
                
                foreach ($location in $nugetLocations) {
                    $resolved = Get-Command $location -ErrorAction SilentlyContinue
                    if ($resolved) {
                        $nugetPath = $resolved.Source
                        break
                    }
                    
                    # Try wildcard resolution for VS paths
                    $wildcard = Get-ChildItem $location -ErrorAction SilentlyContinue | Select-Object -First 1
                    if ($wildcard) {
                        $nugetPath = $wildcard.FullName
                        break
                    }
                }
                
                if ($nugetPath) {
                    Write-Host " - Found nuget.exe: $nugetPath" -ForegroundColor Gray
                    
                    # Initialize repository
                    $initResult = & $nugetPath init $Path $Path 2>&1
                    if ($LASTEXITCODE -eq 0) {
                        Write-Host " [OK] NuGet repository structure initialized" -ForegroundColor Green
                    } else {
                        Write-Warning "Failed to initialize NuGet structure: $initResult"
                    }
                } else {
                    Write-Warning "nuget.exe not found. Repository created without NuGet initialization."
                    Write-Host " To initialize later, run: nuget init `"$Path`" `"$Path`"" -ForegroundColor Gray
                }
            }
            
            # Register as PowerShell repository
            Write-Host " [REGISTER] Registering as PowerShell repository..." -ForegroundColor Gray
            
            # Check if repository is already registered
            $existingRepo = Get-PSRepository -Name $RepoName -ErrorAction SilentlyContinue
            if ($existingRepo) {
                # Update existing repository
                Set-PSRepository -Name $RepoName -SourceLocation $Path -InstallationPolicy Trusted
                Write-Host " [OK] Updated existing PowerShell repository registration" -ForegroundColor Green
            } else {
                # Register new repository
                Register-PSRepository -Name $RepoName -SourceLocation $Path -InstallationPolicy Trusted
                Write-Host " [OK] Registered as PowerShell repository: $RepoName" -ForegroundColor Green
            }
            
            # Show summary
            $packageCount = (Get-ChildItem -Path $Path -Filter "*.nupkg" -File | Measure-Object).Count
            Write-Host "`n[SUCCESS] Local repository created successfully!" -ForegroundColor Green
            Write-Host " Path: $Path" -ForegroundColor White
            Write-Host " Packages: $packageCount" -ForegroundColor White
            
            if ($packageCount -gt 0) {
                Write-Host "`nTo install packages, run:" -ForegroundColor Cyan
                Write-Host " Install-ModuleFromLocalRepo -Path `"$Path`"" -ForegroundColor White
            }
        }
        
    } catch {
        Write-Error "Failed to create local repository: $($_.Exception.Message)"
    }
}