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

DEFAULT_SERVER="https://gitea.oreillyit.nz"
DEFAULT_TIMEOUT=600
DEFAULT_INTERVAL=5

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

Poll a Gitea repo's releases until one matches PATTERN (shell-style glob)
or the timeout expires.

Arguments:
  REPO      owner/name (e.g. skynet/myapp)
  PATTERN   shell-style glob matched against each release's tag_name
            (e.g. 'v1.2.3.*', 'v1.2.*', 'v1.*'). Quote it to prevent
            your shell from expanding it.

Options:
  -t, --token TOKEN       Gitea access token (else $GITEA_TOKEN / $GITHUB_TOKEN)
      --timeout SECONDS   Max total wait time (default: 600; 0 = poll once)
      --interval SECONDS  Polling interval (default: 5; 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  matching release found (tag_name printed to stdout)
  1  timeout expired with no match
  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:-}}"
verbose=false
dryrun=false
repo=""
pattern=""

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 ;;
        --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"
            elif [[ -z "$pattern" ]]; then pattern="$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; }
[[ -z "$pattern" ]] && { echo "Error: PATTERN 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_url="${server%/}/api/v1/repos/${repo}/releases?limit=50"

if $dryrun; then
    echo "[dryrun] Server: ${server}"
    echo "[dryrun] Repo: ${repo}"
    echo "[dryrun] Pattern: ${pattern}"
    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] API URL: ${api_url}"
    exit 0
fi

extract_tags() {
    if command -v jq >/dev/null 2>&1; then
        jq -r '.[]?.tag_name // empty' 2>/dev/null
    else
        grep -oE '"tag_name"[[:space:]]*:[[:space:]]*"[^"]+"' \
            | sed -E 's/.*"([^"]+)"$/\1/'
    fi
}

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

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

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

    response="$("${curl_cmd[@]}" "$api_url" 2>/dev/null)"
    curl_rc=$?

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

        case "$http_code" in
            200)
                match=""
                while IFS= read -r tag; do
                    [[ -z "$tag" ]] && continue
                    # shellcheck disable=SC2053   # intentional glob match
                    if [[ "$tag" == $pattern ]]; then
                        match="$tag"
                        break
                    fi
                done < <(printf '%s' "$body" | extract_tags)

                if [[ -n "$match" ]]; then
                    $verbose && echo "wait-for-release: poll ${poll}: matched '${match}'" >&2
                    echo "$match"
                    exit 0
                fi
                $verbose && echo "wait-for-release: poll ${poll}: HTTP 200, no match yet" >&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-release: 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-release: poll ${poll}: sleeping ${sleep_time}s (${remaining}s remaining)" >&2
    sleep "$sleep_time"
done

echo "Error: timeout after ${timeout}s waiting for '${pattern}' in ${repo}" >&2
exit 1
