GithubPRBuilder.psm1

$moduleName = 'GithubPRBuilder'

<#
.SYNOPSIS
    Create pull request
.DESCRIPTION
    Conditions:
        1) You have remote repository with 'origin' alias
        2) You use ssh protocol
.PARAMETER Target
    Branch with changes
.PARAMETER Base
    Base branch
#>

function New-GithubPullrequest {
    [CmdletBinding(SupportsShouldProcess = $True)]
    param(
        [Parameter(Mandatory = $false)]
        [string]$TargetRef = "HEAD",
        [Parameter(Mandatory = $false)]
        [string]$Base = "develop"
    )
    $settings = Import-GithubPRBuilderConfiguration

    $ErrorActionPreference = "Stop"
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls11

    $targetBranch = Get-GitBranchName -BranchRef $TargetRef
    $title = New-PRTitle -TargetBranch $targetBranch
    $description = New-PRDescription
    $repository = Get-GitRepositoryName

    $pull = Send-GitubPullrequest `
        -Owner $settings.GitHubOwner `
        -Repository $repository `
        -Title $title `
        -Body $description `
        -Base $Base `
        -Head $targetBranch

    If ($PSCmdlet.ShouldProcess("Open pull request github page")) {
        Start-Process $pull.html_url
    }
}

function Send-GitubPullrequest {
    [CmdletBinding(supportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string]$Owner,
        [Parameter(Mandatory)]
        [string]$Repository,
        [Parameter(Mandatory)]
        [string]$Title,
        [Parameter(Mandatory)]
        [string]$Head,
        [Parameter(Mandatory)]
        [string]$Base,
        [Parameter(Mandatory)]
        [AllowEmptyString()]
        [string]$Body
    )
    try {
        $settings = Import-GithubPRBuilderConfiguration
        $postData = @{
            head  = $Head;
            base  = $Base
            title = $Title;
            body  = $Body
        }

        Write-Verbose "PostData = $($postData | Out-String)"

        $params = @{
            Uri         = ("https://api.github.com/repos/$Owner/$Repository/pulls" +
                "?access_token=$($settings.GitHubToken)");
            Method      = 'POST';
            ContentType = 'application/json';
            Body        = [System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $postData -Compress));
        }

        If ($PSCmdlet.ShouldProcess("Invoke-RestMethod $($params | Out-String)")) {
            Invoke-RestMethod @params
        }
    }
    catch {
        Write-Error "An unexpected error occurred $_"
    }
}

