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

ARCH_AMD64=amd64
ARCH_ARM64=arm64
ARCH=
VERSION=
GITHUB_TOKEN=${GITHUB_TOKEN:-}
GCB_URL=https://storage.googleapis.com/cri-o

usage() {
    printf "Usage: %s [-a ARCH] [ -t TAG|SHA ] [ -h ]\n\n" "$(basename "$0")"
    echo "Possible arguments:"
    printf "  -a\tArchitecture to retrieve (defaults to the local system)\n"
    printf "  -t\tVersion tag or full length SHA to be used (defaults to the latest available master)\n"
    printf "  -h\tShow this help message\n"
}

parse_args() {
    echo "Welcome to the CRI-O install script!"

    while getopts 'a:t:h' OPTION; do
        case "$OPTION" in
        a)
            ARCH="$OPTARG"
            echo "Using architecture: $ARCH"
            ;;
        t)
            VERSION="$OPTARG"
            echo "Using version: $VERSION"
            ;;
        h)
            usage
            exit 0
            ;;
        ?)
            usage
            exit 1
            ;;
        esac
    done

    if [[ $ARCH == "" ]]; then
        LOCAL_ARCH=$(uname -m)
        if [[ "$LOCAL_ARCH" == x86_64 ]]; then
            ARCH=$ARCH_AMD64
        elif [[ "$LOCAL_ARCH" == aarch64 ]]; then
            ARCH=$ARCH_ARM64
        else
            echo "Unsupported local architecture: $LOCAL_ARCH"
            exit 1
        fi
        echo "No architecture provided, using: $ARCH"
    fi
}

verify_requirements() {
    CMDS=(curl jq tar)
    echo "Checking if all commands are available: ${CMDS[*]}"
    for CMD in "${CMDS[@]}"; do
        if ! command -v "$CMD" >/dev/null; then
            echo "Command $CMD not available but required"
            exit 1
        fi
    done
}

latest_version() {
    GH_API_URL="https://api.github.com/repos/cri-o/cri-o/actions/runs?per_page=100"

    GH_HEADERS=(-H "Accept: application/vnd.github.v3+json")
    if [[ $GITHUB_TOKEN != "" ]]; then
        GH_HEADERS+=(-H "Authorization: token $GITHUB_TOKEN")
    fi

    if [[ $VERSION == "" ]]; then
        echo Searching for latest version via marker file
        COMMIT=$(curl -sSfL --retry 5 --retry-delay 3 "$GCB_URL/latest-master.txt")
        if [[ "$COMMIT" != "" ]]; then
            VERSION=$COMMIT
            echo "Found latest version $VERSION"
            return
        fi

        echo No version marker found, trying latest successful GitHub actions run
        echo Export a GITHUB_TOKEN environment variable to avoid GitHub API rate limits
        PAGE=0
        while true; do
            PAGE=$((PAGE + 1))
            echo Searching GitHub actions page $PAGE

            URL="${GH_API_URL}&page=$PAGE"
            JSON=$(curl -sSfL --retry 5 --retry-delay 3 "${GH_HEADERS[@]}" "$URL")

            if echo "$JSON" | jq -e '.workflow_runs | length == 0' >/dev/null; then
                echo No more GitHub action runs available to search
                exit 1
            fi

            FOUND=$(echo "$JSON" |
                jq -r '.workflow_runs | map(select(.name == "test" and .head_branch == "master" and .conclusion == "success")) | first | .head_sha')

            if [[ $FOUND == null ]]; then
                continue
            fi

            VERSION=$FOUND
            echo "Using latest successful GitHub action master: $VERSION"
            break
        done

        if [[ $VERSION == "" ]]; then
            echo "Unable to find successful GitHub action"
            exit 1
        fi
    fi
}

prepare() {
    parse_args "$@"
    verify_requirements
    latest_version
}

prepare "$@"

TARBALL_URL=$GCB_URL/artifacts/cri-o.$ARCH.$VERSION.tar.gz

TMPDIR="$(mktemp -d)"
trap 'rm -rf -- "$TMPDIR"' EXIT

