# ci-queue-wait.ps1 - Wait until project CI queue is clear (no running/queued pipeline on branch head)
# Usage: .\ci-queue-wait.ps1 [-Branch main] [-TimeoutSeconds 900] [-IntervalSeconds 15] [-Purpose merge] [-RequireStatus]

[CmdletBinding()]
param(
    [Alias("B")]
    [string]$Branch = "main",

    [Alias("t")]
    [int]$TimeoutSeconds = 900,

    [Alias("i")]
    [int]$IntervalSeconds = 15,

    [ValidateSet("push", "merge")]
    [string]$Purpose = "merge",

    [switch]$RequireStatus,

    [Alias("h")]
    [switch]$Help
)

$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
. "$ScriptDir\detect-platform.ps1"

function Show-Usage {
    @"
Usage: ci-queue-wait.ps1 [-Branch main] [-TimeoutSeconds 900] [-IntervalSeconds 15] [-Purpose push|merge] [-RequireStatus]

Options:
    -Branch, -B BRANCH            Branch head to inspect (default: main)
    -TimeoutSeconds, -t SECONDS   Max wait time (default: 900)
    -IntervalSeconds, -i SECONDS  Poll interval (default: 15)
    -Purpose VALUE                push or merge (default: merge)
    -RequireStatus                Fail if no CI status contexts are present
    -Help, -h                     Show help
"@
}

if ($Help) {
    Show-Usage
    exit 0
}

if ($TimeoutSeconds -lt 1 -or $IntervalSeconds -lt 1) {
    Write-Error "TimeoutSeconds and IntervalSeconds must be positive integers."
    exit 1
}

function Get-RemoteHost {
    $remoteUrl = git remote get-url origin 2>$null
    if ([string]::IsNullOrEmpty($remoteUrl)) { return $null }
    if ($remoteUrl -match "^https?://([^/]+)/") { return $Matches[1] }
    if ($remoteUrl -match "^git@([^:]+):") { return $Matches[1] }
    return $null
}

function Get-GiteaToken {
    param([string]$Host)

    if ($env:GITEA_TOKEN) { return $env:GITEA_TOKEN }

    $credPath = Join-Path $HOME ".git-credentials"
    if (-not (Test-Path $credPath)) { return $null }

    $line = Get-Content $credPath | Where-Object { $_ -like "*$Host*" } | Select-Object -First 1
    if (-not $line) { return $null }

    if ($line -match 'https?://[^@]*:([^@/]+)@') {
        return $Matches[1]
    }
    return $null
}

function Get-QueueState {
    param([object]$Payload)

    $pending = @("pending", "queued", "running", "waiting")
    $failure = @("failure", "error", "failed")
    $success = @("success")

    $state = ""
    if ($null -ne $Payload.state) {
        $state = "$($Payload.state)".ToLowerInvariant()
    }

    if ($pending -contains $state) { return "pending" }
    if ($failure -contains $state) { return "terminal-failure" }
    if ($success -contains $state) { return "terminal-success" }

    $values = @()
    $statuses = @()
    if ($null -ne $Payload.statuses) { $statuses = @($Payload.statuses) }

    foreach ($s in $statuses) {
        if ($null -eq $s) { continue }
        $v = ""
        if ($null -ne $s.status) { $v = "$($s.status)".ToLowerInvariant() }
        elseif ($null -ne $s.state) { $v = "$($s.state)".ToLowerInvariant() }
        if (-not [string]::IsNullOrEmpty($v)) { $values += $v }
    }

    if ($values.Count -eq 0 -and [string]::IsNullOrEmpty($state)) { return "no-status" }
    if (($values | Where-Object { $pending -contains $_ }).Count -gt 0) { return "pending" }
    if (($values | Where-Object { $failure -contains $_ }).Count -gt 0) { return "terminal-failure" }
    if ($values.Count -gt 0 -and ($values | Where-Object { -not ($success -contains $_) }).Count -eq 0) { return "terminal-success" }
    return "unknown"
}

