bin/Copy-CodeGenToSolution.ps1

# =============================================================================
# COPY CODEGENERATORDEV INTO EXISTING SOLUTION - DEVELOPMENT HELPER
# =============================================================================
# PURPOSE: Copy standalone CodeGeneratorDev into ANY existing CA solution
# USAGE: Run from directory containing the target solution folder
# NOTE: Keeps original CodeGeneratorDev intact for reuse with other projects
# =============================================================================

param(
    [Parameter(Mandatory = $false)]
    [string]$TargetSolutionPath,
    
    [Parameter(Mandatory = $false)]
    [string]$CodeGenSourcePath
)

Write-Host "=== COPYING CODEGENERATORDEV INTO SOLUTION ===" -ForegroundColor Cyan
Write-Host ""

# Auto-detect CodeGeneratorDev source if not provided
if ([string]::IsNullOrWhiteSpace($CodeGenSourcePath)) {
    Write-Host "Auto-detecting CodeGeneratorDev location..." -ForegroundColor White
    
    if (Test-Path "CodeGeneratorDev\CodeGeneratorDev\CodeGeneratorDev.csproj") {
        $CodeGenSourcePath = "CodeGeneratorDev"
        Write-Host " ✓ Found at: $CodeGenSourcePath" -ForegroundColor Green
    }
    elseif (Test-Path "..\CodeGeneratorDev\CodeGeneratorDev\CodeGeneratorDev.csproj") {
        $CodeGenSourcePath = "..\CodeGeneratorDev"
        Write-Host " ✓ Found at: $CodeGenSourcePath" -ForegroundColor Green
    }
    else {
        Write-Host " ✗ Cannot find CodeGeneratorDev folder" -ForegroundColor Red
        Write-Host " Expected: CodeGeneratorDev\CodeGeneratorDev\CodeGeneratorDev.csproj" -ForegroundColor Yellow
        exit 1
    }
}

# Auto-detect target solution if not provided
if ([string]::IsNullOrWhiteSpace($TargetSolutionPath)) {
    Write-Host "Auto-detecting target solution..." -ForegroundColor White
    
    $solutions = Get-ChildItem -Path . -Filter "*.sln" -File | 
    Where-Object { $_.Directory.Name -ne "CodeGeneratorDev" }
    
    if ($solutions.Count -eq 1) {
        $TargetSolutionPath = $solutions[0].Directory.FullName
        Write-Host " ✓ Found: $($solutions[0].BaseName)" -ForegroundColor Green
    }
    elseif ($solutions.Count -gt 1) {
        Write-Host " Multiple solutions found. Select one:" -ForegroundColor Yellow
        for ($i = 0; $i -lt $solutions.Count; $i++) {
            Write-Host " [$i] $($solutions[$i].Name)" -ForegroundColor White
        }
        $selection = Read-Host "Enter number"
        if ($selection -match '^\d+$' -and [int]$selection -lt $solutions.Count) {
            $TargetSolutionPath = $solutions[[int]$selection].Directory.FullName
        }
        else {
            Write-Host "Invalid selection" -ForegroundColor Red
            exit 1
        }
    }
    else {
        # Look for directories containing .sln files (exclude CodeGeneratorDev)
        $apiDirs = Get-ChildItem -Directory | Where-Object { 
            $_.Name -ne "CodeGeneratorDev" -and
            (Get-ChildItem -Path $_.FullName -Filter "*.sln" -File).Count -gt 0
        }
    
        if ($apiDirs.Count -eq 1) {
            $TargetSolutionPath = $apiDirs[0].FullName
            Write-Host " ✓ Found: $($apiDirs[0].Name)" -ForegroundColor Green
        }
        elseif ($apiDirs.Count -gt 1) {
            Write-Host " Multiple solutions found. Select one:" -ForegroundColor Yellow
            for ($i = 0; $i -lt $apiDirs.Count; $i++) {
                Write-Host " [$i] $($apiDirs[$i].Name)" -ForegroundColor White
            }
            $selection = Read-Host "Enter number"
            if ($selection -match '^\d+$' -and [int]$selection -lt $apiDirs.Count) {
                $TargetSolutionPath = $apiDirs[[int]$selection].FullName
            }
            else {
                Write-Host "Invalid selection" -ForegroundColor Red
                exit 1
            }
        }
        else {
            Write-Host " ✗ No solution found in subdirectories" -ForegroundColor Red
            Write-Host " ℹ Looking for any folder (except CodeGeneratorDev) containing a .sln file" -ForegroundColor Gray
            exit 1
        }
    }
}

