module/command-file.psm1

#Requires -PSEdition Core -Version 7.2
Import-Module -Name @(
    (Join-Path -Path $PSScriptRoot -ChildPath 'internal\token.psm1')
) -Prefix 'GitHubActions' -Scope 'Local'
<#
.SYNOPSIS
GitHub Actions - Internal - Add File Command
.DESCRIPTION
Add file command for the current step.
.PARAMETER FileCommandPath
File command path.
.PARAMETER Name
Name.
.PARAMETER Value
Value.
.OUTPUTS
[Void]
#>

Function Add-FileCommand {
    [OutputType([Void])]
    Param (
        [Parameter(Mandatory = $True, Position = 0)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][Alias('CommandPath')][String]$FileCommandPath,
        [Parameter(Mandatory = $True, Position = 1)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][String]$Name,
        [Parameter(Mandatory = $True, Position = 2)][AllowEmptyString()][AllowNull()][String]$Value
    )
    If ($Value -imatch '^.*$') {
        [String]$Content = "$Name=$Value"
    }
    Else {
        Do {
            [String]$Token = New-GitHubActionsRandomToken
            [String]$TokenRegExEscape = [RegEx]::Escape($Token)
        }
        While ($Name -imatch $TokenRegExEscape -or $Value -imatch $TokenRegExEscape)
        [String]$Content = "$Name<<$Token`n$($Value -ireplace '\r?\n', "`n")`n$Token"
    }
    Add-Content -LiteralPath $FileCommandPath -Value $Content -Confirm:$False -Encoding 'UTF8NoBOM'
}
<#
.SYNOPSIS
GitHub Actions - Clear File Command
.DESCRIPTION
Clear the file command that set in the current step.
.PARAMETER FileCommand
File command.
.OUTPUTS
[Void]
#>

Function Clear-FileCommand {
    [CmdletBinding(HelpUri = 'https://github.com/hugoalh-studio/ghactions-toolkit-powershell/wiki/api_function_cleargithubactionsfilecommand')]
    [OutputType([Void])]
    Param (
        [Parameter(Mandatory = $True, Position = 0)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][Alias('Command', 'Commands', 'FileCommands')][String[]]$FileCommand
    )
    ForEach ($FC In $FileCommand) {
        Try {
            [String]$FileCommandPath = Resolve-FileCommandPath -FileCommand $FC
            Set-Content -LiteralPath $FileCommandPath -Value '' -Confirm:$False -Encoding 'UTF8NoBOM'
        }
        Catch {
            Write-Error -Message "Unable to clear the GitHub Actions file command: $_" -Category (($_)?.CategoryInfo.Category ?? 'OperationStopped')
        }
    }
}
Set-Alias -Name 'Remove-FileCommand' -Value 'Clear-FileCommand' -Option 'ReadOnly' -Scope 'Local'
<#
.SYNOPSIS
GitHub Actions - Internal - Get File Command
.DESCRIPTION
Get file command that set in the current step.
.PARAMETER FileCommandPath
File command path.
.OUTPUTS
[PSCustomObject[]]
#>

Function Get-FileCommand {
    [OutputType([PSCustomObject[]])]
    Param (
        [Parameter(Mandatory = $True, Position = 0)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][Alias('CommandPath')][String]$FileCommandPath
    )
    [String[]]$FileCommandRaw = Get-Content -LiteralPath $FileCommandPath -Encoding 'UTF8NoBOM'
    [PSCustomObject[]]$Result = @()
    For ([UInt64]$Index = 0; $Index -lt $FileCommandRaw.Count; $Index += 1) {
        [String]$CurrentLine = $FileCommandRaw[$Index]
        If ($CurrentLine.Length -eq 0) {
            Continue
        }
        If ($CurrentLine -imatch '^.+<<.+?$') {
            [String[]]$CurrentLineSplit = $CurrentLine -isplit '<<'
            [String]$Name = $CurrentLineSplit |
                Select-Object -SkipLast 1 |
                Join-String -Separator '<<'
            [String]$Delimiter = $CurrentLineSplit |
                Select-Object -Last 1
            [String[]]$Value = @()
            [UInt64]$IndexOffset = $Index
            While ($True) {
                $IndexOffset += 1
                If ($IndexOffset -ge $FileCommandRaw.Count) {
                    Throw "``$CurrentLine`` is missing pair delimiter in the file command content!"
                }
                [String]$CurrentLineOffset = $FileCommandRaw[$IndexOffset]
                If ($CurrentLineOffset -ceq $Delimiter) {
                    Break
                }
                $Value += $CurrentLineOffset
            }
            $Result += [PSCustomObject]@{
                Name = $Name
                Value = $Value
                Raw = @($CurrentLine) + $Value + @($Delimiter)
            }
            $Index = $IndexOffset
            Continue
        }
        If ($CurrentLine -imatch '^.+?=.+$') {
            [String[]]$CurrentLineSplit = $CurrentLine -isplit '='
            [String]$Name = $CurrentLineSplit |
                Select-Object -Index @(0)
            [String]$Value = $CurrentLineSplit |
                Select-Object -SkipIndex @(0) |
                Join-String -Separator '='
            $Result += [PSCustomObject]@{
                Name = $Name
                Value = $Value
                Raw = @($CurrentLine)
            }
            Continue
        }
        Throw "``$CurrentLine`` is not a valid file command content!"
    }
    $Result |
        Write-Output
}
<#
.SYNOPSIS
GitHub Actions - Internal - Remove File Command
.DESCRIPTION
Remove file command that set in the current step.
.PARAMETER FileCommandPath
File command path.
.PARAMETER Raw
Raw.
.OUTPUTS
[Void]
#>

