#Requires -Version 7.0

<#
.SYNOPSIS
    Builds local generator packages and regenerates Azure SDK for .NET or OpenAI .NET libraries for validation.

.DESCRIPTION
    This script supports three modes:
    
    Azure SDK Mode (default):
    1. Builds a local npm package of @typespec/http-client-csharp with a versioned name (1.0.0-alpha.YYYYMMDD.hash)
    2. Builds and packages the three NuGet generator framework packages with the same versioning
    3. Updates Directory.Generation.Packages.props in azure-sdk-for-net with the local NuGet version
    4. Updates the Azure generator (@azure-typespec/http-client-csharp) to use the local unbranded generator
    5. Builds and packages the Azure generator locally
    6. Updates the management plane generator (@azure-typespec/http-client-csharp-mgmt) to use local generators
    7. Updates the eng folder package.json artifacts in azure-sdk-for-net
    8. Regenerates libraries based on specified filters (all, by generator type, or interactively selected)
    9. Restores all modified artifacts to original state on success
    
    OpenAI Mode (when repository path contains "openai-dotnet"):
    1. Builds a local npm package of @typespec/http-client-csharp with a versioned name
    2. Builds and packages the NuGet generator framework packages
    3. Updates codegen/package.json with local unbranded generator
    4. Updates OpenAI.Library.Plugin.csproj with local NuGet package version
    5. Invokes Invoke-CodeGen.ps1 to regenerate the OpenAI library
    6. Restores all modified artifacts to original state on success
    
    Spector Mode (when -Spector switch is specified):
    1. Builds a local npm package of @typespec/http-client-csharp with a versioned name
    2. Builds and packages the NuGet generator framework packages
    3. Updates the Azure generator's package.json to use the local unbranded package
    4. Updates Directory.Generation.Packages.props with local NuGet version and adds local NuGet source
    5. Runs the Azure generator's Generate.ps1 to regenerate spector test scenarios
    6. Restores all modified artifacts to original state
    
    Generator Filtering (Azure SDK Mode only):
    - Use -Azure to regenerate Azure-branded libraries (@azure-typespec/http-client-csharp)
    - Use -Unbranded to regenerate unbranded libraries (@typespec/http-client-csharp)
    - Use -Mgmt to regenerate management plane libraries (@azure-typespec/http-client-csharp-mgmt)
    - Filters can be combined (e.g. -Azure -Unbranded) to regenerate libraries from multiple generators
    - Omit all filter parameters to regenerate all libraries (default)
        - Use -Select for interactive selection (can be combined with generator filters)

.PARAMETER SdkLibraryRepoPath
    Required. The local file system path to the SDK repository (azure-sdk-for-net or openai-dotnet).
    When the path contains "openai-dotnet", the script automatically operates in OpenAI mode.

.PARAMETER Select
    Optional. Azure SDK Mode only. When specified, displays an interactive menu to select specific libraries to regenerate.
    If omitted, regenerates all libraries without prompting.
    Not applicable in OpenAI mode.

.PARAMETER Azure
    Optional. Azure SDK Mode only. When specified, regenerates libraries using the Azure generator (@azure-typespec/http-client-csharp).
    Can be combined with -Unbranded and/or -Mgmt to regenerate libraries from multiple generators.
    Not applicable in OpenAI mode.

.PARAMETER Unbranded
    Optional. Azure SDK Mode only. When specified, regenerates libraries using the unbranded generator (@typespec/http-client-csharp).
    Can be combined with -Azure and/or -Mgmt to regenerate libraries from multiple generators.
    Not applicable in OpenAI mode.

.PARAMETER Mgmt
    Optional. Azure SDK Mode only. When specified, regenerates libraries using the management plane generator (@azure-typespec/http-client-csharp-mgmt).
    Can be combined with -Azure and/or -Unbranded to regenerate libraries from multiple generators.
    If no generator filter is specified, all libraries are regenerated.
    Not applicable in OpenAI mode.

.PARAMETER Spector
    Optional. Azure SDK Mode only. When specified, regenerates the Azure spector test scenarios in azure-sdk-for-net
    instead of regenerating SDK libraries. This builds the local unbranded generator, wires it into the Azure generator,
    and runs the Azure generator's Generate.ps1 to regenerate spector test projects.
    Mutually exclusive with Select, Azure, Unbranded, and Mgmt parameters.
    Not applicable in OpenAI mode.

.EXAMPLE
    # Azure SDK Mode - Regenerate all libraries
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net"
    
.EXAMPLE
    # OpenAI Mode - Regenerate OpenAI library
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\openai-dotnet"

.EXAMPLE
    # Azure SDK Mode - Interactively select libraries to regenerate (from all available)
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Select

.EXAMPLE
    # Regenerate only Azure-branded libraries (non-interactive)
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Azure

.EXAMPLE
    # Interactively select from Azure-branded libraries only
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Azure -Select

.EXAMPLE
    # Regenerate only unbranded libraries
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Unbranded

.EXAMPLE
    # Regenerate only management plane libraries
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Mgmt

.EXAMPLE
    # Regenerate both Azure-branded and unbranded libraries
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Azure -Unbranded

.EXAMPLE
    # Regenerate Azure spector test scenarios using local changes
    .\RegenPreview.ps1 -SdkLibraryRepoPath "C:\repos\azure-sdk-for-net" -Spector
#>

