#Requires -Version 7.4

function Get-PluginAnalysis {
    <#
    .SYNOPSIS
        Analyzes a plugin's template to determine planned changes.

    .DESCRIPTION
        Loads the plugin's template (if declarative) and evaluates:
        - Which groups will execute (conditions pass)
        - Which groups will skip (conditions fail)
        - Which actions will change state
        - Which actions are already at target (no-op)
        - Rollback capability assessment

    .PARAMETER PluginEntry
        Plugin entry from v2 registry with Name, Phases, Priority, Critical,
        ScriptContent, PluginPath, and other flat fields (no .Metadata wrapper).

    .PARAMETER Globals
        Globals hashtable for condition evaluation.

    .OUTPUTS
        [PSCustomObject] Analysis result with properties:
        - Plugin: Plugin name
        - HasTemplate: Whether plugin has declarative template
        - HasCustomAnalyze: Whether plugin has Invoke-Analyze function
        - Groups: Array of group analysis results
        - TotalGroups: Total group count
        - ExecuteGroups: Groups that will execute
        - SkipGroups: Groups that will skip (conditions)
        - TotalActions: Total action count
        - ChangeActions: Actions that will modify state
        - NoOpActions: Actions already at target
        - SkippedActions: Actions skipped by condition
        - RollbackCapable: Whether plugin supports rollback
        - CommandCount: Count of command actions (not reversible)
    #>
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [hashtable]$PluginEntry,

        [Parameter()]
        [hashtable]$Globals,

        [Parameter()]
        [hashtable]$Config
    )

    $result = @{
        Plugin = $PluginEntry.Name
        PluginId = $PluginEntry.Name
        HasTemplate = $false
        HasCustomAnalyze = $false
        Groups = @()
        TotalGroups = 0
        ExecuteGroups = 0
        SkipGroups = 0
        TotalActions = 0
        ChangeActions = 0
        NoOpActions = 0
        SkippedActions = 0
        RollbackCapable = $false  # Updated after module creation via Get-Command check
        CommandCount = 0
    }

    # Check for custom Invoke-Analyze in plugin
    $pluginModule = $null
    try {
        $pluginModule = New-Module -Name "vSEAL_Analyze_$([guid]::NewGuid().ToString('N'))" -ScriptBlock (
            [scriptblock]::Create($PluginEntry.ScriptContent)
        )
        $hasCustomAnalyze = Get-Command -Name 'Invoke-Analyze' -Module $pluginModule -ErrorAction SilentlyContinue
        $result.HasCustomAnalyze = $null -ne $hasCustomAnalyze

        # Detect rollback capability dynamically via exported Invoke-Rollback function
        $hasRollback = Get-Command -Name 'Invoke-Rollback' -Module $pluginModule -ErrorAction SilentlyContinue
        $result.RollbackCapable = $null -ne $hasRollback

        if ($result.HasCustomAnalyze) {
            # Call plugin's custom analyze
            $customResult = & $pluginModule {
                param($Config, $Globals)
                Invoke-Analyze -Config $Config -Globals $Globals
            } -Config $Config -Globals $Globals

            # Merge custom result if it returns expected format
            if ($customResult -is [hashtable] -or $customResult -is [PSCustomObject]) {
                foreach ($key in @('TotalActions', 'ChangeActions', 'NoOpActions')) {
                    if ($customResult.$key) { $result[$key] = $customResult.$key }
                }
            }

            return [PSCustomObject]$result
        }
    }
    finally {
        if ($pluginModule) {
            Remove-Module -ModuleInfo $pluginModule -Force -ErrorAction SilentlyContinue
        }
    }

    # Discover template file by convention: non-plugin.toml .toml file in plugin folder
    $templatePath = $null
    if ($PluginEntry.PluginPath -and (Test-Path $PluginEntry.PluginPath)) {
        $tomlFiles = Get-ChildItem -Path $PluginEntry.PluginPath -Filter '*.toml' -File |
            Where-Object { $_.Name -ne 'plugin.toml' }
        if ($tomlFiles) {
            $templatePath = $tomlFiles[0].FullName
        }
    }
    if (-not $templatePath -or -not (Test-Path $templatePath)) {
        # No template - imperative plugin without custom analyze
        return [PSCustomObject]$result
    }

    $result.HasTemplate = $true

    # Parse template
    try {
        $template = Import-vSEALTemplate -Path $templatePath
    }
    catch {
        Write-vSEALLog "Failed to parse template for $($result.Plugin): $($_.Exception.Message)" -Level WARNING
        return [PSCustomObject]$result
    }

    # Analyze each group
    foreach ($group in $template.Groups) {
        $result.TotalGroups++

        $groupAnalysis = @{
            Name = $group.name
            WillExecute = $true
            SkipReason = $null
            Actions = @()
        }

        # Evaluate group conditions using public Test-vSEALCondition (Phase 18)
        if ($group.conditions -and $group.conditions.Count -gt 0) {
            foreach ($condition in $group.conditions) {
                $condResult = Test-vSEALCondition -Condition $condition -Globals $Globals
                if (-not $condResult) {
                    $groupAnalysis.WillExecute = $false
                    $groupAnalysis.SkipReason = Format-ConditionReason -Condition $condition -Globals $Globals
                    break
                }
            }
        }

        if ($groupAnalysis.WillExecute) {
            $result.ExecuteGroups++

            # Analyze each action
            foreach ($action in $group.actions) {
                $result.TotalActions++

                # Check per-action conditions
                $actionSkipped = $false
                if ($action.conditions -and $action.conditions.Count -gt 0) {
                    foreach ($actionCondition in $action.conditions) {
                        if (-not (Test-vSEALCondition -Condition $actionCondition -Globals $Globals)) {
                            $actionSkipped = $true
                            $result.SkippedActions++
                            break
                        }
                    }
                }

                $willChange = $false
                if (-not $actionSkipped) {
                    # Check if action will change state
                    $willChange = Test-ActionWillChange -Action $action

                    if ($action.type -eq 'command') {
                        $result.CommandCount++
                    }

                    if ($willChange) {
                        $result.ChangeActions++
                    }
                    else {
                        $result.NoOpActions++
                    }
                }

                $groupAnalysis.Actions += @{
                    Type = $action.type
                    Target = Get-ActionTargetForDisplay -Action $action
                    WillChange = (-not $actionSkipped) -and $willChange
                    Skipped = $actionSkipped
                }
            }
        }
        else {
            $result.SkipGroups++
            # Count actions as skipped
            $result.TotalActions += $group.actions.Count
            $result.SkippedActions += $group.actions.Count
        }

        $result.Groups += [PSCustomObject]$groupAnalysis
    }

    return [PSCustomObject]$result
}