Function Remove-FileCommand {
    [OutputType([Void])]
    Param (
        [Parameter(Mandatory = $True, Position = 0)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][Alias('CommandPath')][String]$FileCommandPath,
        [Parameter(Mandatory = $True, Position = 1)][String[]]$Raw
    )
    [String]$FileCommandRaw = Get-Content -LiteralPath $FileCommandPath -Raw -Encoding 'UTF8NoBOM'
    [String]$RawReplace = $Raw |
        ForEach-Object -Process { [RegEx]::Escape($_) } |
        Join-String -Separator '\r?\n' -OutputPrefix '(?:^|\r?\n)' -OutputSuffix '(?:$|\r?\n)'
    Set-Content -LiteralPath $FileCommandPath -Value ($FileCommandRaw -ireplace $RawReplace, "`n") -Confirm:$False -Encoding 'UTF8NoBOM'
}
<#
.SYNOPSIS
GitHub Actions - Internal - Resolve File Command Path
.DESCRIPTION
Resolve file command path.
.PARAMETER FileCommand
File command.
.OUTPUTS
[String] File command path.
#>

Function Resolve-FileCommandPath {
    [OutputType([String])]
    Param (
        [Parameter(Mandatory = $True, Position = 0)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][Alias('Command')][String]$FileCommand
    )
    [String]$FileCommandToUpper = $FileCommand.ToUpper()
    [AllowEmptyString()][AllowNull()][String]$FileCommandPath = [System.Environment]::GetEnvironmentVariable($FileCommandToUpper)
    If ([String]::IsNullOrEmpty($FileCommandPath)) {
        Throw "Environment path ``$FileCommandToUpper`` is not defined!"
    }
    If (![System.IO.Path]::IsPathFullyQualified($FileCommandPath)) {
        Throw "``$FileCommandPath`` (environment path ``$FileCommandToUpper``) is not a valid absolute path!"
    }
    If (!(Test-Path -LiteralPath $FileCommandPath -PathType 'Leaf')) {
        Throw "Environment path ``$FileCommandToUpper`` is not exist!"
    }
    Return $FileCommandPath
}
<#
.SYNOPSIS
GitHub Actions - Write File Command
.DESCRIPTION
Write file command for the current step.
.PARAMETER FileCommand
File command.
.PARAMETER Name
Name.
.PARAMETER Value
Value.
.PARAMETER Optimize
Whether to have an optimize operation by replace exist command instead of add command directly.
.OUTPUTS
[Void]
#>

Function Write-FileCommand {
    [CmdletBinding(HelpUri = 'https://github.com/hugoalh-studio/ghactions-toolkit-powershell/wiki/api_function_writegithubactionsfilecommand')]
    [OutputType([Void])]
    Param (
        [Parameter(Mandatory = $True, Position = 0)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][Alias('Command')][String]$FileCommand,
        [Parameter(Mandatory = $True, Position = 1, ValueFromPipelineByPropertyName = $True)][ValidatePattern('^.+$', ErrorMessage = 'Value is not a single line string!')][String]$Name,
        [Parameter(Mandatory = $True, Position = 2, ValueFromPipelineByPropertyName = $True)][AllowEmptyString()][AllowNull()][String]$Value,
        [Switch]$Optimize
    )
    Begin {
        [Boolean]$ShouldProceed = $True
        Try {
            [String]$FileCommandPath = Resolve-FileCommandPath -FileCommand $FileCommand
        }
        Catch {
            $ShouldProceed = $False
            Write-Error -Message "Unable to write the GitHub Actions file command: $_" -Category 'ResourceUnavailable'
        }
        If ($Optimize.IsPresent) {
            Try {
                [PSCustomObject[]]$FileCommandContent = Get-FileCommand -FileCommandPath $FileCommandPath
            }
            Catch {
                Write-Warning -Message "Unable to get the GitHub Actions file command: $_"
            }
        }
    }
    Process {
        If (!$ShouldProceed) {
            Return
        }
        Try {
            If ($Optimize.IsPresent -and $Null -ine $FileCommandContent -and $FileCommandContent.Name -icontains $Name) {
                Try {
                    Remove-FileCommand -FileCommandPath $FileCommandPath -Raw (
                        $FileCommandContent |
                            Where-Object -FilterScript { $_.Name -ieq $Name }
                    ).Raw
                    $FileCommandContent = $FileCommandContent |
                        Where-Object -FilterScript { $_.Name -ine $Name }
                }
                Catch {
                    Write-Warning -Message "Unable to remove the GitHub Actions file command: $_"
                }
            }
            Add-FileCommand -FileCommandPath $FileCommandPath -Name $Name -Value $Value
        }
        Catch {
            Write-Error -Message "Unable to write the GitHub Actions file command: $_" -Category (($_)?.CategoryInfo.Category ?? 'OperationStopped')
        }
    }
}
Export-ModuleMember -Function @(
    'Clear-FileCommand',
    'Write-FileCommand'
) -Alias @(
    'Remove-FileCommand'
)