param(
    [Parameter(Mandatory=$true)]
    [string]$SdkLibraryRepoPath,
    
    [Parameter(Mandatory=$false)]
    [switch]$Select,
    
    [Parameter(Mandatory=$false)]
    [switch]$Azure,
    
    [Parameter(Mandatory=$false)]
    [switch]$Unbranded,
    
    [Parameter(Mandatory=$false)]
    [switch]$Mgmt,
    
    [Parameter(Mandatory=$false)]
    [switch]$Spector
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 3.0

# Detect OpenAI mode
$isOpenAIMode = $SdkLibraryRepoPath -like "*openai-dotnet*"

# Validate mutually exclusive parameters
if ($isOpenAIMode) {
    # In OpenAI mode, no filter parameters are allowed
    if ($Select -or $Azure -or $Unbranded -or $Mgmt -or $Spector) {
        Write-Error "OpenAI mode detected. The -Select, -Azure, -Unbranded, -Mgmt, and -Spector parameters are not applicable when regenerating OpenAI libraries."
        exit 1
    }
} else {
    # -Spector is mutually exclusive with all other filter parameters
    if ($Spector -and ($Select -or $Azure -or $Unbranded -or $Mgmt)) {
        Write-Error "The -Spector parameter is mutually exclusive with -Select, -Azure, -Unbranded, and -Mgmt."
        exit 1
    }
}

# Import utility functions
Import-Module "$PSScriptRoot\Generation.psm1" -DisableNameChecking -Force
Import-Module "$PSScriptRoot\RegenPreview.psm1" -DisableNameChecking -Force

# Resolve paths
$packageRoot = Resolve-Path (Join-Path $PSScriptRoot '..' '..')
$sdkRepoPath = Resolve-Path $SdkLibraryRepoPath -ErrorAction Stop

Write-Host "==================== LOCAL VALIDATION SCRIPT ====================" -ForegroundColor Cyan

if ($isOpenAIMode) {
    Write-Host "Repository: OpenAI .NET" -ForegroundColor Gray
    Write-Host "Mode: OpenAI library regeneration" -ForegroundColor Yellow
} else {
    Write-Host "Repository: Azure SDK for .NET" -ForegroundColor Gray
    
    # Display active mode
    $modeText = if ($Spector) {
        "Regenerate Azure spector test scenarios"
    } elseif ($Azure -or $Unbranded -or $Mgmt) {
        $generatorNames = @()
        if ($Azure) { $generatorNames += "Azure" }
        if ($Unbranded) { $generatorNames += "Unbranded" }
        if ($Mgmt) { $generatorNames += "Management plane" }
        if ($Select) {
            "Regenerate $($generatorNames -join ', ') libraries (interactive selection)"
        } else {
            "Regenerate $($generatorNames -join ', ') libraries"
        }
    } elseif ($Select) {
        "Interactive library selection"
    } else {
        "Regenerate ALL libraries"
    }
    Write-Host "Mode: $modeText" -ForegroundColor Yellow
}
Write-Host ""

# Generate version string with timestamp and hash
# Used for both npm and NuGet packages to ensure consistency
function Get-LocalPackageVersion {
    $timestamp = Get-Date -Format "yyyyMMddHHmmss"
    $hash = (git -C $packageRoot rev-parse --short=7 HEAD 2>$null) ?? "local"
    return "1.0.0-alpha-$timestamp.$hash"
}

# Run npm pack and return the package file path
function Invoke-NpmPack {
    param(
        [string]$WorkingDirectory,
        [string]$DebugFolder
    )
    
    Write-Host "Running: npm pack" -ForegroundColor Gray
    Push-Location $WorkingDirectory
    try {
        $output = & npm pack 2>&1
        if ($LASTEXITCODE -ne 0) {
            throw "npm pack failed with exit code $LASTEXITCODE"
        }
        
        # Get the first line that ends with .tgz (the actual package filename)
        # It may be in format "filename: package-name.tgz" or just "package-name.tgz"
        $packageLine = ($output | Where-Object { $_ -match '\.tgz$' } | Select-Object -First 1).ToString().Trim()
        if ($packageLine -match 'filename:\s*(.+\.tgz)') {
            $packageFile = $Matches[1].Trim()
        } else {
            $packageFile = $packageLine
        }
        
        $packagePath = Join-Path $WorkingDirectory $packageFile
        if (-not (Test-Path $packagePath)) {
            throw "Package file not created: $packagePath"
        }
        
        # Move package to debug folder
        $debugPackagePath = Join-Path $DebugFolder $packageFile
        Move-Item $packagePath $debugPackagePath -Force
        
        return $debugPackagePath
    }
    finally {
        Pop-Location
    }
}

# Update package.json with new version
function Update-PackageJsonVersion {
    param(
        [string]$PackageJsonPath,
        [string]$NewVersion
    )
    
    Write-Host "Updating package version to $NewVersion in $PackageJsonPath" -ForegroundColor Gray
    $packageJson = Get-Content $PackageJsonPath -Raw | ConvertFrom-Json -AsHashtable
    $packageJson.version = $NewVersion
    $packageJson | ConvertTo-Json -Depth 100 | Set-Content $PackageJsonPath -Encoding utf8 -NoNewline
}

# Update UnbrandedGeneratorVersion in Directory.Generation.Packages.props
function Update-UnbrandedGeneratorVersion {
    param(
        [string]$PackagesDataPropsPath,
        [string]$NewVersion
    )
    
    Write-Host "Updating UnbrandedGeneratorVersion to $NewVersion in Directory.Generation.Packages.props" -ForegroundColor Gray
    
    $content = Get-Content $PackagesDataPropsPath -Raw
    
    # Use regex to find and replace the UnbrandedGeneratorVersion property
    $pattern = '(<UnbrandedGeneratorVersion>)([^<]+)(</UnbrandedGeneratorVersion>)'
    
    if ($content -match $pattern) {
        $oldVersion = $Matches[2]
        $newContent = $content -replace $pattern, "<UnbrandedGeneratorVersion>$NewVersion</UnbrandedGeneratorVersion>"
        Set-Content $PackagesDataPropsPath -Value $newContent -Encoding utf8 -NoNewline
        Write-Host "  Updated UnbrandedGeneratorVersion from $oldVersion to $NewVersion" -ForegroundColor Green
    } else {
        throw "UnbrandedGeneratorVersion property not found in $PackagesDataPropsPath"
    }
}

# Compute libraries to regenerate by scanning the repository
function Get-LibrariesToRegenerate {
    param([string]$SdkRepoPath)
    
    $EmitterMap = @{
        'eng/azure-typespec-http-client-csharp-emitter-package.json' = '@azure-typespec/http-client-csharp'
        'eng/azure-typespec-http-client-csharp-mgmt-emitter-package.json' = '@azure-typespec/http-client-csharp-mgmt'
        'eng/http-client-csharp-emitter-package.json' = '@typespec/http-client-csharp'
    }
    
    function Get-GeneratorType {
        param([string]$LibraryPath)
        
        # Check for tsp-location.yaml files to identify TypeSpec libraries
        $tspLocationFiles = Get-ChildItem -Path $LibraryPath -Recurse -Filter "tsp-location.yaml" -ErrorAction SilentlyContinue
        
        foreach ($tspLocationFile in $tspLocationFiles) {
            try {
                $content = Get-Content $tspLocationFile.FullName -Raw -ErrorAction SilentlyContinue
                if ($content -and $content -match 'emitterPackageJsonPath:\s*(?<val>"[^"]+"|[^,\s]+)\s*,?') {
                    $emitterPath = $matches['val'].Trim('"')
                    
                    if ($EmitterMap.ContainsKey($emitterPath)) {
                        return $EmitterMap[$emitterPath]
                    }
                }
            }
            catch {
                # Continue to next file if error
            }
        }
        
        return $null
    }
    
    $libraries = @()
    $sdkRoot = Join-Path $SdkRepoPath "sdk"
    
    if (-not (Test-Path $sdkRoot)) {
        Write-Warning "SDK directory not found at: $sdkRoot"
        return @()
    }
    
    # Scan through all service directories
    $serviceDirs = Get-ChildItem -Path $sdkRoot -Directory -Force -ErrorAction SilentlyContinue
    foreach ($serviceDir in $serviceDirs) {
        # Look for library directories
        $libraryDirs = Get-ChildItem -Path $serviceDir.FullName -Directory -Force -ErrorAction SilentlyContinue
        foreach ($libraryDir in $libraryDirs) {
            # Skip directories that don't look like libraries
            if ($libraryDir.Name -in @("tests", "samples", "perf", "assets", "docs")) {
                continue
            }
            
            # Skip libraries that start with "Microsoft." or don't start with "Azure."
            if ($libraryDir.Name.StartsWith("Microsoft.") -or -not $libraryDir.Name.StartsWith("Azure.")) {
                continue
            }
            
            # If it has a /src directory, it's likely a library
            $srcPath = Join-Path $libraryDir.FullName "src"
            if (-not (Test-Path $srcPath)) {
                continue
            }
            
            # Check if this library uses TypeSpec with one of our generators
            $generator = Get-GeneratorType $libraryDir.FullName
            if (-not $generator) {
                continue
            }
            
            # Calculate relative path from SDK repo root
            $relativePath = $libraryDir.FullName.Substring($SdkRepoPath.Length + 1)
            $relativePath = $relativePath -replace "\\", "/"
            
            $libraries += @{
                Service = $serviceDir.Name
                Library = $libraryDir.Name
                Path = $relativePath
                Generator = $generator
            }
        }
    }
    
    return @($libraries)
}

# Interactive library selection
function Select-LibrariesToRegenerate {
    param([array]$Libraries)
    
    # Ensure we have an array
    if (-not $Libraries) {
        return @()
    }
    
    $Libraries = @($Libraries)
    
    Write-Host "`n==================== LIBRARY SELECTION ====================" -ForegroundColor Cyan
    Write-Host "Found $($Libraries.Count) libraries available for regeneration" -ForegroundColor White
    Write-Host ""
    
    # Group libraries by generator
    $azureLibs = @($Libraries | Where-Object { $_.Generator -eq "@azure-typespec/http-client-csharp" })
    $unbrandedLibs = @($Libraries | Where-Object { $_.Generator -eq "@typespec/http-client-csharp" })
    $mgmtLibs = @($Libraries | Where-Object { $_.Generator -eq "@azure-typespec/http-client-csharp-mgmt" })
    
    # Reorder libraries array to match display order (Azure, Unbranded, Mgmt)
    $orderedLibraries = @()
    $orderedLibraries += $azureLibs
    $orderedLibraries += $unbrandedLibs
    $orderedLibraries += $mgmtLibs
    
    $currentIndex = 1
    
    if ($azureLibs.Count -gt 0) {
        Write-Host "Azure SDK Libraries:" -ForegroundColor Yellow
        for ($i = 0; $i -lt $azureLibs.Count; $i++) {
            $lib = $azureLibs[$i]
            Write-Host ("  [{0,2}] {1,-50} ({2})" -f $currentIndex, $lib.Library, $lib.Service) -ForegroundColor Gray
            $currentIndex++
        }
        Write-Host ""
    }
    
    if ($unbrandedLibs.Count -gt 0) {
        Write-Host "Unbranded Libraries:" -ForegroundColor Yellow
        for ($i = 0; $i -lt $unbrandedLibs.Count; $i++) {
            $lib = $unbrandedLibs[$i]
            Write-Host ("  [{0,2}] {1,-50} ({2})" -f $currentIndex, $lib.Library, $lib.Service) -ForegroundColor Gray
            $currentIndex++
        }
        Write-Host ""
    }
    
    if ($mgmtLibs.Count -gt 0) {
        Write-Host "Management Plane Libraries:" -ForegroundColor Yellow
        for ($i = 0; $i -lt $mgmtLibs.Count; $i++) {
            $lib = $mgmtLibs[$i]
            Write-Host ("  [{0,2}] {1,-50} ({2})" -f $currentIndex, $lib.Library, $lib.Service) -ForegroundColor Gray
            $currentIndex++
        }
        Write-Host ""
    }
    
    Write-Host "=============================================================" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "Enter library numbers to regenerate (comma-separated), 'all' for all libraries, or 'q' to quit:" -ForegroundColor White
    Write-Host "Example: 1,3,5  or  1-4,7  or  all" -ForegroundColor DarkGray
    Write-Host ""
    
    $selection = Read-Host "Selection"
    
    if ($selection -ieq 'q' -or $selection -ieq 'quit') {
        Write-Host "Operation cancelled by user." -ForegroundColor Yellow
        exit 0
    }
    
    if ($selection -ieq 'all') {
        return $orderedLibraries
    }
    
    # Parse selection
    $selectedIndices = @()
    $parts = $selection -split ',' | ForEach-Object { $_.Trim() }
    
    foreach ($part in $parts) {
        if ($part -match '^(\d+)-(\d+)$') {
            # Range: 1-4
            $start = [int]$Matches[1]
            $end = [int]$Matches[2]
            $selectedIndices += ($start..$end)
        }
        elseif ($part -match '^\d+$') {
            # Single number: 3
            $selectedIndices += [int]$part
        }
        else {
            Write-Host "Invalid selection format: $part" -ForegroundColor Red
            exit 1
        }
    }
    
    # Validate and collect selected libraries
    $selectedLibraries = @()
    foreach ($index in $selectedIndices | Sort-Object -Unique) {
        if ($index -lt 1 -or $index -gt $orderedLibraries.Count) {
            Write-Host "Invalid library number: $index (valid range: 1-$($orderedLibraries.Count))" -ForegroundColor Red
            exit 1
        }
        $selectedLibraries += $orderedLibraries[$index - 1]
    }
    
    if ($selectedLibraries.Count -eq 0) {
        Write-Host "No libraries selected. Exiting." -ForegroundColor Yellow
        exit 0
    }
    
    Write-Host "`nSelected $($selectedLibraries.Count) libraries for regeneration:" -ForegroundColor Green
    foreach ($lib in $selectedLibraries) {
        Write-Host "  - $($lib.Library) ($($lib.Service))" -ForegroundColor Gray
    }
    Write-Host ""
    
    # Ensure we return an array, even for a single element
    return @($selectedLibraries)
}

# Generate final report
function Write-RegenerationReport {
    param(
        [array]$Results,
        [TimeSpan]$ElapsedTime,
        [string]$DebugFolder
    )
    
    $passed = @($Results | Where-Object { $_.Success -eq $true })
    $failed = @($Results | Where-Object { $_.Success -eq $false })
    
    Write-Host "`n==================== REGENERATION REPORT ====================" -ForegroundColor Cyan
    Write-Host "Total Libraries: $($Results.Count)" -ForegroundColor White
    Write-Host "Passed: $($passed.Count)" -ForegroundColor Green
    Write-Host "Failed: $($failed.Count)" -ForegroundColor Red
    
    if ($ElapsedTime) {
        $elapsedFormatted = "{0:hh\:mm\:ss}" -f $ElapsedTime
        Write-Host "Execution Time: $elapsedFormatted" -ForegroundColor Cyan
    }
    Write-Host ""
    
    if ($passed.Count -gt 0) {
        Write-Host "PASSED LIBRARIES:" -ForegroundColor Green
        foreach ($result in $passed) {
            Write-Host "  ✓ $($result.Library) ($($result.Service))" -ForegroundColor Green
        }
        Write-Host ""
    }
    
    if ($failed.Count -gt 0) {
        Write-Host "FAILED LIBRARIES:" -ForegroundColor Red
        foreach ($result in $failed) {
            Write-Host "  ✗ $($result.Library) ($($result.Service))" -ForegroundColor Red
            Write-Host "    Error: $($result.Error)" -ForegroundColor Gray
            if ($result.Output) {
                Write-Host "    Details: $($result.Output.Substring(0, [Math]::Min(200, $result.Output.Length)))..." -ForegroundColor DarkGray
            }
        }
        Write-Host ""
    }
    
    Write-Host "=============================================================" -ForegroundColor Cyan
    
    # Save detailed report to debug folder
    $reportPath = if ($DebugFolder) { 
        Join-Path $DebugFolder "regen-report.json" 
    } else { 
        Join-Path $packageRoot "regen-report.json" 
    }
    $Results | ConvertTo-Json -Depth 10 | Set-Content $reportPath -Encoding utf8
    Write-Host "Detailed report saved to: $reportPath" -ForegroundColor Gray
}

# ============================================================================
# Main Script Execution
# ============================================================================

# Start timer
$scriptStartTime = Get-Date

try {
    # Step 1: Load and select libraries (Azure SDK Mode only, if using -Select flag)
    if ($Select -and -not $isOpenAIMode) {
        Write-Host "`n[1/5] Loading TypeSpec libraries from repository..." -ForegroundColor Cyan
        
        $allLibraries = Get-LibrariesToRegenerate -SdkRepoPath $sdkRepoPath
        
        # Apply generator filter before interactive selection
        $filteredLibraries = @(Filter-LibrariesByGenerator `
            -Libraries $allLibraries `
            -Azure:$Azure `
            -Unbranded:$Unbranded `
            -Mgmt:$Mgmt)
        
        if (-not $filteredLibraries -or $filteredLibraries.Count -eq 0) {
            Write-Host "No libraries found matching the specified generator filter" -ForegroundColor Yellow
            exit 0
        }
        
        $libraries = @(Select-LibrariesToRegenerate -Libraries $filteredLibraries)
        
        # Check if user cancelled selection
        if (-not $libraries -or $libraries.Count -eq 0) {
            Write-Host "No libraries selected. Exiting..." -ForegroundColor Yellow
            exit 0
        }
    }
    
    # Create debug folder for packaged artifacts with timestamped subfolder
    $timestamp = Get-Date -Format "yyyyMMdd"
    $debugFolder = Join-Path $packageRoot "debug" $timestamp
    if (-not (Test-Path $debugFolder)) {
        New-Item -ItemType Directory -Path $debugFolder -Force | Out-Null
    }
    
    Write-Host "Debug folder: $debugFolder" -ForegroundColor Gray
    Write-Host ""
    
    # Step 1: Build the unbranded generator
    if ($isOpenAIMode -or $Spector) {
        Write-Host "`n[1/3] Building unbranded generator..." -ForegroundColor Cyan
    } else {
        Write-Host "`n[2/5] Building unbranded generator..." -ForegroundColor Cyan
    }
    
    Push-Location $packageRoot
    try {
        Write-Host "Installing dependencies..." -ForegroundColor Gray
        Invoke "npm ci"
        if ($LASTEXITCODE -ne 0) {
            throw "Failed to install dependencies for unbranded generator"
        }
        
        Write-Host "Cleaning build artifacts..." -ForegroundColor Gray
        Invoke "npm run clean"
        if ($LASTEXITCODE -ne 0) {
            throw "Failed to clean unbranded generator"
        }
        
        Write-Host "Building generator..." -ForegroundColor Gray
        Invoke "npm run build"
        if ($LASTEXITCODE -ne 0) {
            throw "Failed to build unbranded generator"
        }
        
        Write-Host "  Build completed" -ForegroundColor Green
    }
    finally {
        Pop-Location
    }
    
    # Step 2: Package the generators with local version
    if ($isOpenAIMode -or $Spector) {
        Write-Host "`n[2/3] Packaging generators..." -ForegroundColor Cyan
    } else {
        Write-Host "`n[2/5] Packaging generators..." -ForegroundColor Cyan
    }
    
    $localVersion = Get-LocalPackageVersion
    Write-Host "Local package version: $localVersion" -ForegroundColor Yellow
    
    $unbrandedPackageJson = Join-Path $packageRoot "package.json"
    $originalPackageJson = Get-Content $unbrandedPackageJson -Raw
    
    try {
        Update-PackageJsonVersion -PackageJsonPath $unbrandedPackageJson -NewVersion $localVersion
        $unbrandedPackagePath = Invoke-NpmPack -WorkingDirectory $packageRoot -DebugFolder $debugFolder
        Write-Host "Created npm package: $unbrandedPackagePath" -ForegroundColor Green
    }
    finally {
        # Restore original package.json
        Set-Content $unbrandedPackageJson $originalPackageJson -Encoding utf8 -NoNewline
    }
    
    # Build and package NuGet packages for generator framework
    Write-Host "Building NuGet generator packages..." -ForegroundColor Gray
    
    $generatorRoot = Join-Path $packageRoot "generator"
    $nugetProjects = @(
        "Microsoft.TypeSpec.Generator\src\Microsoft.TypeSpec.Generator.csproj",
        "Microsoft.TypeSpec.Generator.Input\src\Microsoft.TypeSpec.Generator.Input.csproj",
        "Microsoft.TypeSpec.Generator.ClientModel\src\Microsoft.TypeSpec.Generator.ClientModel.csproj"
    )
    
    foreach ($project in $nugetProjects) {
        $projectPath = Join-Path $generatorRoot $project
        if (-not (Test-Path $projectPath)) {
            throw "NuGet project not found: $projectPath"
        }
        
        Write-Host "Packing: $(Split-Path $projectPath -Leaf)" -ForegroundColor Gray
        $packCmd = "dotnet pack $projectPath /p:Version=$localVersion /p:PackageVersion=$localVersion /p:PackageOutputPath=$debugFolder --configuration Debug --no-build --nologo -v:quiet"
        Invoke $packCmd $generatorRoot
    }
    
    Write-Host "  NuGet packages created" -ForegroundColor Green
    
    # OpenAI Mode: Update and regenerate OpenAI library
    if ($isOpenAIMode) {
        Write-Host "`n[3/3] Updating and regenerating OpenAI library..." -ForegroundColor Cyan
        
        try {
            $generationOutput = Update-OpenAIGenerator `
                -OpenAIRepoPath $sdkRepoPath `
                -UnbrandedPackagePath $unbrandedPackagePath `
                -LocalVersion $localVersion `
                -DebugFolder $debugFolder
            
            # Create result object for successful regeneration
            $result = @{
                Success = $true
                Library = "OpenAI .NET Library"
                Service = "OpenAI"
                Path = "codegen"
                Generator = "@typespec/http-client-csharp"
                Error = ""
                Output = $generationOutput
            }
        }
        catch {
            # Create result object for failed regeneration
            $result = @{
                Success = $false
                Library = "OpenAI .NET Library"
                Service = "OpenAI"
                Path = "codegen"
                Generator = "@typespec/http-client-csharp"
                Error = $_.Exception.Message
                Output = $_.Exception.ToString()
            }
        }
        
        # Calculate elapsed time and generate report
        $scriptEndTime = Get-Date
        $elapsedTime = $scriptEndTime - $scriptStartTime
        
        Write-RegenerationReport -Results @($result) -ElapsedTime $elapsedTime -DebugFolder $debugFolder
        
        # Exit with appropriate code
        if ($result.Success) {
            Write-Host "`nScript completed successfully." -ForegroundColor Cyan
            exit 0
        } else {
            Write-Host "`nScript failed." -ForegroundColor Red
            exit 1
        }
    }
    
    # Spector Mode: Regenerate Azure spector test scenarios
    if ($Spector) {
        Write-Host "`n[3/3] Regenerating Azure spector test scenarios..." -ForegroundColor Cyan
        
        try {
            $generationOutput = Update-AzureSpectorScenarios `
                -AzureGeneratorPath (Join-Path $sdkRepoPath "eng" "packages" "http-client-csharp") `
                -UnbrandedPackagePath $unbrandedPackagePath `
                -LocalVersion $localVersion `
                -DebugFolder $debugFolder
            
            $result = @{
                Success = $true
                Library = "Azure Spector Test Scenarios"
                Service = "Spector"
                Path = "eng/packages/http-client-csharp/generator/TestProjects/Spector"
                Generator = "@azure-typespec/http-client-csharp"
                Error = ""
                Output = $generationOutput
            }
        }
        catch {
            $result = @{
                Success = $false
                Library = "Azure Spector Test Scenarios"
                Service = "Spector"
                Path = "eng/packages/http-client-csharp/generator/TestProjects/Spector"
                Generator = "@azure-typespec/http-client-csharp"
                Error = $_.Exception.Message
                Output = $_.Exception.ToString()
            }
        }
        
        $scriptEndTime = Get-Date
        $elapsedTime = $scriptEndTime - $scriptStartTime
        
        Write-RegenerationReport -Results @($result) -ElapsedTime $elapsedTime -DebugFolder $debugFolder
        
        if ($result.Success) {
            Write-Host "`nScript completed successfully." -ForegroundColor Cyan
            exit 0
        } else {
            Write-Host "`nScript failed." -ForegroundColor Red
            exit 1
        }
    }
    
    # Azure SDK Mode: Continue with Azure SDK-specific steps
    
    # Update Directory.Generation.Packages.props with local NuGet version
    $packagesDataPropsPath = Join-Path $sdkRepoPath "eng" "centralpackagemanagement" "Directory.Generation.Packages.props"
    if (-not (Test-Path $packagesDataPropsPath)) {
        throw "Directory.Generation.Packages.props not found at: $packagesDataPropsPath"
    }
    
    Update-UnbrandedGeneratorVersion -PackagesDataPropsPath $packagesDataPropsPath -NewVersion $localVersion
    
    # Add debug folder as a NuGet package source
    $nugetConfigPath = Join-Path $sdkRepoPath "NuGet.Config"
    Add-LocalNuGetSource -NuGetConfigPath $nugetConfigPath -SourcePath $debugFolder
    
    # Step 3: Build required generators and update artifacts
    Write-Host "`n[3/5] Building required generators and updating artifacts..." -ForegroundColor Cyan
    
    # Determine which generators are needed based on libraries to be regenerated
    if ($Select) {
        # Libraries were already selected at the beginning
        $librariesToAnalyze = $libraries
    } else {
        # Load all libraries and apply filters to determine what would be regenerated
        $allLibraries = Get-LibrariesToRegenerate -SdkRepoPath $sdkRepoPath
        $librariesToAnalyze = Filter-LibrariesByGenerator `
            -Libraries $allLibraries `
            -Azure:$Azure `
            -Unbranded:$Unbranded `
            -Mgmt:$Mgmt
    }
    
    # Determine which generators are actually needed
    $needsUnbranded = $false
    $needsAzure = $false
    $needsMgmt = $false
    
    if ($librariesToAnalyze -and $librariesToAnalyze.Count -gt 0) {
        foreach ($lib in $librariesToAnalyze) {
            switch ($lib.Generator) {
                "@typespec/http-client-csharp" { $needsUnbranded = $true }
                "@azure-typespec/http-client-csharp" { $needsAzure = $true; $needsUnbranded = $true }
                "@azure-typespec/http-client-csharp-mgmt" { $needsMgmt = $true; $needsAzure = $true; $needsUnbranded = $true }
            }
        }
    }
    
    $generatorSummary = @()
    if ($needsUnbranded) { $generatorSummary += "Unbranded" }
    if ($needsAzure) { $generatorSummary += "Azure" }
    if ($needsMgmt) { $generatorSummary += "Management" }
    
    if ($generatorSummary.Count -gt 0) {
        Write-Host "Required generators: $($generatorSummary -join ', ')" -ForegroundColor Yellow
    } else {
        Write-Host "No additional generators required" -ForegroundColor Yellow
    }
    
    # Update Azure generator if needed
    if ($needsAzure) {
        Write-Host "Building Azure generator..." -ForegroundColor Gray
        
        $azureGeneratorPath = Join-Path $sdkRepoPath "eng" "packages" "http-client-csharp"
        $packagesDataPropsPath = Join-Path $sdkRepoPath "eng" "centralpackagemanagement" "Directory.Generation.Packages.props"
        
        $azurePackagePath = Update-AzureGenerator `
            -AzureGeneratorPath $azureGeneratorPath `
            -UnbrandedPackagePath $unbrandedPackagePath `
            -DebugFolder $debugFolder `
            -PackagesDataPropsPath $packagesDataPropsPath `
            -LocalVersion $localVersion
        
        Write-Host "  Azure generator completed" -ForegroundColor Green
    } else {
        $azurePackagePath = $null
    }
    
    # Update eng folder artifacts if needed
    if ($needsAzure -or $needsUnbranded) {
        Write-Host "Updating eng folder artifacts..." -ForegroundColor Gray
        
        $engFolder = Join-Path $sdkRepoPath "eng"
        $tempDir = Join-Path $engFolder "temp-package-update"
        New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
        
        # Helper function to update emitter package files
        $updateEmitterPackage = {
            param($PackagePath, $EmitterJsonName, $LockJsonName)
            
            $emitterJson = Join-Path $engFolder $EmitterJsonName
            $tempPackageJson = Join-Path $tempDir "package.json"
            
            Copy-Item $emitterJson $tempPackageJson -Force
            
            Push-Location $tempDir
            try {
                Invoke "npm install `"`"file:$PackagePath`"`" --package-lock-only" $tempDir
                if ($LASTEXITCODE -ne 0) {
                    throw "Failed to install local package for emitter update."
                }
                
                Copy-Item $tempPackageJson $emitterJson -Force
                $lockFile = Join-Path $tempDir "package-lock.json"
                if (Test-Path $lockFile) {
                    Copy-Item $lockFile (Join-Path $engFolder $LockJsonName) -Force
                }
                
                # Cleanup temp files
                Remove-Item $tempPackageJson, $lockFile -Force -ErrorAction SilentlyContinue
            }
            finally {
                Pop-Location
            }
        }
        
        try {
            if ($needsAzure -and $azurePackagePath) {
                & $updateEmitterPackage $azurePackagePath "azure-typespec-http-client-csharp-emitter-package.json" "azure-typespec-http-client-csharp-emitter-package-lock.json"
            }
            
            if ($needsUnbranded) {
                & $updateEmitterPackage $unbrandedPackagePath "http-client-csharp-emitter-package.json" "http-client-csharp-emitter-package-lock.json"
            }
            
            Write-Host "  Artifacts updated" -ForegroundColor Green
        }
        finally {
            Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
        }
    }
    
    # Update management plane generator if needed
    if ($needsMgmt) {
        Write-Host "Building management plane generator..." -ForegroundColor Gray
        
        $engFolder = Join-Path $sdkRepoPath "eng"
        $mgmtGeneratorPath = Join-Path $engFolder "packages" "http-client-csharp-mgmt"
        
        if (Test-Path $mgmtGeneratorPath) {
            Update-MgmtGenerator `
                -EngFolder $engFolder `
                -DebugFolder $debugFolder `
                -LocalVersion $localVersion
            
            Write-Host "  Management plane generator completed" -ForegroundColor Green
        } else {
            Write-Host "  Management plane generator not found, skipping..." -ForegroundColor Yellow
        }
    }
    
    # Step 4: Regenerate libraries
    Write-Host "`n[4/5] Regenerating libraries..." -ForegroundColor Cyan
    
    if (-not $Select) {
        # Load all libraries if not using -Select flag
        $allLibraries = Get-LibrariesToRegenerate -SdkRepoPath $sdkRepoPath
        
        # Apply generator filter
        $libraries = Filter-LibrariesByGenerator `
            -Libraries $allLibraries `
            -Azure:$Azure `
            -Unbranded:$Unbranded `
            -Mgmt:$Mgmt
        
        if ($libraries.Count -eq 0) {
            Write-Host "No libraries found matching the specified generator filter" -ForegroundColor Yellow
            Write-Host "Skipping regeneration step..." -ForegroundColor Gray
        } else {
            $filterParts = @()
            if ($Azure) { $filterParts += "Azure" }
            if ($Unbranded) { $filterParts += "Unbranded" }
            if ($Mgmt) { $filterParts += "Management plane" }
            $filterText = if ($filterParts.Count -gt 0) {
                " ($($filterParts -join ', ') generator$(if ($filterParts.Count -gt 1) { 's' } else { '' }) only)"
            } else {
                ""
            }
            Write-Host "Regenerating $($libraries.Count) libraries$filterText" -ForegroundColor Yellow
        }
    } else {
        # Libraries were already selected at the beginning
        if ($libraries -and $libraries.Count -gt 0) {
            Write-Host "Using $($libraries.Count) previously selected libraries" -ForegroundColor Yellow
        } else {
            Write-Host "No libraries were selected" -ForegroundColor Yellow
        }
    }
    
    if (-not $libraries -or $libraries.Count -eq 0) {
        Write-Host "No libraries selected for regeneration" -ForegroundColor Yellow
        $failedCount = 0
    } else {
        
        # Determine parallel execution throttle limit: (CPU cores - 2), min 1, max 8
        $cpuCores = if ($IsWindows -or $PSVersionTable.PSVersion.Major -lt 6) {
            (Get-CimInstance -ClassName Win32_Processor | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
        } elseif ($IsMacOS) {
            [int](sysctl -n hw.ncpu)
        } else {
            [int](nproc)
        }
        
        $throttleLimit = [Math]::Max(1, [Math]::Min(8, $cpuCores - 2))
        
        Write-Host "Using $throttleLimit concurrent jobs (detected $cpuCores logical processors)" -ForegroundColor Gray
        Write-Host ""
        
    # Pre-install tsp-client to avoid concurrent npm operations
    $sdkForNetEngFolder = Join-Path $sdkRepoPath "eng"
    Write-Host "Pre-installing tsp-client..." -ForegroundColor Gray
    $tspClientDir = Join-Path $sdkForNetEngFolder "common" "tsp-client"
    Invoke "npm ci --prefix $tspClientDir" $tspClientDir
    if ($LASTEXITCODE -ne 0) {
        throw "Failed to install tsp-client"
    }
    Write-Host "  tsp-client ready" -ForegroundColor Green
    Write-Host ""

    # Pre-build the client plugin to avoid concurrent builds
    $codeGenerationTargetPath = Join-Path $sdkForNetEngFolder  "CodeGeneration.targets"
    if (-not (Test-Path $codeGenerationTargetPath)) {
        throw "CodeGeneration.targets not found at: $codeGenerationTargetPath"
    }
    Write-Host "Pre-building client plugin..." -ForegroundColor Gray
    Invoke "dotnet build $codeGenerationTargetPath /t:BuildPlugin /p:TypeSpecInput=temp" $sdkForNetEngFolder
    if ($LASTEXITCODE -ne 0) {
        throw "Failed to build client plugin"
    }
    
    # Thread-safe collections for progress tracking
    $completed = [System.Collections.Concurrent.ConcurrentBag[int]]::new()
    $totalCount = $libraries.Count
    
    # Run regeneration in parallel
    $results = $libraries | ForEach-Object -ThrottleLimit $throttleLimit -Parallel {
        $library = $_
        $azureSdkPath = $using:sdkRepoPath
        $completedBag = $using:completed
        $total = $using:totalCount
        
        # Determine build path (check for src subdirectory)
        $libraryPath = Join-Path $azureSdkPath $library.Path
        $srcPath = Join-Path $libraryPath "src"
        $buildPath = if ((Test-Path $srcPath) -and (Get-ChildItem -Path $srcPath -Filter "*.csproj" -ErrorAction SilentlyContinue)) {
            $srcPath
        } else {
            $libraryPath
        }
        
        # Regenerate library
        $result = try {
            if (-not (Test-Path $libraryPath)) {
                @{ Success = $false; Error = "Library path not found"; Output = "" }
            } else {
                Push-Location $buildPath
                try {
                    $output = & dotnet build /t:GenerateCode /p:SkipTspClientInstall=true /p:SkipBuildPlugin=true 2>&1
                    $exitCode = $LASTEXITCODE
                    
                    if ($exitCode -ne 0) {
                        @{ Success = $false; Error = "Generation failed with exit code $exitCode"; Output = ($output -join "`n") }
                    } else {
                        @{ Success = $true; Output = ($output -join "`n") }
                    }
                }
                finally {
                    Pop-Location
                }
            }
        }
        catch {
            @{ Success = $false; Error = $_.Exception.Message; Output = $_.Exception.ToString() }
        }
        
        # Update progress counter
        $completedBag.Add(1)
        $currentCount = $completedBag.Count
        
        # Thread-safe console output with progress
        $status = if ($result.Success) { "✓" } else { "✗" }
        $color = if ($result.Success) { "Green" } else { "White" }
        
        $progressMsg = "[$currentCount/$total] $status $($library.Library)"
        Write-Host $progressMsg -ForegroundColor $color
        
        # Return result with library metadata
        return @{
            Service = $library.Service
            Library = $library.Library
            Path = $library.Path
            Generator = $library.Generator
            Success = if ($result.ContainsKey('Success')) { $result.Success } else { $false }
            Error = if ($result.ContainsKey('Error')) { $result.Error } else { "" }
            Output = if ($result.ContainsKey('Output')) { $result.Output } else { "" }
        }
    }
    
    # Generate final report
    $scriptEndTime = Get-Date
    $elapsedTime = $scriptEndTime - $scriptStartTime
    
    Write-RegenerationReport -Results $results -ElapsedTime $elapsedTime -DebugFolder $debugFolder
    
    # Check if any libraries failed
    $failedLibraries = @($results | Where-Object { -not $_.Success })
    $failedCount = $failedLibraries.Count
    }
    
    if ($failedCount -gt 0) {
        Write-Host "`nValidation completed with warnings: $failedCount libraries failed to regenerate" -ForegroundColor Yellow
        Write-Host "Check the detailed report above for error information" -ForegroundColor Yellow
    } else {
        Write-Host "`nValidation completed successfully! All libraries regenerated without errors." -ForegroundColor Green
        
        # Step 5: Restore artifacts to original state
        Write-Host "`n[5/5] Restoring artifacts to original state..." -ForegroundColor Cyan
        
        Push-Location $sdkRepoPath
        try {
            Write-Host "Restoring modified files..." -ForegroundColor Gray
            $filesToRestore = @(
                "eng/azure-typespec-http-client-csharp-emitter-package.json"
                "eng/azure-typespec-http-client-csharp-emitter-package-lock.json"
                "eng/http-client-csharp-emitter-package.json"
                "eng/http-client-csharp-emitter-package-lock.json"
                "eng/azure-typespec-http-client-csharp-mgmt-emitter-package.json"
                "eng/azure-typespec-http-client-csharp-mgmt-emitter-package-lock.json"
                "eng/packages/http-client-csharp/package-lock.json"
                "eng/packages/http-client-csharp-mgmt/package.json"
                "eng/packages/http-client-csharp-mgmt/package-lock.json"
                "eng/centralpackagemanagement/Directory.Generation.Packages.props"
                "NuGet.Config"
            )
            $restoreCmd = "git restore $($filesToRestore -join ' ')"
            Invoke $restoreCmd $sdkRepoPath
            if ($LASTEXITCODE -ne 0) {
                throw "Failed to restore modified files"
            }
            Write-Host "  All artifacts restored" -ForegroundColor Green
        }
        finally {
            Pop-Location
        }
    }
}
catch {
    Write-Host "`nScript encountered an error during setup or configuration." -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    Write-Host $_.ScriptStackTrace -ForegroundColor DarkGray
    
    # Attempt to restore NuGet.Config even on failure
    Write-Host "`nAttempting to restore NuGet.Config..." -ForegroundColor Yellow
    Push-Location $sdkRepoPath -ErrorAction SilentlyContinue
    try {
        #& git restore NuGet.Config 2>&1 | Out-Null
        Invoke "git restore NuGet.Config" $sdkRepoPath
        Write-Host "  NuGet.Config restored" -ForegroundColor Green
    }
    catch {
        Write-Host "  Failed to restore NuGet.Config" -ForegroundColor Red
    }
    finally {
        Pop-Location -ErrorAction SilentlyContinue
    }
    
    exit 1
}

Write-Host "`nScript completed." -ForegroundColor Cyan