function Get-GitRepositoryName {
    $originUrls = (git config --get remote.origin.url)
    $repository = $originUrls `
        | ? {$_ -match ':[^/]*?/(?<rep>.*?)\.git'} `
        | % {$Matches['rep']} `
        | select -First 1
    return $repository
}

function New-PRDescription {
    $gitRoot = (git rev-parse --show-toplevel)
    $template = "$gitRoot\PULL_REQUEST_TEMPLATE.md"
    if (Test-Path $template) {
        Get-Content $template -Encoding UTF8 -Raw
    }
    else {
        ""
    }
    
}

function New-PRTitle {
    param(
        [Parameter(Mandatory = $false)]
        [string]$TargetBranch = "HEAD",
        [Parameter(Mandatory = $false)]
        [string]$Base = "develop"
    )
    $title = $targetBranch
    if ($targetBranch -match 'CASEM-\d+') {
        $issueKey = $Matches[0]
        # Get jira issue name
        try {
            $branchIssue = Get-JiraIssue -IssueKey $issueKey
        }
        catch {
            Write-Warning "jira issue $_ not found"
        }
        if ($branchIssue) {
            $title = "$targetBranch $($branchIssue.fields.summary)"
        }
    }
    return $title
}

function Get-GitBranchName {
    param(
        [Parameter(Mandatory = $false)]
        [string]$BranchRef = "HEAD"
    )

    $branch = $BranchRef
    if ($BranchRef -eq "HEAD") {
        $branch = (git rev-parse --abbrev-ref HEAD)
    }
    $branch
}

function Get-JiraIssue {
    param(
        [Parameter(Mandatory)]
        [string]$IssueKey
    )
    $settings = Import-GithubPRBuilderConfiguration
    $authorizationHeader = New-JiraAuthHeader
    $fullUri = "$($settings.JiraHost)/rest/api/2/issue/$IssueKey"
    $response = Invoke-WebRequest -Uri $fullUri -Headers $authorizationHeader
    ConvertFrom-Json $response.Content
}

function New-JiraAuthHeader {
    $settings = Import-GithubPRBuilderConfiguration
    $bytes = [System.Text.Encoding]::ASCII.GetBytes("$($settings.JiraLogin):$($settings.JiraPassword)")
    $encodedCreds = 'Basic ' + [System.Convert]::ToBase64String($bytes)
    @{Authorization = $encodedCreds}
}

function Import-GithubPRBuilderConfiguration {
    $configuration = Import-Configuration
    $requiredKeys = @('JiraHost', 'JiraLogin', 'JiraPassword', 'GithubToken', 'GithubOwner')
    $notExistedConfigs = $requiredKeys `
        | ? {!$configuration.ContainsKey($_) -or [string]::IsNullOrEmpty($configuration[$_])}

    if ($notExistedConfigs) {
        $congigsStr = [string]::Join(', ', $notExistedConfigs)
        Write-Warning "Thanks for using $moduleName, configurations not found: $congigsStr, please run Update-GithubPRBuilderConfiguration to configure."
        throw "Module not configured. Run Update-GithubPRBuilderConfiguration"
    }
    $secureString = $configuration.JiraPassword | ConvertTo-SecureString
    $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList '_', $secureString
    $configuration.JiraPassword = $credential.GetNetworkCredential().Password
    $configuration
}

function Update-GithubPRBuilderConfiguration {
    [CmdletBinding(supportsShouldProcess)]
    param(
        # Jira
        [string]$JiraHost,
        [string]$JiraLogin,
        [switch]$JiraPassword,
        # Github
        [string]$GithubToken,
        [string]$GithubOwner
    )
    end {
        If ($PSCmdlet.ShouldProcess("Saving settings to $(Get-ConfigurationPath)")) {
            $config = Import-Configuration
            if (!$config) {
                $config = @{}
            }
            foreach ($parKey in $PSBoundParameters.Keys) {
                $config[$parKey] = $PSBoundParameters[$parKey]
            }
            if ($JiraPassword) {
                $securePassword = (Read-Host "Enter jira password" -AsSecureString | ConvertFrom-SecureString)
                $config.JiraPassword = $securePassword
            }
            $config | Export-Configuration
        }
    }
}

function Set-GithubPRBuilderConfiguration {
    [CmdletBinding(supportsShouldProcess)]
    param(
        # Jira
        [Parameter(Mandatory)]
        [string]$JiraHost,
        [Parameter(Mandatory)]
        [string]$JiraLogin,
        [Parameter(Mandatory)]
        [SecureString]$JiraPassword,
        # Github
        [Parameter(Mandatory)]
        [string]$GithubToken,
        [Parameter(Mandatory)]
        [string]$GithubOwner
    )
    end {
        If ($PSCmdlet.ShouldProcess("Saving settings to $(Get-ConfigurationPath)")) {
            $config = Import-Configuration
            if (!$config) {
                $config = @{}
            }
            foreach ($parKey in $PSBoundParameters.Keys | ? {$_ -ne 'JiraPassword'}) {
                $config[$parKey] = $PSBoundParameters[$parKey]
            }
            if ($PSBoundParameters.ContainsKey('JiraPassword')) {
                $securePassword = $JiraPassword | ConvertFrom-SecureString
                $config.JiraPassword = $securePassword
            }
            $config | Export-Configuration
        }
    }
}

# Test for it **during** module import:
try {
    $null = Import-GithubPRBuilderConfiguration
}
catch {
    # Hide the error on import, just warn them
    Write-Warning "You must configure module to avoid this warning on first run. Use Set-GithubPRBuilderConfiguration"
}