src/functions/invoke-entry.ps1

function Invoke-EntryWrapper {
    param(
        [string]$MainCommand,
        [string]$TargetKey,
        $TargetEntry,
        [string]$Command,
        [hashtable]$Bound,
        [string[]]$RemainingArguments
    )

    $context = @{
        MainCommand        = $MainCommand
        TargetKey          = $TargetKey
        TargetEntry        = $TargetEntry
        Command            = $Command
        Bound              = $Bound
        RemainingArguments = $RemainingArguments
    }

    $settings = if ($TargetEntry -is [System.Collections.IDictionary]) { $TargetEntry._settings } else { $null }
    $settingsScope = Enter-ConfigMapSettingsScope -Settings $settings
    try {
        $hookResult = Invoke-ConfigMapPluginHooks -HookName 'InvokeEntryWrapper' -Context $context
        if ($hookResult.Handled) {
            return $hookResult.Result
        }

        Invoke-EntryCommand -entry $TargetEntry -key $Command -bound $Bound -settingsScopeEntered $true
    }
    finally {
        Exit-ConfigMapSettingsScope -PreviousSettings $settingsScope
    }
}

function Invoke-EntryCommand($entry, $key = "exec", $ordered = @(), $bound = @{}, [switch]$settingsScopeEntered) {
    if (-not $settingsScopeEntered) {
        $settings = if ($entry -is [System.Collections.IDictionary]) { $entry._settings } else { $null }
        $settingsScope = Enter-ConfigMapSettingsScope -Settings $settings
    }
    try {
        $command = Get-EntryCommand $entry $key
        
        if (!$command) {
            throw "Command '$key' not found"
        }
        
        # Handle exec as list of subcommand names
        if ($command -is [array]) {
            if ($entry -isnot [System.Collections.IDictionary]) {
                throw "Entry must be a hashtable when exec is an array"
            }

            $commandList = $command -join ", "
            Write-Host "Resolved to commands: [$commandList]" -ForegroundColor Cyan

            for ($i = 0; $i -lt $command.Count; $i++) {
                $subCommandName = $command[$i]
                Write-Host "[$($i + 1)/$($command.Count)] Running '$subCommandName'..." -ForegroundColor Yellow

                if (!$entry.$subCommandName) {
                    throw "Subcommand '$subCommandName' not found in entry"
                }

                $subEntry = $entry.$subCommandName
                Invoke-EntryCommand -entry $subEntry -ordered $ordered -bound $bound
            }

            return
        }
        
        # Normal scriptblock execution
        $scriptArgs = Get-ScriptArgs $command -exclude @()

        if ($command -isnot [scriptblock]) {
            throw "Entry '$key' of type $($command.GetType().Name) is not supported"
        }

        if (!$bound) { $bound = @{} }
        if (!$bound._context) { $bound._context = @{} }
        if (!$bound._context.self) { $bound._context.self = $entry }

        $baseDir = $null
        if ($entry -is [System.Collections.IDictionary] -and $entry._baseDir) {
            $baseDir = $entry._baseDir
        }
        if (!$baseDir) {
            $baseDir = Get-Location
        }
        if (!$bound._context.workDir) { $bound._context.workDir = (Get-Location).Path }

        # Always pass special parameters (_context, _self) plus any that match script params
        $specialParams = @("_context", "_self") | ? { $scriptArgs.Keys -contains $_ }
        $filtered = @{}
        Write-Verbose "script args: $( $scriptArgs.Keys -join ', ' )"
        foreach ($boundKey in $bound.Keys) {
            if ($boundKey -in $scriptArgs.Keys -or $boundKey -in $specialParams) {
                Write-Verbose "adding '$boundKey'"
                $filtered[$boundKey] = $bound[$boundKey]
            }
            else {
                Write-Verbose "skipping '$boundKey'"
            }
        }
        
        try {
            pushd $baseDir
            return & $command @ordered @filtered
        }
        finally {
            popd
        }
    }
    finally {
        if (-not $settingsScopeEntered) {
            Exit-ConfigMapSettingsScope -PreviousSettings $settingsScope
        }
    }
}

# function Invoke-Entry(
# [ValidateScript({
# $_ -is [string] -or $_ -is [System.Collections.IDictionary]
# })]
# $map,
# $entry,
# $bound
# ) {
# $map = Import-ConfigMap $map

# $targets = Get-MapEntries $map $entry
# Write-Verbose "running targets: $($targets.Key)"

# @($targets) | % {
# Write-Verbose "running entry '$($_.key)'"
# Invoke-EntryCommand -entry $_.value -key "exec" -bound $bound
# }
# }

function Invoke-Set($entry, $bound = @{}) {
    # use ordered parameters, just in case the handler has different parameter names
    Invoke-EntryCommand $entry "set" -ordered @("", $bound.key, $bound.value) -bound $bound
}

function Invoke-Get($entry, $bound = @{}) {
    # use ordered parameters, just in case the handler has different parameter names
    Invoke-EntryCommand $entry "get" -ordered @("", $bound.options) -bound $bound
}