#!/usr/bin/env bash
set -uo pipefail

DEFAULT_SERVER="https://gitea.oreillyit.nz"
DEFAULT_TIMEOUT=300
DEFAULT_INTERVAL=15
DEFAULT_WORKFLOW="version"

usage() {
    cat <<'EOF'
Usage: wait-for-version [OPTIONS] REPO

Poll a Gitea repo's Actions runs until the CI workflow for HEAD of main
completes successfully, then print the latest release tag.

Arguments:
  REPO      owner/name (e.g. skynet/hugo-accelerator)

Options:
  -t, --token TOKEN       Gitea access token (else $GITEA_TOKEN / $GITHUB_TOKEN)
      --workflow NAME     Workflow name to wait for (default: version)
      --timeout SECONDS   Max total wait time (default: 300; 0 = poll once)
      --interval SECONDS  Polling interval (default: 15; min: 1)
      --server URL        Gitea base URL (else $GITHUB_SERVER_URL, else
                          https://gitea.oreillyit.nz)
  -v, --verbose           Print polling progress to stderr
  -n, --dryrun            Show resolved configuration without making API calls
  -h, --help              Show this help

Exit codes:
  0  CI succeeded; latest release tag printed to stdout
  1  timeout expired, CI failed, or no release tag found
  2  usage error
  3  terminal API error (401, 403, 404)
EOF
}

timeout=$DEFAULT_TIMEOUT
interval=$DEFAULT_INTERVAL
server="${GITHUB_SERVER_URL:-$DEFAULT_SERVER}"
token="${GITEA_TOKEN:-${GITHUB_TOKEN:-}}"
workflow="$DEFAULT_WORKFLOW"
verbose=false
dryrun=false
repo=""

while [[ $# -gt 0 ]]; do
    case "$1" in
        -h|--help) usage; exit 0 ;;
        -n|--dryrun) dryrun=true; shift ;;
        -v|--verbose) verbose=true; shift ;;
        -t|--token)
            [[ $# -lt 2 ]] && { echo "Error: --token requires a value" >&2; exit 2; }
            token="$2"; shift 2 ;;
        --workflow)
            [[ $# -lt 2 ]] && { echo "Error: --workflow requires a value" >&2; exit 2; }
            workflow="$2"; shift 2 ;;
        --timeout)
            [[ $# -lt 2 ]] && { echo "Error: --timeout requires a value" >&2; exit 2; }
            timeout="$2"; shift 2 ;;
        --interval)
            [[ $# -lt 2 ]] && { echo "Error: --interval requires a value" >&2; exit 2; }
            interval="$2"; shift 2 ;;
        --server)
            [[ $# -lt 2 ]] && { echo "Error: --server requires a value" >&2; exit 2; }
            server="$2"; shift 2 ;;
        --) shift; break ;;
        -*) echo "Error: Unknown option: $1" >&2; exit 2 ;;
        *)
            if [[ -z "$repo" ]]; then repo="$1"
            else echo "Error: Unexpected argument: $1" >&2; exit 2
            fi
            shift ;;
    esac
done

[[ -z "$repo" ]]     && { echo "Error: REPO is required" >&2; echo >&2; usage >&2; exit 2; }
[[ "$repo" != */* ]] && { echo "Error: REPO must be in 'owner/name' form (got: $repo)" >&2; exit 2; }
[[ ! "$timeout"  =~ ^[0-9]+$ ]] && { echo "Error: --timeout must be a non-negative integer (got: $timeout)" >&2; exit 2; }
[[ ! "$interval" =~ ^[0-9]+$ ]] && { echo "Error: --interval must be a positive integer (got: $interval)" >&2; exit 2; }
(( interval < 1 )) && { echo "Error: --interval must be >= 1" >&2; exit 2; }

api_base="${server%/}/api/v1/repos/${repo}"

if $dryrun; then
    echo "[dryrun] Server: ${server}"
    echo "[dryrun] Repo: ${repo}"
    echo "[dryrun] Workflow: ${workflow}"
    echo "[dryrun] Timeout: ${timeout}s"
    echo "[dryrun] Interval: ${interval}s"
    if [[ -n "$token" ]]; then
        echo "[dryrun] Auth: yes"
    else
        echo "[dryrun] Auth: no"
    fi
    echo "[dryrun] Actions API: ${api_base}/actions/runs"
    exit 0
fi

curl_args=(-sS -m 30 -w $'\n%{http_code}')
[[ -n "$token" ]] && curl_args+=(-H "Authorization: token ${token}")

sha_response="$(curl "${curl_args[@]}" "${api_base}/branches/main" 2>/dev/null)"
sha_rc=$?
sha_http="${sha_response##*$'\n'}"
sha_body="${sha_response%$'\n'*}"

if (( sha_rc != 0 )); then
    echo "Error: curl failed resolving HEAD SHA for ${repo} (rc=${sha_rc})" >&2
    exit 3
fi

case "$sha_http" in
    200)
        sha="$(printf '%s' "$sha_body" | jq -r '.commit.id // empty' 2>/dev/null)"
        [[ -z "$sha" ]] && { echo "Error: could not extract HEAD SHA from branch response" >&2; exit 3; }
        ;;
    401|403) echo "Error: Gitea API authentication failed (HTTP ${sha_http}) for ${repo}" >&2; exit 3 ;;
    404)     echo "Error: repo not found: ${repo} (HTTP 404)" >&2; exit 3 ;;
    *)       echo "Error: unexpected HTTP ${sha_http} resolving HEAD SHA for ${repo}" >&2; exit 3 ;;
esac

echo "Waiting for '${workflow}' CI on ${repo} @ ${sha:0:8}..." >&2

start=$(date +%s)
deadline=$(( start + timeout ))
poll=0

while true; do
    poll=$(( poll + 1 ))

    runs_response="$(curl "${curl_args[@]}" \
        "${api_base}/actions/runs?head_sha=${sha}&limit=50" 2>/dev/null)"
    runs_rc=$?

    if (( runs_rc != 0 )); then
        $verbose && echo "wait-for-version: poll ${poll}: curl error (rc=${runs_rc})" >&2
    else
        http_code="${runs_response##*$'\n'}"
        body="${runs_response%$'\n'*}"

        case "$http_code" in
            200)
                status="$(printf '%s' "$body" | jq -r \
                    --arg w "$workflow" \
                    '[.workflow_runs[] | select(.path | test($w + "\\.ya?ml"))] | first | .status // "pending"' \
                    2>/dev/null)"
                conclusion="$(printf '%s' "$body" | jq -r \
                    --arg w "$workflow" \
                    '[.workflow_runs[] | select(.path | test($w + "\\.ya?ml"))] | first | .conclusion // ""' \
                    2>/dev/null)"

                if [[ "$status" == "completed" ]]; then
                    if [[ "$conclusion" == "success" ]]; then
                        # Resolve tag by SHA match — avoids races with release creation
                        # and works regardless of whether the repo uses Gitea releases.
                        # When multiple tags share the SHA (e.g. v0.0.8 and v0.0.8.14),
                        # prefer the 3-component semver root (fewest dot-components).
                        tag_response="$(curl "${curl_args[@]}" "${api_base}/tags?limit=50" 2>/dev/null)"
                        tag_http="${tag_response##*$'\n'}"
                        tag_body="${tag_response%$'\n'*}"
                        if [[ "$tag_http" != "200" ]]; then
                            echo "Error: could not fetch tags for ${repo} (HTTP ${tag_http})" >&2
                            exit 1
                        fi
                        tag="$(printf '%s' "$tag_body" | jq -r --arg sha "$sha" \
                            '[.[] | select(.commit.sha == $sha)] |
                             sort_by(.name | ltrimstr("v") | split(".") | length) |
                             first | .name // empty' 2>/dev/null)"
                        if [[ -n "$tag" ]]; then
                            $verbose && echo "wait-for-version: poll ${poll}: success, tag=${tag}" >&2
                            echo "$tag"
                            exit 0
                        else
                            echo "Error: no tag found for ${repo} @ ${sha:0:8}" >&2
                            exit 1
                        fi
                    else
                        echo "Error: CI failed for ${repo} @ ${sha:0:8} (conclusion: ${conclusion})" >&2
                        exit 1
                    fi
                fi
                $verbose && echo "wait-for-version: poll ${poll}: status=${status:-pending}, retrying" >&2
                ;;
            401|403)
                echo "Error: Gitea API authentication failed (HTTP ${http_code}) for ${repo}" >&2
                exit 3 ;;
            404)
                echo "Error: repo not found: ${repo} (HTTP 404)" >&2
                exit 3 ;;
            *)
                $verbose && echo "wait-for-version: poll ${poll}: HTTP ${http_code}, retrying" >&2
                ;;
        esac
    fi

    now=$(date +%s)
    if (( now >= deadline )); then
        break
    fi
    remaining=$(( deadline - now ))
    sleep_time=$interval
    (( sleep_time > remaining )) && sleep_time=$remaining
    $verbose && echo "wait-for-version: poll ${poll}: sleeping ${sleep_time}s (${remaining}s remaining)" >&2
    sleep "$sleep_time"
done

echo "Error: timeout after ${timeout}s waiting for '${workflow}' CI on ${repo} @ ${sha:0:8}" >&2
exit 1
