#!/bin/bash
set -euo pipefail

readonly -a PACKAGES=(
    adwyra
    anidesk
    balena-etcher
    chatbox
    clash-verge-rev
    claude-alt
    claude
    codex
    fisher
    github-desktop
    happ
    netbird
    nivora-cli
    opencode
    parsec
    pineconemc
    tailscale
    telegram-desktop
    ventoy
    vintner
    vual
    yandex-browser-stable
)

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "${script_dir}/.." && pwd)"

die() {
    echo "package_updates: $*" >&2
    exit 1
}

require_package() {
    local requested="$1"
    local package
    for package in "${PACKAGES[@]}"; do
        [[ "$package" == "$requested" ]] && return 0
    done
    die "unknown package: ${requested}"
}

current_version() {
    stplr-spec get-field --path "${repo_root}/$1/Staplerfile" version
}

github_json() {
    local url="$1"
    local -a headers=(
        -H 'Accept: application/vnd.github+json'
        -H 'X-GitHub-Api-Version: 2022-11-28'
    )
    if [[ -n "${GITHUB_TOKEN:-}" ]]; then
        headers+=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
    fi
    curl --retry 3 --retry-delay 2 --retry-all-errors \
        --connect-timeout 30 --max-time 120 -fsSL "${headers[@]}" "$url"
}

git_latest_stable_tag() {
    local repository="$1"
    local attempt tag=''

    for attempt in 1 2 3; do
        tag="$(
            GIT_TERMINAL_PROMPT=0 timeout 60s \
                git ls-remote --tags --refs "https://github.com/${repository}.git" 'refs/tags/v*' |
                awk '{sub("refs/tags/", "", $2); print $2}' |
                grep -Eiv '(^|[-_.])(alpha|beta|rc|pre|preview)([-_.0-9]|$)' |
                sort -V |
                tail -1
        )" || tag=''
        if [[ -n "$tag" ]]; then
            printf '%s\n' "$tag"
            return 0
        fi
        sleep "$((attempt * 2))"
    done

    return 1
}

github_latest_release() {
    local repository="$1"
    local tag=''
    tag="$(
        github_json "https://api.github.com/repos/${repository}/releases?per_page=30" |
            jq -r '
                [
                    .[]
                    | select(.draft == false and .prerelease == false)
                    | .tag_name
                    | select(test("(?:^|[-_.])(alpha|beta|rc|pre|preview)(?:[-_.0-9]|$)"; "i") | not)
                ][0] // empty
            ' 2>/dev/null
    )" || tag=''

    if [[ -z "$tag" ]]; then
        tag="$(git_latest_stable_tag "$repository")" || tag=''
    fi

    [[ -n "$tag" ]] || {
        echo "package_updates: cannot determine latest release for ${repository}" >&2
        return 1
    }
    printf '%s\n' "${tag#v}"
}

latest_anidesk() {
    local version
    version="$(
        GIT_TERMINAL_PROMPT=0 timeout 60s \
            git ls-remote --tags --refs https://github.com/theDesConnet/AniDesk.git 'refs/tags/v*' |
            awk '{sub("refs/tags/v", "", $2); print $2}' |
            sort -V |
            tail -1
    )"
    [[ -n "$version" ]] || die 'cannot determine latest AniDesk version'
    printf '%s\n' "$version"
}

latest_chatbox() {
    local version
    version="$(
        github_json 'https://api.github.com/repos/chatboxai/chatbox/releases?per_page=30' |
            jq -er '
                [
                    .[]
                    | select(.draft == false and .prerelease == false)
                    | . as $release
                    | $release.assets[]?
                    | select(.name | test("^Chatbox-[0-9]+(?:\\.[0-9]+)+-amd64\\.deb$"))
                    | $release.tag_name
                ][0]
            ' |
            sed 's/^v//'
    )" || die 'cannot determine latest Chatbox Linux release'
    printf '%s\n' "$version"
}

latest_claude_desktop() {
    local version
    version="$(
        curl --retry 3 --retry-delay 2 --retry-all-errors \
            --connect-timeout 30 --max-time 120 -fsSL \
            'https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages' |
            awk '
                /^Package: claude-desktop$/ { selected = 1; next }
                /^Package: / { selected = 0 }
                selected && /^Version: / { print $2 }
            ' |
            sort -V |
            tail -1
    )"
    [[ -n "$version" ]] || die 'cannot determine latest Claude Desktop version'
    printf '%s\n' "$version"
}

