Forge.psm1

function Invoke-ForgeCommand {
    <#
    .SYNOPSIS
    Resolves the provider and invokes the target command.
    Used as a fallback for providers not in the known set.
    For known providers, each forge command has explicit inline mapping.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]
        $TargetCommand,

        [Parameter()]
        [hashtable]
        $Parameters = @{}
    )

    $Cmd = Get-Command $TargetCommand -ErrorAction SilentlyContinue
    if (-not $Cmd) {
        throw "Command '$TargetCommand' is not available. Is the provider module loaded?"
    }

    # Pass through only parameters the target command accepts
    $ValidParams = @{}
    foreach ($Key in $Parameters.Keys) {
        if ($Cmd.Parameters.ContainsKey($Key)) {
            $ValidParams[$Key] = $Parameters[$Key]
        }
    }

    & $TargetCommand @ValidParams
}

function Resolve-ForgeCommand {
    <#
    .SYNOPSIS
    Resolves the provider and returns the target command name.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]
        $CommandName,

        [Parameter()]
        [string]
        $Provider
    )

    $Resolved = Resolve-ForgeProvider -Provider $Provider -CommandName $CommandName

    $TargetCommand = $Resolved.Commands[$CommandName]
    if (-not $TargetCommand) {
        $Available = ($Resolved.Commands.Keys | Sort-Object) -join ', '
        throw "Provider '$($Resolved.Name)' does not support '$CommandName'. Available: $Available"
    }

    [PSCustomObject]@{
        ProviderName = $Resolved.Name.ToLower()
        Command      = $TargetCommand
    }
}

# =============================================================================
# Unified Commands
# =============================================================================