function Print-PendingContexts {
    param([object]$Payload)

    $pending = @("pending", "queued", "running", "waiting")
    $statuses = @()
    if ($null -ne $Payload.statuses) { $statuses = @($Payload.statuses) }

    if ($statuses.Count -eq 0) {
        Write-Host "[ci-queue-wait] no status contexts reported"
        return
    }

    $found = $false
    foreach ($s in $statuses) {
        if ($null -eq $s) { continue }
        $name = if ($s.context) { $s.context } elseif ($s.name) { $s.name } else { "unknown-context" }
        $value = if ($s.status) { "$($s.status)".ToLowerInvariant() } elseif ($s.state) { "$($s.state)".ToLowerInvariant() } else { "unknown" }
        $target = if ($s.target_url) { $s.target_url } elseif ($s.url) { $s.url } else { "" }
        if ($pending -contains $value) {
            $found = $true
            if ($target) {
                Write-Host "[ci-queue-wait] pending: $name=$value ($target)"
            }
            else {
                Write-Host "[ci-queue-wait] pending: $name=$value"
            }
        }
    }

    if (-not $found) {
        Write-Host "[ci-queue-wait] no pending contexts"
    }
}

$platform = Get-GitPlatform
$owner = Get-GitRepoOwner
$repo = Get-GitRepoName

if ([string]::IsNullOrEmpty($owner) -or [string]::IsNullOrEmpty($repo)) {
    Write-Error "Could not determine repository owner/name from git remote."
    exit 1
}

$headSha = $null
$host = $null
$giteaToken = $null

switch ($platform) {
    "github" {
        if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
            Write-Error "gh CLI is required for GitHub CI queue guard."
            exit 1
        }
        $headSha = (& gh api "repos/$owner/$repo/branches/$Branch" --jq ".commit.sha").Trim()
        if ([string]::IsNullOrEmpty($headSha)) {
            Write-Error "Could not resolve $Branch head SHA."
            exit 1
        }
        Write-Host "[ci-queue-wait] platform=github purpose=$Purpose branch=$Branch sha=$headSha"
    }
    "gitea" {
        $host = Get-RemoteHost
        if ([string]::IsNullOrEmpty($host)) {
            Write-Error "Could not determine remote host."
            exit 1
        }
        $giteaToken = Get-GiteaToken -Host $host
        if ([string]::IsNullOrEmpty($giteaToken)) {
            Write-Error "Gitea token not found. Set GITEA_TOKEN or configure ~/.git-credentials."
            exit 1
        }
        try {
            $branchUrl = "https://$host/api/v1/repos/$owner/$repo/branches/$Branch"
            $branchPayload = Invoke-RestMethod -Method Get -Uri $branchUrl -Headers @{ Authorization = "token $giteaToken" }
            $headSha = ($branchPayload.commit.id | Out-String).Trim()
        }
        catch {
            Write-Error "Could not resolve $Branch head SHA from Gitea API."
            exit 1
        }
        if ([string]::IsNullOrEmpty($headSha)) {
            Write-Error "Could not resolve $Branch head SHA."
            exit 1
        }
        Write-Host "[ci-queue-wait] platform=gitea purpose=$Purpose branch=$Branch sha=$headSha"
    }
    default {
        Write-Error "Unsupported platform '$platform'."
        exit 1
    }
}

$deadline = (Get-Date).AddSeconds($TimeoutSeconds)

while ($true) {
    if ((Get-Date) -gt $deadline) {
        Write-Error "Timed out waiting for CI queue to clear on $Branch after ${TimeoutSeconds}s."
        exit 124
    }

    try {
        if ($platform -eq "github") {
            $statusJson = & gh api "repos/$owner/$repo/commits/$headSha/status"
            $payload = $statusJson | ConvertFrom-Json
        }
        else {
            $statusUrl = "https://$host/api/v1/repos/$owner/$repo/commits/$headSha/status"
            $payload = Invoke-RestMethod -Method Get -Uri $statusUrl -Headers @{ Authorization = "token $giteaToken" }
        }
    }
    catch {
        Write-Error "Failed to query commit status for queue guard."
        exit 1
    }

    $state = Get-QueueState -Payload $payload
    Write-Host "[ci-queue-wait] state=$state purpose=$Purpose branch=$Branch"

    switch ($state) {
        "pending" {
            Print-PendingContexts -Payload $payload
            Start-Sleep -Seconds $IntervalSeconds
        }
        "no-status" {
            if ($RequireStatus) {
                Write-Error "No CI status contexts found while -RequireStatus is set."
                exit 1
            }
            Write-Host "[ci-queue-wait] no status contexts present; proceeding."
            exit 0
        }
        "terminal-success" { exit 0 }
        "terminal-failure" { exit 0 }
        "unknown" { exit 0 }
        default { exit 0 }
    }
}
