g-release.ps1

param(
    [Parameter(ValueFromPipeline)]
    [string]$Version,
    [switch]$View
)

. (Join-Path $PSScriptRoot 'g-registry.ps1')

$repo   = Get-Location
$branch = git -C $repo branch --show-current 2>$null
if (-not $branch) { Write-Host "not a git repo"; exit 1 }

if ($View) {
    $tags = @(git -C $repo tag --sort=-version:refname 2>$null | Select-Object -First 10)
    if ($tags.Count -eq 0) { Write-Host "no releases found"; exit 0 }
    $tags | ForEach-Object { Write-Host $_ }
    exit 0
}

$state = Get-GitRepoState
$cfg   = Get-GitboxConfig -RepoPath $repo

if (-not $state) { Write-Host "not a git repo"; exit 1 }
if (-not $state.RepoName) { Write-Host "could not resolve repo name"; exit 1 }

if ($state.Branch -ne $cfg.BaseBranch) {
    Write-Host "must be on base branch ($($cfg.BaseBranch)); currently on '$($state.Branch)'"
    exit 1
}
if ($state.DirtyFiles.Count -gt 0) {
    Write-Host "working tree has $($state.DirtyFiles.Count) uncommitted file(s); commit or stash before releasing"
    exit 1
}
function Resolve-NextVersion {
    param([string]$Arg)

    $latest = git tag --sort=-version:refname 2>$null | Select-Object -First 1

    $bumpType = if ($Arg -in @('', 'patch', 'minor', 'major')) { if ($Arg) { $Arg } else { 'patch' } } else { $null }

    if (-not $bumpType) { return $Arg }

    if (-not $latest) { return 'v0.1.0' }

    if ($latest -match '^(v?)(\d+)\.(\d+)\.(\d+)$') {
        $prefix = $Matches[1]
        [int]$maj = $Matches[2]; [int]$min = $Matches[3]; [int]$pat = $Matches[4]
        switch ($bumpType) {
            'major' { $maj++; $min = 0; $pat = 0 }
            'minor' { $min++; $pat = 0 }
            'patch' { $pat++ }
        }
        return "${prefix}${maj}.${min}.${pat}"
    }

    if ($latest -match '^\d{4}\.\d{2}\.\d{2}') {
        $candidate = Get-Date -Format 'yyyy.MM.dd'
        $n = 1
        while (git tag --list $candidate 2>$null) { $n++; $candidate = "$(Get-Date -Format 'yyyy.MM.dd').$n" }
        return $candidate
    }

    Write-Host "unrecognized tag scheme '$latest'; pass an explicit version string"
    exit 1
}

$resolved = Resolve-NextVersion -Arg $Version

if ($Version -and $Version -notin @('patch','minor','major') -and
    $resolved -notmatch '^v?\d+\.\d+\.\d+$' -and
    $resolved -notmatch '^\d{4}\.\d{2}\.\d{2}(\.\d+)?$') {
    Write-Host "invalid version '$Version' -- use vMAJOR.MINOR.PATCH or: patch | minor | major"
    exit 1
}

if (-not $Version) {
    $isInteractive = [Environment]::UserInteractive -and -not [Console]::IsInputRedirected
    if ($isInteractive) {
        try { $answer = Read-Host " release $resolved? [y/N]" } catch { $answer = 'n' }
        if ($answer -notmatch '^[yY]$') { Write-Host "release aborted"; exit 0 }
    }
}

if (git tag --list $resolved 2>$null) {
    Write-Host "tag '$resolved' already exists -- use a different version or: gitbox z -View to list existing tags"
    exit 1
}

Write-Host "releasing $resolved ..."

$prNumber = $null
if ($cfg.BaseBranch -ne $cfg.DefaultBranch) {
    $prOut = "release $resolved" | & (Join-Path $PSScriptRoot 'g-open-pr.ps1') -Base $cfg.DefaultBranch
    $prOut | ForEach-Object { Write-Host $_ }
    if ($LASTEXITCODE -ne 0) { exit 1 }

    foreach ($line in $prOut) {
        if ("$line" -match 'PR #(\d+)') { $prNumber = $Matches[1]; break }
    }

    & (Join-Path $PSScriptRoot 'g-pr-checks.ps1')
    if ($LASTEXITCODE -ne 0) { Write-Host "release blocked: checks failing on PR #$prNumber"; exit 1 }

    & (Join-Path $PSScriptRoot 'g-merge-rotate.ps1') -Steps 2
    if ($LASTEXITCODE -ne 0) { Write-Host "merge failed; branch '$($cfg.BaseBranch)' preserved"; exit 1 }

    $coOut = git -C $repo checkout $cfg.DefaultBranch 2>&1
    if ($LASTEXITCODE -ne 0) { Write-Host "checkout $($cfg.DefaultBranch) failed"; $coOut | ForEach-Object { Write-Host " $_" }; exit 1 }
    $pullOut = git -C $repo pull origin $cfg.DefaultBranch 2>&1
    if ($LASTEXITCODE -ne 0) { Write-Host "pull $($cfg.DefaultBranch) failed"; $pullOut | ForEach-Object { Write-Host " $_" }; exit 1 }
}

$tagOut = git -C $repo tag $resolved 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host "tag failed: $($tagOut -join ' ')"; exit 1 }

$pushOut = git -C $repo push origin $resolved 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host "push tag failed"; $pushOut | ForEach-Object { Write-Host " $_" }; exit 1 }

if ($cfg.BaseBranch -ne $cfg.DefaultBranch) {
    $coBackOut = git -C $repo checkout $cfg.BaseBranch 2>&1
    if ($LASTEXITCODE -ne 0) { Write-Host "checkout $($cfg.BaseBranch) failed"; $coBackOut | ForEach-Object { Write-Host " $_" }; exit 1 }
    $pullBackOut = git -C $repo pull origin $cfg.BaseBranch 2>&1
    if ($LASTEXITCODE -ne 0) { Write-Host "pull $($cfg.BaseBranch) failed"; $pullBackOut | ForEach-Object { Write-Host " $_" }; exit 1 }
    Write-Host "released $resolved |PR #$prNumber merged |tagged |back on $($cfg.BaseBranch)"
} else {
    Write-Host "released $resolved |tagged |on $($cfg.BaseBranch)"
}
exit 0