# Validate paths
if (-not (Test-Path $TargetSolutionPath)) {
    Write-Host "ERROR: Target solution not found: $TargetSolutionPath" -ForegroundColor Red
    exit 1
}

if (-not (Test-Path "$CodeGenSourcePath\CodeGeneratorDev\CodeGeneratorDev.csproj")) {
    Write-Host "ERROR: CodeGeneratorDev project not found at: $CodeGenSourcePath\CodeGeneratorDev" -ForegroundColor Red
    exit 1
}

$solutionFile = Get-ChildItem -Path $TargetSolutionPath -Filter "*.sln" | Select-Object -First 1
if (-not $solutionFile) {
    Write-Host "ERROR: No .sln file found in $TargetSolutionPath" -ForegroundColor Red
    exit 1
}

$projectName = $solutionFile.BaseName
Write-Host ""
Write-Host "Target solution: $projectName" -ForegroundColor White
Write-Host "Location: $TargetSolutionPath" -ForegroundColor Gray
Write-Host ""

try {
    Write-Host "Step 1: Copying CodeGeneratorDev project..." -ForegroundColor White
    
    $targetCodeGenPath = Join-Path $TargetSolutionPath "src\CodeGeneratorDev"
    
    if (Test-Path $targetCodeGenPath) {
        Write-Host " CodeGeneratorDev already exists in this solution" -ForegroundColor Yellow
        $overwrite = Read-Host " Overwrite? (y/n)"
        if ($overwrite -ne 'y') {
            Write-Host "Cancelled" -ForegroundColor Yellow
            exit 0
        }
        Remove-Item -Path $targetCodeGenPath -Recurse -Force
    }
    
    $srcPath = Join-Path $TargetSolutionPath "src"
    if (-not (Test-Path $srcPath)) {
        New-Item -ItemType Directory -Path $srcPath -Force | Out-Null
    }
    
    Copy-Item -Path "$CodeGenSourcePath\CodeGeneratorDev" -Destination $srcPath -Recurse -Force
    Write-Host " ✓ Copied to src\CodeGeneratorDev (original preserved)" -ForegroundColor Green
    
    Write-Host ""
    Write-Host "Step 2: Adding to solution..." -ForegroundColor White
    
    Push-Location $TargetSolutionPath
    
    $addResult = dotnet sln add "src\CodeGeneratorDev\CodeGeneratorDev.csproj" 2>&1
    if ($LASTEXITCODE -eq 0 -or $addResult -match "already in the solution") {
        Write-Host " ✓ Added to solution" -ForegroundColor Green
    }
    else {
        Write-Host " ⚠ Add may have failed: $addResult" -ForegroundColor Yellow
    }
    
    Write-Host ""
    Write-Host "Step 3: Adding project references..." -ForegroundColor White
    
    $projectsToReference = Get-ChildItem -Path "src" -Filter "*.csproj" -Recurse | 
    Where-Object { $_.Directory.Name -ne "CodeGeneratorDev" } |
    Where-Object { $_.Name -notlike "*Tests*" }
    
    if ($projectsToReference.Count -eq 0) {
        Write-Host " ⚠ No projects found to reference" -ForegroundColor Yellow
    }
    else {
        foreach ($proj in $projectsToReference) {
            $relativePath = $proj.FullName.Substring($TargetSolutionPath.Length + 1)
            $result = dotnet add "src\CodeGeneratorDev\CodeGeneratorDev.csproj" reference $relativePath 2>&1
            
            if ($LASTEXITCODE -eq 0 -or $result -match "already has a reference") {
                Write-Host " ✓ Referenced $($proj.Directory.Name)" -ForegroundColor Green
            }
            else {
                Write-Host " ⚠ Failed to reference $($proj.Directory.Name)" -ForegroundColor Yellow
            }
        }
    }
    
    Pop-Location
    
    Write-Host ""
    Write-Host "Step 4: Updating namespaces..." -ForegroundColor White

    $generatorFiles = Get-ChildItem -Path $targetCodeGenPath -Filter "*.cs" -Recurse
    $updated = 0

    foreach ($file in $generatorFiles) {
        $content = Get-Content -Path $file.FullName -Raw
        $modified = $false
    
        # Replace namespace declarations
        if ($content -match 'namespace CodeGeneratorDev') {
            $content = $content -replace 'namespace CodeGeneratorDev', "namespace $projectName.CodeGeneratorDev"
            $modified = $true
        }
    
        # Replace using statements
        if ($content -match 'using CodeGeneratorDev\.') {
            $content = $content -replace 'using CodeGeneratorDev\.', "using $projectName.CodeGeneratorDev."
            $modified = $true
        }
    
        if ($modified) {
            Set-Content -Path $file.FullName -Value $content -Encoding UTF8
            $updated++
        }
    }

    Write-Host " ✓ Updated $updated file(s)" -ForegroundColor Green
    
    Write-Host ""
    Write-Host "Step 4.5: Adapting for Central Package Management..." -ForegroundColor White

    $csprojPath = Join-Path $targetCodeGenPath "CodeGeneratorDev.csproj"
    $directoryPackagesPath = Join-Path $TargetSolutionPath "Directory.Packages.props"

    if (Test-Path $directoryPackagesPath) {
        Write-Host " ✓ CPM detected" -ForegroundColor Gray
    
        # Step A: Get packages from CodeGeneratorDev.csproj
        [xml]$csproj = Get-Content $csprojPath
        $requiredPackages = @()
    
        foreach ($itemGroup in $csproj.Project.ItemGroup) {
            if ($itemGroup.PackageReference) {
                foreach ($packageRef in $itemGroup.PackageReference) {
                    $packageName = $packageRef.GetAttribute("Include")
                    $packageVersion = $packageRef.GetAttribute("Version")
                
                    if ($packageName -and $packageVersion) {
                        $requiredPackages += @{
                            Name    = $packageName
                            Version = $packageVersion
                        }
                    
                        # Remove version from .csproj
                        $packageRef.RemoveAttribute("Version")
                    }
                }
            }
        }
    
        $csproj.Save($csprojPath)
        Write-Host " Stripped versions from $($requiredPackages.Count) packages" -ForegroundColor DarkGray
    
        # Step B: Check Directory.Packages.props for existing packages
        [xml]$dirPackages = Get-Content $directoryPackagesPath
        $existingPackages = @{}
    
        foreach ($itemGroup in $dirPackages.Project.ItemGroup) {
            if ($itemGroup.PackageVersion) {
                foreach ($pkgVer in $itemGroup.PackageVersion) {
                    $name = $pkgVer.GetAttribute("Include")
                    $version = $pkgVer.GetAttribute("Version")
                    $existingPackages[$name] = $version
                }
            }
        }
    
        # Step C: Add only truly missing packages (not already defined)
        $packagesToAdd = @()
    
        foreach ($pkg in $requiredPackages) {
            if (-not $existingPackages.ContainsKey($pkg.Name)) {
                $packagesToAdd += $pkg
            }
            else {
                Write-Host " ℹ $($pkg.Name) already defined (v$($existingPackages[$pkg.Name]))" -ForegroundColor DarkGray
            }
        }
    
        if ($packagesToAdd.Count -gt 0) {
            Write-Host " Adding $($packagesToAdd.Count) missing package(s)..." -ForegroundColor Yellow
        
            # Find the first ItemGroup to add packages to
            $itemGroup = $dirPackages.Project.ItemGroup | Select-Object -First 1
        
            foreach ($pkg in $packagesToAdd) {
                Write-Host " + $($pkg.Name) v$($pkg.Version)" -ForegroundColor DarkGray
            
                $newElement = $dirPackages.CreateElement("PackageVersion")
                $newElement.SetAttribute("Include", $pkg.Name)
                $newElement.SetAttribute("Version", $pkg.Version)
                $itemGroup.AppendChild($newElement) | Out-Null
            }
        
            $dirPackages.Save($directoryPackagesPath)
            Write-Host " ✓ Updated Directory.Packages.props" -ForegroundColor Green
        }
        else {
            Write-Host " ✓ All required packages already defined in CPM" -ForegroundColor Green
        }
    
    }
    else {
        Write-Host " ℹ No CPM detected - keeping package versions" -ForegroundColor Gray
    }

    Write-Host ""
    Write-Host "Step 4.6: Setting nullable to annotations for generated code compatibility..." -ForegroundColor White

    $csprojPath = Join-Path $targetCodeGenPath "CodeGeneratorDev.csproj"
    [xml]$csproj = Get-Content $csprojPath

    # Find or update the Nullable property
    $propertyGroup = $csproj.Project.PropertyGroup | Select-Object -First 1
    $nullableNode = $propertyGroup.SelectSingleNode("Nullable")

    if ($nullableNode) {
        $nullableNode.InnerText = "annotations"
        Write-Host " ✓ Changed <Nullable> to 'annotations'" -ForegroundColor Green
    }
    else {
        $newElement = $csproj.CreateElement("Nullable")
        $newElement.InnerText = "annotations"
        $propertyGroup.AppendChild($newElement) | Out-Null
        Write-Host " ✓ Added <Nullable>annotations</Nullable>" -ForegroundColor Green
    }

    $csproj.Save($csprojPath)

    Write-Host ""
    Write-Host "Step 4.7: Setting nullable to annotations in solution-wide settings..." -ForegroundColor White

    $directoryBuildPropsPath = Join-Path $TargetSolutionPath "Directory.Build.props"

    if (Test-Path $directoryBuildPropsPath) {
        $content = Get-Content $directoryBuildPropsPath -Raw
    
        if ($content -match '<Nullable>enable</Nullable>') {
            $content = $content -replace '<Nullable>enable</Nullable>', '<Nullable>annotations</Nullable>'
            Set-Content $directoryBuildPropsPath $content -NoNewline
            Write-Host " ✓ Changed <Nullable> from 'enable' to 'annotations' in Directory.Build.props" -ForegroundColor Green
        }
        elseif ($content -match '<Nullable>disable</Nullable>') {
            $content = $content -replace '<Nullable>disable</Nullable>', '<Nullable>annotations</Nullable>'
            Set-Content $directoryBuildPropsPath $content -NoNewline
            Write-Host " ✓ Changed <Nullable> from 'disable' to 'annotations' in Directory.Build.props" -ForegroundColor Green
        }
        else {
            Write-Host " ℹ Nullable already set to annotations or not found" -ForegroundColor Gray
        }
    }
    else {
        Write-Host " ℹ No Directory.Build.props found" -ForegroundColor Gray
    }


    Write-Host ""
    Write-Host "Step 5: Verifying build..." -ForegroundColor White
    
    Push-Location $TargetSolutionPath
    $buildResult = dotnet build "src\CodeGeneratorDev\CodeGeneratorDev.csproj" --verbosity quiet 2>&1
    Pop-Location
    
    if ($LASTEXITCODE -eq 0) {
        Write-Host " ✓ Build successful" -ForegroundColor Green
    }
    else {
        Write-Host " ⚠ Build issues detected - open in VS2022 to review" -ForegroundColor Yellow
    }
    
    # Step 6: Create launchSettings.json for VS2022 debugging
    Write-Host ""
    Write-Host "Step 6: Creating launchSettings.json..." -ForegroundColor White

    $launchSettingsDir = Join-Path $targetCodeGenPath "Properties"
    if (-not (Test-Path $launchSettingsDir)) {
        New-Item -ItemType Directory -Path $launchSettingsDir -Force | Out-Null
    }

    $launchSettingsFile = Join-Path $launchSettingsDir "launchSettings.json"

    # Create JSON with relative paths from solution root
    $launchSettings = @{
        profiles = @{
            "DryRun (Safe)"                  = @{
                commandName      = "Project"
                workingDirectory = "`$(SolutionDir)"
                commandLineArgs  = "artifacts/bin/Domain/debug/$projectName.Domain.dll . $projectName --dry-run"
            }
            "Production (Writes to Project)" = @{
                commandName      = "Project"
                workingDirectory = "`$(SolutionDir)"
                commandLineArgs  = "artifacts/bin/Domain/release/$projectName.Domain.dll . $projectName"
            }
        }
    }

    $launchSettings | ConvertTo-Json -Depth 10 | Set-Content -Path $launchSettingsFile -Encoding UTF8
    Write-Host " ✓ Created launchSettings.json with relative paths" -ForegroundColor Green


    Write-Host ""
    Write-Host "=== COPY COMPLETE ===" -ForegroundColor Green
    Write-Host ""
    Write-Host "Original CodeGeneratorDev remains at: $CodeGenSourcePath" -ForegroundColor Gray
    Write-Host "Copy integrated into: $projectName" -ForegroundColor White
    Write-Host ""
    Write-Host "Next: Open $projectName.sln in Visual Studio" -ForegroundColor White
    Write-Host ""
    
}
catch {
    Write-Host ""
    Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}