echo "Downloading $TARBALL_URL to $TMPDIR"
curl -sSfL --retry 5 --retry-delay 3 "$TARBALL_URL" | tar xfz - --strip-components=1 -C "$TMPDIR"

echo Installing CRI-O
pushd "$TMPDIR"

# In this section we include the contents of
# contrib/bundle/install-paths and contrib/bundle/install

# INCLUDE
DESTDIR=${DESTDIR:-}
PREFIX=${PREFIX:-/usr/local}
ETCDIR=${ETCDIR:-/etc}
CONTAINERS_DIR=${CONTAINERS_DIR:-$ETCDIR/containers}
CNIDIR=${CNIDIR:-$ETCDIR/cni/net.d}
BINDIR=${BINDIR:-$PREFIX/bin}
MANDIR=${MANDIR:-$PREFIX/share/man}
OCIDIR=${OCIDIR:-$PREFIX/share/oci-umount/oci-umount.d}
BASHINSTALLDIR=${BASHINSTALLDIR:-$PREFIX/share/bash-completion/completions}
FISHINSTALLDIR=${FISHINSTALLDIR:-$PREFIX/share/fish/completions}
ZSHINSTALLDIR=${ZSHINSTALLDIR:-$PREFIX/share/zsh/site-functions}
SYSTEMDDIR=${SYSTEMDDIR:-$PREFIX/lib/systemd/system}
OPT_CNI_BIN_DIR=${OPT_CNI_BIN_DIR:-/opt/cni/bin}

SELINUX=
if selinuxenabled 2>/dev/null; then
    SELINUX=-Z
fi
ARCH=${ARCH:-amd64}
set -x
install $SELINUX -d -m 755 "$DESTDIR$CNIDIR"
install $SELINUX -D -m 755 -t "$DESTDIR$OPT_CNI_BIN_DIR" cni-plugins/*
install $SELINUX -D -m 644 -t "$DESTDIR$CNIDIR" contrib/10-crio-bridge.conf
install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/conmon
install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/crictl
install $SELINUX -d -m 755 "$DESTDIR$BASHINSTALLDIR"
install $SELINUX -d -m 755 "$DESTDIR$FISHINSTALLDIR"
install $SELINUX -d -m 755 "$DESTDIR$ZSHINSTALLDIR"
install $SELINUX -d -m 755 "$DESTDIR$CONTAINERS_DIR"
install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/crio-status
install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/crio
install $SELINUX -D -m 644 -t "$DESTDIR$ETCDIR" etc/crictl.yaml
install $SELINUX -D -m 644 -t "$DESTDIR$OCIDIR" etc/crio-umount.conf
install $SELINUX -D -m 644 -t "$DESTDIR$ETCDIR/crio" etc/crio.conf
install $SELINUX -D -m 644 -t "$DESTDIR$ETCDIR/crio/crio.conf.d" etc/10-crun.conf
install $SELINUX -D -m 644 -t "$DESTDIR$MANDIR/man5" man/crio.conf.5
install $SELINUX -D -m 644 -t "$DESTDIR$MANDIR/man5" man/crio.conf.d.5
install $SELINUX -D -m 644 -t "$DESTDIR$MANDIR/man8" man/crio-status.8
install $SELINUX -D -m 644 -t "$DESTDIR$MANDIR/man8" man/crio.8
install $SELINUX -D -m 644 -t "$DESTDIR$BASHINSTALLDIR" completions/bash/crio
install $SELINUX -D -m 644 -t "$DESTDIR$FISHINSTALLDIR" completions/fish/crio.fish
install $SELINUX -D -m 644 -t "$DESTDIR$ZSHINSTALLDIR" completions/zsh/_crio
install $SELINUX -D -m 644 -t "$DESTDIR$CONTAINERS_DIR" contrib/policy.json
install $SELINUX -D -m 644 -t "$DESTDIR$SYSTEMDDIR" contrib/crio.service
install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/pinns
install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/crun

if [[ $ARCH == amd64 ]]; then
    install $SELINUX -D -m 755 -t "$DESTDIR$BINDIR" bin/runc
fi
