Public/Plugins/Get-DataversePluginRegistrations.ps1

function Get-DataversePluginRegistrations {
    <#
    .SYNOPSIS
        Extracts plugin registrations from compiled assemblies.

    .DESCRIPTION
        Loads compiled plugin DLLs via .NET reflection, finds all classes with
        [PluginStep] attributes, extracts step and image configurations, and
        generates registrations.json files for each plugin project.

    .PARAMETER AssemblyPath
        Path to a specific compiled plugin DLL.

    .PARAMETER RepositoryRoot
        Root of the repository to discover plugin projects.

    .PARAMETER Project
        Specific project name to extract. If not specified, extracts all.

    .PARAMETER Configuration
        Build configuration to use (Release or Debug). Default: Release.

    .PARAMETER Build
        Build projects before extraction.

    .PARAMETER OutputPath
        Custom output path for registrations.json. Default: project directory.

    .EXAMPLE
        Get-DataversePluginRegistrations -AssemblyPath "./bin/Release/net462/MyPlugins.dll" -OutputPath "./registrations.json"
        Extracts registrations from a specific assembly.

    .EXAMPLE
        Get-DataversePluginRegistrations -RepositoryRoot "." -Build
        Discovers and extracts all plugin projects in the repository.

    .OUTPUTS
        Array of assembly registration objects.
    #>

    [CmdletBinding()]
    param(
        [Parameter(ParameterSetName = "Assembly")]
        [string]$AssemblyPath,

        [Parameter(ParameterSetName = "Repository")]
        [string]$RepositoryRoot,

        [Parameter(ParameterSetName = "Repository")]
        [string]$Project,

        [Parameter(ParameterSetName = "Repository")]
        [ValidateSet("Release", "Debug")]
        [string]$Configuration = "Release",

        [Parameter(ParameterSetName = "Repository")]
        [switch]$Build,

        [Parameter()]
        [string]$OutputPath
    )

    Write-Log "Plugin Registration Extractor"

    # Single assembly mode
    if ($AssemblyPath) {
        if (-not (Test-Path $AssemblyPath)) {
            throw "Assembly not found: $AssemblyPath"
        }

        Write-Log "Extracting from: $AssemblyPath"

        $allTypeNames = Get-AllPluginTypeNames -DllPath $AssemblyPath
        $plugins = Get-PluginRegistrationsFromAssembly -DllPath $AssemblyPath

        $assemblyName = [System.IO.Path]::GetFileNameWithoutExtension($AssemblyPath)
        $relativePath = (Resolve-Path -Path $AssemblyPath -Relative -ErrorAction SilentlyContinue) -replace '\\','/'

        $assemblyReg = [PSCustomObject]@{
            name = $assemblyName
            type = "Assembly"
            solution = $null
            path = $relativePath
            allTypeNames = $allTypeNames
            plugins = $plugins
        }

        if ($OutputPath) {
            $registrationJson = ConvertTo-RegistrationJson -Assemblies @($assemblyReg)
            [System.IO.File]::WriteAllText($OutputPath, $registrationJson, [System.Text.UTF8Encoding]::new($false))
            Write-LogSuccess "Generated: $OutputPath"
        }

        return @($assemblyReg)
    }

    # Repository discovery mode
    if (-not $RepositoryRoot) {
        $RepositoryRoot = Get-Location
    }

    Write-Log "Repository: $RepositoryRoot"
    Write-Log "Configuration: $Configuration"

    Push-Location $RepositoryRoot
    try {
        Write-Log "Discovering plugin projects..."
        $allProjects = Get-PluginProjects -RepositoryRoot $RepositoryRoot

        if ($allProjects.Count -eq 0) {
            Write-LogWarning "No plugin projects found"
            return @()
        }

        Write-Log "Found $($allProjects.Count) plugin project(s)"

        $projects = if ($Project) {
            $filtered = $allProjects | Where-Object { $_.Name -eq $Project }
            if (-not $filtered) {
                Write-LogError "Project not found: $Project"
                Write-Log "Available projects:"
                $allProjects | ForEach-Object { Write-Log " - $($_.Name) ($($_.Type))" }
                throw "Project not found: $Project"
            }
            $filtered
        } else {
            $allProjects
        }

        if ($Build) {
            Write-Log ""
            Write-Log "Building projects..."
            foreach ($proj in $projects) {
                Write-Log "Building: $($proj.Name)"
                $buildResult = & dotnet build $proj.ProjectPath -c $Configuration --nologo -v q 2>&1
                if ($LASTEXITCODE -ne 0) {
                    Write-LogError "Build failed for $($proj.Name)"
                    throw "Build failed"
                }
                Write-LogSuccess " Built successfully"

                $dllPath = Join-Path $proj.ProjectDir "bin/$Configuration/net462/$($proj.Name).dll"
                if (Test-Path $dllPath) {
                    $proj.DllPath = $dllPath
                    $proj.RelativeDllPath = (Resolve-Path -Path $dllPath -Relative) -replace '\\','/'
                }
            }
        }

        Write-Log ""
        Write-Log "Extracting plugin registrations..."

        $allResults = @()
        $successCount = 0
        $errorCount = 0

        foreach ($proj in $projects) {
            Write-Log ""
            Write-Log "Processing: $($proj.Name) ($($proj.Type))"

            if (-not $proj.DllPath -or -not (Test-Path $proj.DllPath)) {
                Write-LogWarning " DLL not found. Run with -Build or build manually first."
                $errorCount++
                continue
            }

            Write-Log " DLL: $($proj.RelativeDllPath)"

            try {
                $allTypeNames = Get-AllPluginTypeNames -DllPath $proj.DllPath
                $plugins = Get-PluginRegistrationsFromAssembly -DllPath $proj.DllPath

                if ($plugins.Count -eq 0) {
                    Write-LogWarning " No plugins with [PluginStep] attributes found"
                }

                Write-Log " Found $($plugins.Count) plugin class(es) with step registrations"

                # Check for existing registrations.json to preserve solution property
                $existingJsonPath = Join-Path $proj.ProjectDir "registrations.json"
                $existingSolution = $null
                $existingPackagePath = $null
                if (Test-Path $existingJsonPath) {
                    try {
                        $existingReg = Read-RegistrationJson -Path $existingJsonPath
                        if ($existingReg -and $existingReg.assemblies) {
                            $existingAsm = $existingReg.assemblies | Where-Object { $_.name -eq $proj.Name } | Select-Object -First 1
                            if ($existingAsm) {
                                $existingSolution = $existingAsm.solution
                                $existingPackagePath = $existingAsm.packagePath
                            }
                        }
                    } catch {
                        Write-LogDebug " Could not read existing registrations.json"
                    }
                }

                $assemblyReg = [PSCustomObject]@{
                    name = $proj.Name
                    type = $proj.Type
                    solution = $existingSolution
                    path = $proj.RelativeDllPath
                    allTypeNames = $allTypeNames
                    plugins = $plugins
                }

                $packagePath = if ($proj.RelativeNupkgPath) { $proj.RelativeNupkgPath } else { $existingPackagePath }
                if ($proj.Type -eq "Nuget" -and $packagePath) {
                    $assemblyReg | Add-Member -MemberType NoteProperty -Name "packagePath" -Value $packagePath
                }

                $registrationJson = ConvertTo-RegistrationJson -Assemblies @($assemblyReg)

                $jsonOutputPath = if ($OutputPath) { $OutputPath } else { Join-Path $proj.ProjectDir "registrations.json" }

                [System.IO.File]::WriteAllText($jsonOutputPath, $registrationJson, [System.Text.UTF8Encoding]::new($false))
                Write-LogSuccess " Generated: $jsonOutputPath"

                $allResults += $assemblyReg
                $successCount++
            }
            catch {
                Write-LogError " Failed to extract: $($_.Exception.Message)"
                $errorCount++
            }
        }

        Write-Log ""
        Write-Log ("=" * 60)
        Write-Log "Extraction Summary"
        Write-Log ("=" * 60)
        Write-Log "Projects processed: $($projects.Count)"
        Write-LogSuccess "Successful: $successCount"
        if ($errorCount -gt 0) {
            Write-LogError "Failed: $errorCount"
        }

        return $allResults
    }
    finally {
        Pop-Location
    }
}