scripts/modules/prerequisites/azure-cli/install-azure-cli.ps1

# strangeloop Setup - Azure CLI Installation Module
# Version: 1.0.0


param(
    [string]$Version = "latest",
    [switch]${test-only},
    [switch]${what-if}
)

# Import shared modules
$SharedPath = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent | Join-Path -ChildPath "shared"
Import-Module "$SharedPath\write-functions.ps1" -Force -DisableNameChecking
Import-Module "$SharedPath\test-functions.ps1" -Force -DisableNameChecking
Import-Module "$SharedPath\path-functions.ps1" -Force -DisableNameChecking

function Install-AzureCLIViaMSI {
    param(
        
    )
    
    try {
        Write-Progress "Downloading Azure CLI installer..."
        
        # Download Azure CLI installer
        $azCliUrl = "https://aka.ms/installazurecliwindows"
        $tempDir = Join-Path $env:TEMP "AzureCLI-$(Get-Random)"
        New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
        if (-not (Test-Path $tempDir)) {
            Write-Error "Could not create temporary directory"
            return $false
        }
        
        $installerPath = Join-Path $tempDir "AzureCLI.msi"
        
        try {
            Invoke-WebRequest -Uri $azCliUrl -OutFile $installerPath -UseBasicParsing
            Write-Success "Azure CLI installer downloaded successfully"
        } catch {
            Write-Error "Failed to download Azure CLI installer: $($_.Exception.Message)"
            Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
            return $false
        }
        
        # Verify download
        if (-not (Test-Path $installerPath)) {
            Write-Error "Azure CLI installer not found after download"
            Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
            return $false
        }
        
        Write-Progress "Installing Azure CLI via MSI..."
        
        # Install Azure CLI with elevation (required for MSI)
        $installResult = Invoke-CommandWithTimeout -ScriptBlock {
            Start-Process msiexec.exe -ArgumentList "/i", $using:installerPath, "/quiet", "/norestart" -Wait -NoNewWindow -PassThru -Verb RunAs
        } -TimeoutSeconds 600 -Description "Azure CLI MSI installation"
        
        # Clean up installer
        Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
        
        if (-not $installResult.Success) {
            Write-Warning "Azure CLI MSI installation failed: $($installResult.Error)"
            return $false
        }
        
        $process = $installResult.Output
        if ($process.ExitCode -eq 0) {
            Write-Success "Azure CLI installed successfully via MSI"
            
            # Refresh PATH immediately after MSI installation
            Write-Info "Refreshing environment PATH after MSI installation..."
            $env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine") + 
                       ";" + [Environment]::GetEnvironmentVariable("PATH", "User")
            
            # Also try to refresh from registry
            try {
                $machinePath = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\Environment").GetValue("PATH", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
                $userPath = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey("Environment").GetValue("PATH", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
                $env:PATH = $machinePath + ";" + $userPath
                Write-Info "PATH refreshed from registry after MSI installation"
            } catch {
                Write-Warning "Could not refresh PATH from registry: $($_.Exception.Message)"
            }
            
            # Wait for PATH changes to take effect
            Start-Sleep -Seconds 3
            
            return $true
        } else {
            Write-Warning "Azure CLI MSI installation failed with exit code: $($process.ExitCode)"
            return $false
        }
        
    } catch {
        Write-Warning "Azure CLI MSI installation failed: $($_.Exception.Message)"
        return $false
    }
}

function Test-AzureCLI {
    param(
        
    )
    
    try {
        Write-Info "Testing Azure CLI installation..."
        
        # Check if Azure CLI command is available
        if (-not (Test-Command "az")) {
            Write-Warning "Azure CLI command 'az' not found"
            return $false
        }
        
        # Check Azure CLI version
        $azVersion = Get-ToolVersion "az"
        if (-not $azVersion) {
            Write-Warning "Could not get Azure CLI version"
            return $false
        }
        
        # Test basic functionality
        $versionOutput = az version --output json 2>$null
        if (-not $versionOutput) {
            Write-Warning "Azure CLI functionality test failed"
            return $false
        }
        
        Write-Success "Azure CLI is properly installed: $azVersion"
        return $true
        
    } catch {
        Write-Warning "Error testing Azure CLI: $($_.Exception.Message)"
        return $false
    }
}

function Install-AzureCLI {
    param(
        [string]$Version = "latest",
        [switch]${test-only},
        [switch]${what-if}
    )
    
    # If test-only mode, just test current installation
    if (${test-only}) {
        return Test-AzureCLI
    }
    
    # If what-if mode, show what would be done
    if (${what-if}) {
        Write-Host "what if: Would test if Azure CLI is already installed" -ForegroundColor Yellow
        Write-Host "what if: Would check internet connection" -ForegroundColor Yellow
        Write-Host "what if: Would attempt installation via winget (preferred method)" -ForegroundColor Yellow
        Write-Host "what if: Would fallback to MSI installer if winget fails" -ForegroundColor Yellow
        Write-Host "what if: Would verify installation and configure Azure CLI" -ForegroundColor Yellow
        return $true
    }
    
    Write-Step "Installing Azure CLI..."
    
    try {
        # Check if Azure CLI is already installed
        if (Test-Command "az") {
            $currentVersion = Get-ToolVersion "az"
            if ($currentVersion) {
                Write-Info "Azure CLI $currentVersion is already installed"
                Write-Success "Azure CLI installation confirmed"
                return $true
            }
        }
        
        # Check internet connection
        if (-not (Test-InternetConnection)) {
            Write-Error "Internet connection required for Azure CLI installation"
            return $false
        }
        
        Write-Progress "Attempting Azure CLI installation..."
        
        # Try installation methods in order of preference
        $installationSuccessful = $false
        
        # Method 1: Try winget (preferred - doesn't require admin)
        Write-Info "Attempting installation via winget (preferred method - no admin required)..."
        if (Test-Command "winget") {
            try {
                Write-Progress "Installing Azure CLI via winget..."
                Write-Info "Running: winget install Microsoft.AzureCLI --silent --accept-package-agreements --accept-source-agreements"
                
                # Use Invoke-CommandWithTimeout for better control
                $wingetResult = Invoke-CommandWithTimeout -ScriptBlock {
                    $output = winget install "Microsoft.AzureCLI" --silent --accept-package-agreements --accept-source-agreements 2>&1
                    return @{
                        ExitCode = $LASTEXITCODE
                        Output = $output
                    }
                } -TimeoutSeconds 600 -Description "Azure CLI winget installation"
                
                if ($wingetResult.Success) {
                    $result = $wingetResult.Output
                    if ($result.ExitCode -eq 0) {
                        Write-Success "Azure CLI installed successfully via winget"
                        
                        # Refresh PATH immediately after winget installation
                        Write-Info "Refreshing environment PATH after installation..."
                        $env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine") + 
                                   ";" + [Environment]::GetEnvironmentVariable("PATH", "User")
                        
                        # Also try to refresh from registry for completeness
                        try {
                            $machinePath = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\Environment").GetValue("PATH", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
                            $userPath = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey("Environment").GetValue("PATH", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
                            $env:PATH = $machinePath + ";" + $userPath
                            Write-Info "PATH refreshed from registry"
                        } catch {
                            Write-Warning "Could not refresh PATH from registry: $($_.Exception.Message)"
                        }
                        
                        # Wait for PATH changes to take effect
                        Start-Sleep -Seconds 3
                        
                        $installationSuccessful = $true
                    } elseif ($result.ExitCode -eq -1978335189) {
                        # This exit code often means "already installed" in winget
                        Write-Info "Azure CLI may already be installed (winget exit code: $($result.ExitCode))"
                        Write-Info "Proceeding to verification..."
                        
                        # Still refresh PATH in case it's installed but not in current session
                        Write-Info "Refreshing environment PATH..."
                        $env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine") + 
                                   ";" + [Environment]::GetEnvironmentVariable("PATH", "User")
                        Start-Sleep -Seconds 2
                        
                        $installationSuccessful = $true
                    } else {
                        Write-Warning "winget installation failed with exit code: $($result.ExitCode)"
                        Write-Info "winget output: $($result.Output -join '; ')"
                    }
                } else {
                    Write-Warning "winget installation timed out or failed: $($wingetResult.Error)"
                }
            } catch {
                Write-Warning "winget installation failed with exception: $($_.Exception.Message)"
            }
        } else {
            Write-Warning "winget not available - this is the preferred installation method"
            Write-Info "Consider installing winget from Microsoft Store or GitHub releases"
        }
        
        # Method 2: Try elevated MSI installation
        if (-not $installationSuccessful) {
            Write-Info "Attempting elevated MSI installation..."
            
            if (-not (Test-AdminPrivileges)) {
                Write-Info "MSI installation requires administrator privileges - will prompt for elevation"
            }
            
            $installationSuccessful = Install-AzureCLIViaMSI
        }
        
        # Method 3: Provide manual installation guidance
        if (-not $installationSuccessful) {
            Write-Warning "Automated Azure CLI installation failed"
            Write-Host ""
            Write-Host "📋 Recommended Installation Methods (in order):" -ForegroundColor Cyan
            Write-Host "1. Install winget first, then retry: winget install Microsoft.AzureCLI" -ForegroundColor Green
            Write-Host "2. Run this script as Administrator for MSI installation" -ForegroundColor Yellow
            Write-Host "3. Download MSI from: https://aka.ms/installazurecliwindows" -ForegroundColor Yellow
            Write-Host "4. Install via web installer: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows" -ForegroundColor Yellow
            Write-Host ""
            Write-Host "💡 winget is the preferred method as it doesn't require admin privileges" -ForegroundColor Cyan
            Write-Host "After installation, restart your terminal and run this setup script again." -ForegroundColor Green
            return $false
        }
        
        # Update PATH and verify installation
        Write-Progress "Verifying Azure CLI installation..."
        
        # Refresh environment variables one more time for verification
        Write-Info "Final PATH refresh for verification..."
        $env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine") + 
                   ";" + [Environment]::GetEnvironmentVariable("PATH", "User")
        
        # Give more time for PATH changes to take effect after installation
        Write-Info "Waiting for PATH changes to take effect..."
        Start-Sleep -Seconds 5
        
        # Test if az command is now available
        Write-Info "Testing if 'az' command is available..."
        $azCommand = Get-Command "az" -ErrorAction SilentlyContinue
        if ($azCommand) {
            Write-Info "Found az command at: $($azCommand.Source)"
        } else {
            Write-Warning "az command not found in PATH after installation"
            Write-Info "Current PATH directories containing 'Azure' or 'CLI':"
            $env:PATH -split ';' | Where-Object { $_ -like '*Azure*' -or $_ -like '*CLI*' } | ForEach-Object { Write-Info " - $_" }
        }
        
        # Verify installation
        if (Test-Command "az") {
            $installedVersion = Get-ToolVersion "az"
            if ($installedVersion) {
                Write-Success "Azure CLI $installedVersion verified and ready"
                return $true
            } else {
                Write-Warning "Azure CLI installed but version detection failed"
                return $true  # Still consider it successful if command exists
            }
        } else {
            Write-Error "Azure CLI installation completed but 'az' command not found"
            Write-Info "You may need to restart your terminal or reboot your system"
            Write-Info "Try running: refreshenv (if using Chocolatey) or restart your terminal"
            return $false
        }
        
    } catch {
        Write-Error "Azure CLI installation failed: $($_.Exception.Message)"
        return $false
    }
}

# Main execution
if ($MyInvocation.InvocationName -ne '.') {
    $result = Install-AzureCLI -Version $Version -test-only:${test-only} -what-if:${what-if}
    
    if ($result) {
        if (${test-only}) {
            Write-Success "Azure CLI test completed successfully"
        } else {
            Write-CompletionSummary @{
                'Azure CLI Installation' = 'Completed Successfully'
                'Version' = (Get-ToolVersion "az")
                'Command Available' = if (Test-Command "az") { "Yes" } else { "No" }
            } -Title "Azure CLI Installation Summary"
        }
    } else {
        if (${test-only}) {
            Write-Error "Azure CLI test failed"
        } else {
            Write-Error "Azure CLI installation failed"
        }
    }
    
    # Return the result for the calling script
    return $result
}

# Export functions for module usage
Export-ModuleMember -Function @(
    'Install-AzureCLI',
    'Install-AzureCLIViaMSI'
)