function Get-Issue {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Id,

        [Parameter()]
        [ValidateSet('open', 'closed', 'all')]
        [string]
        $State,

        [Parameter()]
        [switch]
        $Mine,

        [Parameter()]
        [string]
        $Group,

        [Parameter()]
        [string]
        $Assignee,

        [Parameter()]
        [string]
        $Author,

        [Parameter()]
        [string]
        $Labels,

        [Parameter()]
        [string]
        $Since,

        [Parameter()]
        [ValidateSet('created', 'updated', 'comments')]
        [string]
        $Sort,

        [Parameter()]
        [ValidateSet('asc', 'desc')]
        [string]
        $Direction,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-Issue' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Id)        { $Params.IssueId     = $Id }
            if ($State)     { $Params.State        = $State }
            if ($Mine)      { $Params.Mine          = $true }
            if ($Group)     { $Params.Organization  = $Group }
            if ($Assignee)  { $Params.Assignee      = $Assignee }
            if ($Author)    { $Params.Creator        = $Author }
            if ($Labels)    { $Params.Labels         = $Labels }
            if ($Since)     { $Params.Since          = $Since }
            if ($Sort)      { $Params.Sort           = $Sort }
            if ($Direction) { $Params.Direction       = $Direction }
            if ($MaxPages)  { $Params.MaxPages       = $MaxPages }
            if ($All)       { $Params.All            = $true }
        }
        'gitlab' {
            if ($Id)        { $Params.IssueId          = $Id }
            if ($Mine)      { $Params.Mine              = $true }
            if ($Group)     { $Params.GroupId            = $Group }
            if ($Assignee)  { $Params.AssigneeUsername   = $Assignee }
            if ($Author)    { $Params.AuthorUsername     = $Author }
            if ($Since)     { $Params.CreatedAfter       = $Since }
            if ($MaxPages)  { $Params.MaxPages           = $MaxPages }
            if ($All)       { $Params.All                = $true }
            if ($State) {
                $Params.State = switch ($State) {
                    'open'   { 'opened' }
                    'closed' { 'closed' }
                    'all'    { $null }
                }
            }
            if ($Labels)    { Write-Warning "Get-Issue -Labels is not yet supported by the Gitlab provider" }
            if ($Sort)      { Write-Warning "Get-Issue -Sort is not yet supported by the Gitlab provider" }
            if ($Direction) { Write-Warning "Get-Issue -Direction is not yet supported by the Gitlab provider" }
        }
        default {
            # Unknown provider: best-effort pass-through
            $AllParams = @{}
            if ($Id)        { $AllParams.Id        = $Id }
            if ($State)     { $AllParams.State     = $State }
            if ($Mine)      { $AllParams.Mine      = $true }
            if ($Group)     { $AllParams.Group     = $Group }
            if ($Assignee)  { $AllParams.Assignee  = $Assignee }
            if ($Author)    { $AllParams.Author    = $Author }
            if ($Labels)    { $AllParams.Labels    = $Labels }
            if ($Since)     { $AllParams.Since     = $Since }
            if ($Sort)      { $AllParams.Sort      = $Sort }
            if ($Direction) { $AllParams.Direction  = $Direction }
            if ($MaxPages)  { $AllParams.MaxPages  = $MaxPages }
            if ($All)       { $AllParams.All       = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Get-ChangeRequest {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Id,

        [Parameter()]
        [ValidateSet('open', 'closed', 'all', 'merged')]
        [string]
        $State,

        [Parameter()]
        [switch]
        $Mine,

        [Parameter()]
        [string]
        $Group,

        [Parameter()]
        [Alias('Branch')]
        [string]
        $SourceBranch,

        [Parameter()]
        [string]
        $TargetBranch,

        [Parameter()]
        [string]
        $Author,

        [Parameter()]
        [switch]
        $IsDraft,

        [Parameter()]
        [string]
        $Since,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-ChangeRequest' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Id)           { $Params.PullRequestId     = $Id }
            if ($State) {
                $Params.State = switch ($State) {
                    'open'   { 'open' }
                    'closed' { 'closed' }
                    'all'    { 'all' }
                    'merged' { 'closed' }
                }
                if ($State -eq 'merged') {
                    Write-Warning "Github does not have a 'merged' state filter; using 'closed' instead"
                }
            }
            if ($Mine)         { $Params.Mine      = $true }
            if ($SourceBranch) { $Params.Head       = $SourceBranch }
            if ($TargetBranch) { $Params.Base        = $TargetBranch }
            if ($MaxPages)     { $Params.MaxPages    = $MaxPages }
            if ($All)          { $Params.All         = $true }
            if ($Author)       { Write-Warning "Get-ChangeRequest -Author is not yet supported by the Github provider" }
            if ($IsDraft)      { Write-Warning "Get-ChangeRequest -IsDraft is not yet supported by the Github provider" }
            if ($Since)        { Write-Warning "Get-ChangeRequest -Since is not yet supported by the Github provider" }
        }
        'gitlab' {
            if ($Id)           { $Params.MergeRequestId = $Id }
            if ($Mine)         { $Params.Mine            = $true }
            if ($Group)        { $Params.GroupId          = $Group }
            if ($SourceBranch) { $Params.SourceBranch     = $SourceBranch }
            if ($Author)       { $Params.Username         = $Author }
            if ($IsDraft)      { $Params.IsDraft          = $true }
            if ($Since)        { $Params.CreatedAfter     = $Since }
            if ($MaxPages)     { $Params.MaxPages         = $MaxPages }
            if ($All)          { $Params.All              = $true }
            if ($State) {
                $Params.State = switch ($State) {
                    'open'   { 'opened' }
                    'closed' { 'closed' }
                    'all'    { 'all' }
                    'merged' { 'merged' }
                }
            }
            if ($TargetBranch) { Write-Warning "Get-ChangeRequest -TargetBranch is not yet supported by the Gitlab provider" }
        }
        default {
            $AllParams = @{}
            if ($Id)           { $AllParams.Id           = $Id }
            if ($State)        { $AllParams.State        = $State }
            if ($Mine)         { $AllParams.Mine         = $true }
            if ($Group)        { $AllParams.Group        = $Group }
            if ($SourceBranch) { $AllParams.SourceBranch = $SourceBranch }
            if ($TargetBranch) { $AllParams.TargetBranch = $TargetBranch }
            if ($Author)       { $AllParams.Author       = $Author }
            if ($IsDraft)      { $AllParams.IsDraft      = $true }
            if ($Since)        { $AllParams.Since        = $Since }
            if ($MaxPages)     { $AllParams.MaxPages     = $MaxPages }
            if ($All)          { $AllParams.All          = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Get-Branch {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Name,

        [Parameter()]
        [switch]
        $Protected,

        [Parameter()]
        [string]
        $Search,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-Branch' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Name)      { $Params.Name      = $Name }
            if ($Protected) { $Params.Protected  = $true }
            if ($MaxPages)  { $Params.MaxPages   = $MaxPages }
            if ($All)       { $Params.All        = $true }
            if ($Search)    { Write-Warning "Get-Branch -Search is not supported by the Github provider; use -Name for exact match" }
        }
        'gitlab' {
            if ($Name)      { $Params.Ref        = $Name }
            if ($Search)    { $Params.Search     = $Search }
            if ($MaxPages)  { $Params.MaxPages   = $MaxPages }
            if ($All)       { $Params.All        = $true }
            if ($Protected) { Write-Warning "Get-Branch -Protected is not supported by the Gitlab provider; use Get-GitlabProtectedBranch" }
        }
        default {
            $AllParams = @{}
            if ($Name)      { $AllParams.Name      = $Name }
            if ($Protected) { $AllParams.Protected  = $true }
            if ($Search)    { $AllParams.Search     = $Search }
            if ($MaxPages)  { $AllParams.MaxPages   = $MaxPages }
            if ($All)       { $AllParams.All        = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Get-Release {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Tag,

        [Parameter()]
        [switch]
        $Latest,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-Release' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Tag)      { $Params.Tag      = $Tag }
            if ($Latest)   { $Params.Latest   = $true }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
        }
        'gitlab' {
            if ($Tag)      { $Params.Tag      = $Tag }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
            if ($Latest)   { Write-Warning "Get-Release -Latest is not supported by the Gitlab provider" }
        }
        default {
            $AllParams = @{}
            if ($Tag)      { $AllParams.Tag      = $Tag }
            if ($Latest)   { $AllParams.Latest   = $true }
            if ($MaxPages) { $AllParams.MaxPages = $MaxPages }
            if ($All)      { $AllParams.All      = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Get-User {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Username,

        [Parameter()]
        [switch]
        $Me,

        [Parameter()]
        [string]
        $Select,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-User' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Username) { $Params.Username = $Username }
            if ($Me)       { $Params.Me       = $true }
            if ($Select)   { $Params.Select   = $Select }
        }
        'gitlab' {
            if ($Username) { $Params.UserId   = $Username }
            if ($Me)       { $Params.Me       = $true }
            if ($Select)   { $Params.Select   = $Select }
        }
        default {
            $AllParams = @{}
            if ($Username) { $AllParams.Username = $Username }
            if ($Me)       { $AllParams.Me       = $true }
            if ($Select)   { $AllParams.Select   = $Select }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Get-Group {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Name,

        [Parameter()]
        [switch]
        $Mine,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-Group' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Name)     { $Params.Name     = $Name }
            if ($Mine)     { $Params.Mine     = $true }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
        }
        'gitlab' {
            if ($Name)     { $Params.GroupId  = $Name }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
            if ($Mine)     { Write-Warning "Get-Group -Mine is not directly supported by the Gitlab provider" }
        }
        default {
            $AllParams = @{}
            if ($Name)     { $AllParams.Name     = $Name }
            if ($Mine)     { $AllParams.Mine     = $true }
            if ($MaxPages) { $AllParams.MaxPages = $MaxPages }
            if ($All)      { $AllParams.All      = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function New-Issue {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string]
        $Title,

        [Parameter()]
        [string]
        $Description,

        [Parameter()]
        [string[]]
        $Assignees,

        [Parameter()]
        [string[]]
        $Labels,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'New-Issue' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.Title = $Title
            if ($Description) { $Params.Description = $Description }
            if ($Assignees)   { $Params.Assignees   = $Assignees }
            if ($Labels)      { $Params.Labels      = $Labels }
        }
        'gitlab' {
            $Params.Title = $Title
            if ($Description) { $Params.Description = $Description }
            if ($Assignees)   { Write-Warning "New-Issue -Assignees is not yet supported by the Gitlab provider" }
            if ($Labels)      { $Params.Labels      = $Labels -join ',' }
        }
        default {
            $AllParams = @{ Title = $Title }
            if ($Description) { $AllParams.Description = $Description }
            if ($Assignees)   { $AllParams.Assignees   = $Assignees }
            if ($Labels)      { $AllParams.Labels      = $Labels }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    if ($PSCmdlet.ShouldProcess($Title, 'Create Issue')) {
        & $Target.Command @Params
    }
}

function Update-Issue {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, Position=0)]
        [string]
        $Id,

        [Parameter()]
        [string]
        $Title,

        [Parameter()]
        [string]
        $Description,

        [Parameter()]
        [ValidateSet('open', 'closed')]
        [string]
        $State,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Update-Issue' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.IssueId = $Id
            if ($Title)       { $Params.Title       = $Title }
            if ($Description) { $Params.Description = $Description }
            if ($State)       { $Params.State       = $State }
        }
        'gitlab' {
            $Params.IssueId = $Id
            if ($Title)       { $Params.Title       = $Title }
            if ($Description) { $Params.Description = $Description }
            if ($State) {
                $Params.StateEvent = switch ($State) {
                    'open'   { 'reopen' }
                    'closed' { 'close' }
                }
            }
        }
        default {
            $AllParams = @{ Id = $Id }
            if ($Title)       { $AllParams.Title       = $Title }
            if ($Description) { $AllParams.Description = $Description }
            if ($State)       { $AllParams.State       = $State }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    if ($PSCmdlet.ShouldProcess($Id, 'Update Issue')) {
        & $Target.Command @Params
    }
}

function Close-Issue {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, Position=0)]
        [string]
        $Id,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Close-Issue' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.IssueId = $Id
        }
        'gitlab' {
            $Params.IssueId = $Id
        }
        default {
            $AllParams = @{ Id = $Id }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    if ($PSCmdlet.ShouldProcess("Issue #$Id", 'Close')) {
        & $Target.Command @Params
    }
}

function New-ChangeRequest {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string]
        $Title,

        [Parameter(Mandatory)]
        [Alias('Branch', 'Head')]
        [string]
        $SourceBranch,

        [Parameter()]
        [Alias('Base')]
        [string]
        $TargetBranch,

        [Parameter()]
        [string]
        $Description,

        [Parameter()]
        [switch]
        $Draft,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'New-ChangeRequest' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.Title        = $Title
            $Params.SourceBranch = $SourceBranch
            if ($TargetBranch) { $Params.TargetBranch = $TargetBranch }
            if ($Description)  { $Params.Description  = $Description }
            if ($Draft)        { $Params.Draft        = $true }
        }
        'gitlab' {
            $Params.Title        = $Title
            $Params.SourceBranch = $SourceBranch
            if ($TargetBranch) { $Params.TargetBranch = $TargetBranch }
            if ($Description)  { $Params.Description  = $Description }
            if ($Draft)        { Write-Warning "New-ChangeRequest -Draft is not yet supported by the Gitlab provider" }
        }
        default {
            $AllParams = @{
                Title        = $Title
                SourceBranch = $SourceBranch
            }
            if ($TargetBranch) { $AllParams.TargetBranch = $TargetBranch }
            if ($Description)  { $AllParams.Description  = $Description }
            if ($Draft)        { $AllParams.Draft        = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    if ($PSCmdlet.ShouldProcess("$SourceBranch -> $TargetBranch", 'Create Change Request')) {
        & $Target.Command @Params
    }
}

function Merge-ChangeRequest {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, Position=0)]
        [string]
        $Id,

        [Parameter()]
        [switch]
        $Squash,

        [Parameter()]
        [switch]
        $DeleteSourceBranch,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Merge-ChangeRequest' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.PullRequestId = $Id
            if ($Squash)             { $Params.MergeMethod        = 'squash' }
            if ($DeleteSourceBranch) { $Params.DeleteSourceBranch = $true }
        }
        'gitlab' {
            $Params.MergeRequestId = $Id
            if ($Squash)             { $Params.Squash                   = $true }
            if ($DeleteSourceBranch) { $Params.ShouldRemoveSourceBranch = $true }
        }
        default {
            $AllParams = @{ Id = $Id }
            if ($Squash)             { $AllParams.Squash             = $true }
            if ($DeleteSourceBranch) { $AllParams.DeleteSourceBranch = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    if ($PSCmdlet.ShouldProcess("Change Request #$Id", 'Merge')) {
        & $Target.Command @Params
    }
}

function New-Repo {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, Position=0)]
        [string]
        $Name,

        [Parameter()]
        [string]
        $Description,

        [Parameter()]
        [ValidateSet('public', 'private')]
        [string]
        $Visibility,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'New-Repo' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.Name = $Name
            if ($Description) { $Params.Description = $Description }
            if ($Visibility)  { $Params.Visibility  = $Visibility }
        }
        'gitlab' {
            $Params.Name = $Name
            if ($Description) { $Params.Description = $Description }
            if ($Visibility)  { $Params.Visibility  = $Visibility }
        }
        default {
            $AllParams = @{ Name = $Name }
            if ($Description) { $AllParams.Description = $Description }
            if ($Visibility)  { $AllParams.Visibility  = $Visibility }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    if ($PSCmdlet.ShouldProcess($Name, 'Create Repository')) {
        & $Target.Command @Params
    }
}

function Get-Commit {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Ref,

        [Parameter()]
        [string]
        $Branch,

        [Parameter()]
        [string]
        $Author,

        [Parameter()]
        [string]
        $Since,

        [Parameter()]
        [string]
        $Until,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-Commit' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Ref)      { $Params.Sha      = $Ref }
            if ($Branch)   { $Params.Branch   = $Branch }
            if ($Author)   { $Params.Author   = $Author }
            if ($Since)    { $Params.Since    = $Since }
            if ($Until)    { $Params.Until    = $Until }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
        }
        'gitlab' {
            if ($Ref)      { $Params.Sha      = $Ref }
            if ($Branch)   { $Params.Ref      = $Branch }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
            if ($Author)   { Write-Warning "Get-Commit -Author is not yet supported by the Gitlab provider" }
            if ($Since)    { Write-Warning "Get-Commit -Since is not yet supported by the Gitlab provider" }
            if ($Until)    { Write-Warning "Get-Commit -Until is not yet supported by the Gitlab provider" }
        }
        default {
            $AllParams = @{}
            if ($Ref)      { $AllParams.Ref      = $Ref }
            if ($Branch)   { $AllParams.Branch   = $Branch }
            if ($Author)   { $AllParams.Author   = $Author }
            if ($Since)    { $AllParams.Since    = $Since }
            if ($Until)    { $AllParams.Until    = $Until }
            if ($MaxPages) { $AllParams.MaxPages = $MaxPages }
            if ($All)      { $AllParams.All      = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Search-Repo {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position=0)]
        [string]
        $Query,

        [Parameter()]
        [ValidateSet('code', 'commits', 'issues')]
        [string]
        $Scope,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Search-Repo' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            $Params.Query = $Query
            if ($Scope)    { $Params.Scope    = $Scope }
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
        }
        'gitlab' {
            $Params.Search = $Query
            if ($MaxPages) { $Params.MaxPages = $MaxPages }
            if ($All)      { $Params.All      = $true }
            if ($Scope)    { Write-Warning "Search-Repo -Scope is not yet supported by the Gitlab provider" }
        }
        default {
            $AllParams = @{ Query = $Query }
            if ($Scope)    { $AllParams.Scope    = $Scope }
            if ($MaxPages) { $AllParams.MaxPages = $MaxPages }
            if ($All)      { $AllParams.All      = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}

function Get-Repo {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [string]
        $Id,

        [Parameter()]
        [switch]
        $Mine,

        [Parameter()]
        [string]
        $Group,

        [Parameter()]
        [string]
        $Select,

        [Parameter()]
        [switch]
        $IncludeArchived,

        [Parameter()]
        [uint]
        $MaxPages,

        [switch]
        [Parameter()]
        $All,

        [Parameter()]
        [string]
        $Provider
    )

    $Target = Resolve-ForgeCommand -CommandName 'Get-Repo' -Provider $Provider
    $Params = @{}

    switch ($Target.ProviderName) {
        'github' {
            if ($Id)       { $Params.RepositoryId  = $Id }
            if ($Mine)     { $Params.Mine          = $true }
            if ($Group)    { $Params.Organization  = $Group }
            if ($Select)   { $Params.Select        = $Select }
            if ($MaxPages) { $Params.MaxPages      = $MaxPages }
            if ($All)      { $Params.All           = $true }
            if ($IncludeArchived) { Write-Warning "Get-Repo -IncludeArchived is not applicable to Github" }
        }
        'gitlab' {
            if ($Id)       { $Params.ProjectId      = $Id }
            if ($Mine)     { $Params.Mine            = $true }
            if ($Group)    { $Params.GroupId          = $Group }
            if ($Select)   { $Params.Select          = $Select }
            if ($MaxPages) { $Params.MaxPages        = $MaxPages }
            if ($All)      { $Params.All             = $true }
            if ($IncludeArchived) { $Params.IncludeArchived = $true }
        }
        default {
            $AllParams = @{}
            if ($Id)              { $AllParams.Id              = $Id }
            if ($Mine)            { $AllParams.Mine            = $true }
            if ($Group)           { $AllParams.Group           = $Group }
            if ($Select)          { $AllParams.Select          = $Select }
            if ($IncludeArchived) { $AllParams.IncludeArchived = $true }
            if ($MaxPages)        { $AllParams.MaxPages        = $MaxPages }
            if ($All)             { $AllParams.All             = $true }
            Invoke-ForgeCommand -TargetCommand $Target.Command -Parameters $AllParams
            return
        }
    }

    & $Target.Command @Params
}