#!/bin/bash

# Manager for manual interventions.

# Exit code:
#    0   Success (i.e. execution detected no error).
#   >0   Unspecified failure, see the code below.

echo2() { echo -e "$@" >&2; }
WARN()  { echo2 "==> $progname: warning: $1"; }
DIE()   { echo2 "==> $progname: error: $1"; exit 1; }

RUN() {
    local reply=""
    # echo2 "   ->" "$@"
    read -p "==> Run '$*' (Y/n)? " reply >&2
    case "$reply" in
        "" | [yY]*) "$@" || DIE "'$*' failed." ;;
    esac
}

MarkOk() {
    echo2 "  ==> marking '$1' as handled"
    echo "$1" >> "$handled_fixes"
}
IsOk() { grep "^$1$" "$handled_fixes" &>/dev/null; }

FetchInterventionsFile() {
    local -r dirname="$progname"
    local -r filename="${interventions_file##*/}"
    local -r github="https://raw.githubusercontent.com/endeavouros-team/PKGBUILDS/master/$dirname/$filename"
    local -r gitlab="https://gitlab.com/endeavouros-filemirror/PKGBUILDS/raw/master/$dirname/$filename"

    if [ ! -d "$interventions_dir" ] ; then
        mkdir -p "$interventions_dir" || DIE "cannot create folder '${interventions_dir/$HOME/\~}'"
    fi

    if [ $use_local_ifile = yes ] ; then
        cp "/etc/$filename" "$interventions_file"
    else
        # fetch the interventions file from github/gitlab
        local dlok=yes tim=20
        wget -T$tim -qO "$interventions_file" "$github" || wget -T$tim -qO "$interventions_file" "$gitlab" || dlok=no
        if [ $dlok = no ] ; then
            WARN "failed to download $filename\n    from github/gitlab, use it from /etc instead."
            cp "/etc/$filename" "$interventions_file"    # /etc/$filename should exist always
        fi
    fi

    if [ -e "$interventions_file" ] && [ $(stat -c %s "$interventions_file") -gt 0 ] ; then
        source "$interventions_file"
    else
        DIE "file ${interventions_file/$HOME/\~} has no contents!"
    fi
}

RunInterventionsIfNeeded() {
    local count=${#MI_func_names[@]}
    local ix mifunc descr
    local ret=2
    local updated=no

    # ret:
    #      0 = intervention handled and system updated ==> all is OK
    #      1 = error occurred

    if [ $only_show_count = yes ] ; then
        echo "$((count/2))"
        return 2
    fi

    if [ $count -eq 0 ] ; then
        echo2 "==> No manual interventions available."
        return 2
    fi

    for ((ix=0; ix < count; ix+=2)) ; do
        mifunc="${MI_func_names[$ix]}"
        descr="${MI_func_names[$((ix+1))]}"
        if IsOk "$mifunc" ; then
            echo2 "$mifunc:\talready OK"
        else
            echo2 "$mifunc: $descr"
            $mifunc
            ret=$?
            case "$ret" in
                0) MarkOk "$mifunc"; updated=yes ;;
                2) MarkOk "$mifunc" ;;
                1) break ;;
            esac
        fi
    done

    Cleanup

    case "$ret" in
        0|2) [ $updated = yes ] && ret=0 ;;
    esac
    return $ret
}

NewsPage() {
    local bb
    for bb in exo-open kde-open xdg-open firefox ; do
        if [ -x /bin/$bb ] ; then
            /bin/$bb https://archlinux.org/news &
            break
        fi
    done
}

CheckPacmanConf() {
    local lines="$(grep -n "^[ \t]*IgnorePkg[ \t]*=[ \t]*[a-zA-Z0-9]" $pacmanconf)"
    if [ "$lines" ] ; then
        cat <<EOF >&2

==> $progname: warning:
        File $pacmanconf contains IgnorePkg line(s).
        This *may* affect manual interventions because partial upgrades are not supported.
EOF
        if [ $ask_ignorepkg = yes ] ; then
            readarray -t lines < <(echo "$lines")
            printf "      %s\n" "${lines[@]}" >&2
            echo2 ""
            local reply=""
            read -p "==> Do you want to continue [yes/NO]? " reply >&2
            case "$reply" in
                [yY]*) ;;
                *) exit 0 ;;
            esac
        fi
    fi
}

Cleanup() {
    rm -f "$interventions_file"
}

Help() {
    cat <<EOF >&2
Purpose: Execute and manage essential manual interventions.
Usage:   $progname [options]
Options: -h, --help            This help.
         --ask-ignorepkg       If 'IgnorePkg' is used in $pacmanconf, ask user whether to proceed.
         -n, --show-arch-news  Show the Arch news page in a browser.
         --count               Only show the number of available interventions, don't run them.
EOF
}

Options() {
    local arg
    for arg in "$@" ; do
        case "$arg" in
            -h | --help)           Help; exit 0 ;;
            -n | --show-arch-news) NewsPage; exit 0 ;;
            --ask-ignorepkg)       ask_ignorepkg=yes ;;
            --count)               only_show_count=yes ;;
            -t | --test)           use_local_ifile=yes ;;   # for internal testing
            -*)                    WARN "unsupported option $arg, see $progname --help" ;;
            *)                     WARN "unsupported parameter '$arg', see $progname --help" ;;
        esac
    done
}

Main() {
    local -r progname=${0##*/}
    local -r interventions_dir="$HOME/.cache/$progname"
    local -r interventions_file="$interventions_dir/$progname.interventions"
    local -r handled_fixes="$interventions_dir/$progname.handled"
    local -r pacmanconf=/etc/pacman.conf
    local ask_ignorepkg=no
    local MI_func_names=()
    local only_show_count=no
    local use_local_ifile=no

    Options "$@"
    CheckPacmanConf
    FetchInterventionsFile
    RunInterventionsIfNeeded
    echo "" >&2
    return 0
}

Main "$@"