latest_parsec() {
    local temp_dir version
    temp_dir="$(mktemp -d)"
    curl --retry 3 --retry-delay 2 --retry-all-errors \
        --connect-timeout 30 --max-time 300 -fsSL \
        -o "${temp_dir}/parsec.deb" \
        'https://builds.parsec.app/package/parsec-linux.deb'
    (
        cd "$temp_dir"
        ar x parsec.deb
        tar -xOf control.tar.* ./control
    ) >"${temp_dir}/control"
    version="$(awk '$1 == "Version:" {print $2; exit}' "${temp_dir}/control")"
    [[ -n "$version" ]] || die 'cannot determine latest Parsec version'
    printf '%s\n' "$version"
    find "$temp_dir" -mindepth 1 -delete
    rmdir "$temp_dir"
}

latest_tailscale() {
    local effective version
    effective="$(
        curl --retry 3 --retry-delay 2 --retry-all-errors \
            --connect-timeout 30 --max-time 120 -fsSLI -o /dev/null \
            -w '%{url_effective}' \
            'https://pkgs.tailscale.com/stable/tailscale_latest_amd64.tgz'
    )"
    version="$(sed -n 's/.*tailscale_\([0-9][0-9.]*\)_amd64\.tgz.*/\1/p' <<<"$effective")"
    [[ -n "$version" ]] || die 'cannot determine latest Tailscale version'
    printf '%s\n' "$version"
}

latest_yandex_browser() {
    local version
    version="$(
        curl --retry 3 --retry-delay 2 --retry-all-errors \
            --connect-timeout 30 --max-time 120 -fsSL \
            'https://repo.yandex.ru/yandex-browser/deb/dists/stable/main/binary-amd64/Packages.gz' |
            gzip -dc |
            awk '
                /^Package: yandex-browser-stable$/ { selected = 1; next }
                /^Package: / { selected = 0 }
                selected && /^Version: / {
                    version = $2
                    sub(/-[^-]+$/, "", version)
                    print version
                }
            ' |
            sort -V |
            tail -1
    )"
    [[ -n "$version" ]] || die 'cannot determine latest Yandex Browser version'
    printf '%s\n' "$version"
}

latest_version() {
    case "$1" in
    adwyra) github_latest_release Cheviiot/Adwyra ;;
    anidesk) latest_anidesk ;;
    balena-etcher) github_latest_release balena-io/etcher ;;
    chatbox) latest_chatbox ;;
    clash-verge-rev) github_latest_release clash-verge-rev/clash-verge-rev ;;
    claude | claude-alt) latest_claude_desktop ;;
    codex) github_latest_release Boria138/codex-app-linux ;;
    fisher) github_latest_release jorgebucaran/fisher ;;
    github-desktop)
        github_latest_release desktop/desktop | sed 's/^release-//'
        ;;
    happ) github_latest_release Happ-proxy/happ-desktop ;;
    netbird) github_latest_release netbirdio/netbird ;;
    nivora-cli) current_version nivora-cli ;;
    opencode) github_latest_release anomalyco/opencode ;;
    parsec) latest_parsec ;;
    pineconemc) github_latest_release ElyPrismLauncher/Launcher ;;
    tailscale) latest_tailscale ;;
    telegram-desktop) github_latest_release telegramdesktop/tdesktop ;;
    ventoy) github_latest_release ventoy/Ventoy ;;
    vintner) github_latest_release Cheviiot/vintner ;;
    vual) github_latest_release Cheviiot/Vual ;;
    yandex-browser-stable) latest_yandex_browser ;;
    *) die "unknown package: $1" ;;
    esac
}

check_package() {
    local package="$1"
    local current latest
    require_package "$package"
    current="$(current_version "$package")" || return
    latest="$(latest_version "$package")" || return
    printf '%s %s\n' "$current" "$latest"
}

check_all() {
    local package current latest status versions
    local updates=0
    printf '%-24s %-24s %-24s %s\n' PACKAGE CURRENT LATEST STATUS
    for package in "${PACKAGES[@]}"; do
        versions="$(check_package "$package")" || return
        read -r current latest <<<"$versions"
        status=current
        if [[ "$current" != "$latest" ]]; then
            status=update
            updates=1
        fi
        printf '%-24s %-24s %-24s %s\n' "$package" "$current" "$latest" "$status"
    done
    [[ "$updates" -eq 0 ]] || return 10
}

outdated_packages() {
    local package current latest versions
    for package in "${PACKAGES[@]}"; do
        versions="$(check_package "$package")" || return
        read -r current latest <<<"$versions"
        if [[ "$current" != "$latest" ]]; then
            printf '%s\n' "$package"
        fi
    done
}

case "${1:-}" in
check)
    [[ "$#" -eq 2 ]] || die 'usage: package_updates.sh check <package>'
    check_package "$2"
    ;;
check-all)
    [[ "$#" -eq 1 ]] || die 'usage: package_updates.sh check-all'
    check_all
    ;;
outdated)
    [[ "$#" -eq 1 ]] || die 'usage: package_updates.sh outdated'
    outdated_packages
    ;;
*)
    die 'usage: package_updates.sh {check <package>|check-all|outdated}'
    ;;
esac
