#!/usr/bin/env bash

###############################################################################
# Handle privilege escalation for docker/non-docker
# There is no sudo_user in docker + there is no sudo in docker even
###############################################################################
if [[ -f "/.dockerenv" ]]; then
    export SUDO_USER=$(whoami)
    export PRIVILEGE=""
else
    export PRIVILEGE="sudo"
fi

###############################################################################
# The following code helps make script errors more prominent and descriptive.
###############################################################################

error_handler() {
    local -r code="$1"
    local -r line="$2"
    local -r file="$3"
    local -r red="\033[0;31m"
    local -r no_color="\033[0m"
    echo -e "${red}ERROR(${code}) on line ${line} of ${file}:${no_color}"\
        "$(head -n ${line} ${file} | tail -1 | xargs)" 1>&2
    exit "${code}"
}
trap 'error_handler $? ${LINENO} ${BASH_SOURCE}' ERR
set -uEo pipefail
if [[ $(echo $BASH_VERSION | cut -d. -f1) -lt 4 ]]; then
    error "Bash 4.0 or later is required to run these scripts."
fi

###############################################################################
# The following helpers make it more convenient to access gitlab resources
###############################################################################

GITLAB_URL=https://git.doit.wisc.edu/
GITLAB_API=api/v4/
GITLAB_COURSE=cdis/cs/courses/cs400/202601/
cgl() {
    local target=$1
    local type=${2:-GET}
    local data=${3:-}
    if [[ -n "$data" ]]; then
        data=(--header "Content-type: application/json" --data "${data}")
    else
        data=()
    fi
    curl --silent --request ${type^^} --header "PRIVATE-TOKEN: ${GITLAB_TOKEN##*:}" "${data[@]}" --url ${GITLAB_URL}${GITLAB_API}${target}
}

###############################################################################
# The following entry point either displays a usage message, or dispatches the
# requested command appropriately (after confirming the availability of needed
# software and gitlab token).
###############################################################################