# ===== PRIVATE HELPER FUNCTIONS (embedded in Get-PluginAnalysis.ps1) =====

function Test-ActionWillChange {
    <#
    .SYNOPSIS
        Checks if an action will actually modify system state.
    .NOTES
        Private helper - not exported. Embedded in Get-PluginAnalysis.ps1.
    #>
    param([hashtable]$Action)

    switch ($Action.type) {
        'registry' {
            $key = $Action.key
            $name = $Action.name
            try {
                $current = Get-ItemProperty -Path $key -Name $name -ErrorAction SilentlyContinue
                if ($null -eq $current) { return $true }  # Doesn't exist, will change

                $currentValue = $current.$name
                $targetValue = $Action.data

                # Compare values
                return $currentValue -ne $targetValue
            }
            catch {
                return $true  # Error reading = assume will change
            }
        }
        'service' {
            try {
                $service = Get-Service -Name $Action.name -ErrorAction SilentlyContinue
                if ($null -eq $service) { return $false }  # Service doesn't exist

                # Use existing public function Get-vSEALServiceStartMode
                $currentMode = (Get-vSEALServiceStartMode -Service $Action.name).StartMode
                return $currentMode -ne $Action.start_mode
            }
            catch {
                return $true
            }
        }
        'task' {
            try {
                # Use public function for existence check (handles path parsing and errors)
                if (-not (Test-vSEALTaskExists -Name $Action.name)) {
                    return $false  # Task doesn't exist, no change possible
                }

                # Task exists — check current state vs target
                $normalizedName = $Action.name.TrimStart('\')
                $parts = $normalizedName -split '\\'
                $taskName = $parts[-1]
                $taskPath = if ($parts.Length -gt 1) {
                    '\' + ($parts[0..($parts.Length - 2)] -join '\') + '\'
                } else { '\' }

                $task = Get-ScheduledTask -TaskPath $taskPath -TaskName $taskName -ErrorAction SilentlyContinue
                if ($null -eq $task) { return $false }

                $targetState = if ($Action.action -eq 'disable') { 'Disabled' } else { 'Ready' }
                return $task.State -ne $targetState
            }
            catch {
                return $false  # Can't determine state, assume no change
            }
        }
        'command' {
            return $true  # Commands always execute
        }
        default {
            return $true  # Unknown type - assume will change
        }
    }
}

function Get-ActionTargetForDisplay {
    <#
    .SYNOPSIS
        Gets a display-friendly target string for an action.
    .NOTES
        Private helper - not exported. Embedded in Get-PluginAnalysis.ps1.
    #>
    param([hashtable]$Action)

    switch ($Action.type) {
        'registry' { return "$($Action.key)\$($Action.name)" }
        'service' { return $Action.name }
        'task' { return $Action.name }
        'command' { return ($Action.command -split '\r?\n')[0] }  # First line only
        default { return $Action.type }
    }
}

function Format-ConditionReason {
    <#
    .SYNOPSIS
        Formats a human-readable reason for why a condition failed.
    .NOTES
        Private helper - not exported. Embedded in Get-PluginAnalysis.ps1.
    #>
    param(
        [hashtable]$Condition,
        [hashtable]$Globals
    )

    switch ($Condition.type) {
        'globals' {
            $parts = $Condition.path -split '\.'
            $currentValue = $Globals
            foreach ($part in $parts) {
                if ($null -eq $currentValue) { break }
                $currentValue = $currentValue[$part]
            }
            return "$($Condition.path)=$currentValue $($Condition.operator) $($Condition.value)"
        }
        'registry' {
            return "Registry $($Condition.key) $($Condition.operator)"
        }
        'service' {
            return "Service $($Condition.name) $($Condition.operator)"
        }
        'task' {
            return "Task $($Condition.name) $($Condition.operator)"
        }
        default {
            return "$($Condition.type) condition failed"
        }
    }
}