cs400() {
    if [[ $# -lt 1 || $# -gt 2 ]]; then
        echo "Usage: cs400 [-v] <command>"
        echo ""
        echo "Available commands include:"
        echo "    init     configures user account to use other cs400 commands"
        echo "    get      retrieves starter folder for a specific assignment"
        echo "    submit   submits assignment from current working directory"
        echo "    check    retrieves feedback for current assignment folder"
        echo "    restart  resets contents of current assignment folder"
        echo ""
        echo "The option -v provides a more verbose display of any errors."
        echo ""
        exit 0
    fi

    # redirect extra diagnostics and error messages to VERBOSE
    VERBOSE="/dev/null"
    if [[ $(echo "$@" | grep -ic "\-v") -gt 0 ]]; then
        VERBOSE="/dev/stdout"
    fi

    # switch through command options (collecing username, token, etc as needed)
    read -p "Enter your NetID username: " NETID
    if [[ ${1,,} = init ]]; then
        init
    else
        readToken # sets GITLAB_TOKEN
        readActivityList # sets ACTIVITYLIST
        if [[ ${1,,} = get ]]; then
            get
        else
            confirmActivityWorkingDirectory
            if [[ ${1,,} = submit ]]; then
                submit
            elif [[ ${1,,} = check ]]; then
                check
            elif [[ ${1,,} = restart ]]; then
                restart
            fi
        fi
    fi
}

###############################################################################
# Helper methods for installing needed software, and storing gitlab token.
###############################################################################
init() {
    installJq
    installGum
    # overwrite $USER when run with sudo, to be actual user rather than root
    if [[ $EUID -eq 0 ]]; then export USER="$SUDO_USER"; fi

    setupGitlabAccessToken
    readToken # sets GITLAB_TOKEN
    configLocalGitAccount
}

installJq() {
    echo "Attempting to install jq, when needed."
    if [[ ! $(command -v jq) ]]; then
        if [[ $EUID -ne 0 ]]; then
            echo "This script requires super user privelege to install jq."
            echo "Please try running \"sudo cs400 init\"."
            exit
        fi
        $PRIVILEGE apt update -y
        $PRIVILEGE apt install -y jq
    fi
}

installGum() {
    echo "Attempting to install gum, when needed."
    if [[ ! $(command -v gum) ]]; then
        if [[ $EUID -ne 0 ]]; then
            echo "This script requires super user privelege to install jq."
            echo "Please try running \"sudo cs400 init\"."
            exit
        fi
        $PRIVILEGE apt update -y
        $PRIVILEGE apt install curl gpg -y
        $PRIVILEGE mkdir -p /etc/apt/keyrings
        curl -fsSL https://repo.charm.sh/apt/gpg.key | $PRIVILEGE gpg --dearmor -o /etc/apt/keyrings/charm.gpg
        echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | $PRIVILEGE tee /etc/apt/sources.list.d/charm.list
        $PRIVILEGE apt update -y && $PRIVILEGE apt install -y gum
    fi
}

setupGitlabAccessToken() {
    echo ""
    echo "Setup GitLab Access Token:"
    echo ""

    echo "1) Navigate your browser to https://git.doit.wisc.edu/users/sign_in"
    echo "2) When prompted to sign in, use the \"UW-Madison NetID\" button"
    echo "3) When you've logged in, navigate to https://git.doit.wisc.edu/-/user_settings/personal_access_tokens"
    echo "4) Click the \"Add new token\" button to the right of \"Personal Access Token\" on this page."
    echo "5) Enter your choice of token name, \"cs400-pat\" is descriptive"
    echo "6) Enter May 31, 2026 as the expiration date"
    echo "7) Click to mark every checkboxes under \"Select scopes\""
    echo "8) Click the \"Create token\" button at the bottom of the page"
    echo "9) Click the \"Copy token\" button with a copy icon, and then paste the result below"
    echo ""
    read -p "Paste your GitLab Personal Access Token here (then press enter/return): " GITLAB_TOKEN
    if [[ $(echo "$GITLAB_TOKEN" | grep -Ec "^glpat-") != 1 ]]; then
        echo "A valid Gitlab Personal Access Token should begin with: glpat-"
        echo "Please try running this script again to enter a valid token."
        exit 1
    fi
    ACCESSFILE="$(getent passwd $USER | cut -d: -f6)/.gitlab.access"
    echo "${NETID,,}:${GITLAB_TOKEN}" > $ACCESSFILE
    chown $USER $ACCESSFILE
}

readToken() { # sets GITLAB_TOKEN
    # confirm access token is available
    if [[ ! -f "$(getent passwd $USER | cut -d: -f6)/.gitlab.access" ]]; then
        echo "No gitlab access token found, run \"cs400 init\" to create one"
        exit 1
    fi
    FILE="$(getent passwd $USER | cut -d: -f6)/.gitlab.access"
    declare -g GITLAB_TOKEN=$(cat $FILE)
    # confirm access token matches NETID entered by user
    if [[ ! ${GITLAB_TOKEN%%:*} = ${NETID,,} ]]; then
        echo "NetID entered: $NETID does NOT match NetID stored with GitLab access token: ${GITLAB_TOKEN%%:*}.  Try running \"cs400 init\" to store your NetID along with a valid Gitlab Access Token here for the current user."
        exit 1
    fi
    # confirm Gitlab username also matches NETID entered by user
    local username=$(cgl user | jq -r ".username")
    if [[ ! ${username,,} = ${NETID,,} ]]; then
        echo "Gitlab Username: ${username,,} does not match NetID entered: ${NETID,,}"
        exit 1
    fi
}

configLocalGitAccount() {
    echo "Attempting to install git, when needed."
    if [[ ! $(command -v git) ]]; then
        if [[ $EUID -ne 0 ]]; then
            echo "This script requires super user privelege to install git."
            echo "Please try running \"sudo cs400 init\"."
            exit
        fi
        $PRIVILEGE apt update -y
        $PRIVILEGE apt install -y git
    fi

    echo "Copying gitlab name and email here to local git profile."
    USERINFO=$(cgl user)

    if [[ $EUID -eq 0 ]]; then # work when running this script with sudo
        su $USER -c "git config --global user.name \"$(echo $USERINFO | jq -r .name)\""
        su $USER -c "git config --global user.email \"$(echo $USERINFO | jq -r .email)\""
        su $USER -c "git config --global pull.rebase false"
    else # and also when running this script without sudo
        git config --global user.name "$(echo $USERINFO | jq .name)"
        git config --global user.email "$(echo $USERINFO | jq .email)"
        git config --global pull.rebase false
    fi
}

###############################################################################
# Read list of available activities from gitlab for the non-init commands
###############################################################################
readActivityList() { # sets ACTIVITYLIST
    STUDENTGROUP="${GITLAB_COURSE}students/$NETID"
    ENCODED_STUDENTGROUP="${STUDENTGROUP//\//%2F}"
    GROUPID=$(cgl groups/${ENCODED_STUDENTGROUP})
    if [[ $(echo $GROUPID | grep -c "id") < 1 ]]; then
        echo "Unable to access gitlab group id."
        exit 1
    else
        GROUPID=$(echo $GROUPID | jq .id)
        ACTIVITYLIST="$(cgl groups/$GROUPID/projects | jq -r '.[].name')"
    fi
}

###############################################################################
# Retrieves a copy of the student's gitlab repository for any activity
###############################################################################
get() {
    ACTIVITY=$(gum filter --prompt "Choose activity to get:" $ACTIVITYLIST)
    if [[ -d "$ACTIVITY" ]]; then
        echo "You already have a folder for this activity in your working directory.  You'll need to remove or rename this directory before another with the same name can be created here."
        exit 1
    fi
    if [[ -z "$ACTIVITY" ]]; then
        echo "You failed to select an available activity."
        exit 1
    fi

    URL="${GITLAB_URL}${GITLAB_COURSE}students/${NETID,,}/${ACTIVITY}.git"
    git clone ${URL/https:\/\//https:\/\/${GITLAB_TOKEN}@} > $VERBOSE 2>&1

    echo "You should now have a new $ACTIVITY folder."
    echo "If not, try running \"cs400 get -v\" for more information about any problems that migth have prevented this from working as expected."
}

###############################################################################
# Confirms that script is running from within a listed activity directory
###############################################################################
confirmActivityWorkingDirectory() {
    ACTIVITY="${PWD##*/}"
    if [[ $(echo "$ACTIVITYLIST" | grep -ic "^${ACTIVITY}$") != 1 ]]; then
        echo "The folder you are running this script from does not appear to correspond to a CS400 activity.  This command should be run from a folder containing one of the following names:" $ACTIVITYLIST
        exit 1
    fi
    if [[ ! -d "./.git" ]]; then
        echo "You can only run this script from within a directory that was previously created by running the \"cs400 get\" command."
        exit 1
    fi
}

###############################################################################
# Submits the contents fo the current directory to gitlab
###############################################################################
submit() {
    # update token in origin path in case token used to get has been deleted
    URL="${GITLAB_URL}${GITLAB_COURSE}students/${NETID,,}/${ACTIVITY}.git"
    git remote set-url origin ${URL/https:\/\//https:\/\/$(cat ~/.gitlab.access)@}

    git add -A > $VERBOSE
    git commit -m "cs400 script submission from $(TZ='America/Chicago' date)" > $VERBOSE || true
    git pull -s ours --no-edit &> $VERBOSE
    git push > $VERBOSE 2>&1
    if [[ $? -eq 0 ]]; then
        echo "Work submitted, use \"cs400 check\" to confirm submission time and check result of automated submission checks."
    else
        echo "Encountered a submission error.  Use \"cs400 submit -v\" to retry and display more details about the cause of this error."
    fi
}

###############################################################################
# Retrieves the time and submission checker feedback for most recent submission
###############################################################################
check() {
    STUDENTGROUP="${GITLAB_COURSE}students/$NETID"
    ENCODED_STUDENTGROUP="${STUDENTGROUP//\//%2F}"
    GROUPID=$(cgl groups/${ENCODED_STUDENTGROUP} | jq -r .id)
    PROJECTID=$(cgl groups/$GROUPID/projects?search=$ACTIVITY | jq ".[] | select(.name==\"${ACTIVITY}\") | .id")
    PIPEINFO=$(cgl projects/$PROJECTID/jobs | jq first)
    echo "Status of automated submission checks from most recent submission $(echo $PIPEINFO | jq -r .created_at): $(echo $PIPEINFO | jq -r .status)"
    echo ""
    echo "Note that these submission checks are NOT used for grading, but are instead to help you detect some kinds of submission failures.  For more details about any problems detected, review the \"Executing step_script stage of the job script\" section on this webpage: $(echo $PIPEINFO | jq .web_url)"
}

###############################################################################
# Restarts the assignment by returning repository to previous state
###############################################################################
restart() {
    read -p "Are you sure you want to overwrite the work in this directory: ${PWD} and restart? Enter yes or no: " CONFIRMCHOICE
    if [[ $CONFIRMCHOICE = yes ]]; then
        if [[ -f .restartSubmission ]]; then
            ./.restartSubmission
        else
            if git tag | grep -q restart; then
                rm -rf *
                git checkout restart . > $VERBOSE
            else
                echo "This assignment does not have a way to be restarted."
                exit 1
            fi
        fi
    else
        echo "Assignment restart SAFELY ABORTED."
    fi
}

##############################################################################
# Calls the cs400 method when executing this script from the command line
if ! (return 0 2> /dev/null); then
    cs400 "$@"
fi
##############################################################################



