#!/usr/bin/env bash

set -e
set -o pipefail

# Script for Git repos housing edX services. These repos are mounted as
# data volumes into their corresponding Docker containers to facilitate development.
# Repos are cloned to/removed from the directory above the one housing this file.

if [ -z "$DEVSTACK_WORKSPACE" ]; then
    echo "need to set workspace dir"
    exit 1
elif [ -d "$DEVSTACK_WORKSPACE" ]; then
    cd "$DEVSTACK_WORKSPACE"
else
    echo "Workspace directory $DEVSTACK_WORKSPACE doesn't exist"
    exit 1
fi

# When you add new services should add them to both repos and ssh_repos
# (or non_release_repos and non_release_ssh_repos if they are not part
# of Open edX releases).
repos=(
    "https://github.com/openedx/course-discovery.git"
    "https://github.com/openedx/credentials.git"
    "https://github.com/openedx/cs_comments_service.git"
    "https://github.com/openedx/ecommerce.git"
    "https://github.com/openedx/edx-notes-api.git"
    "https://github.com/openedx/edx-platform.git"
    "https://github.com/openedx/xqueue.git"
    "https://github.com/openedx/edx-analytics-dashboard.git"
    "https://github.com/openedx/frontend-app-gradebook.git"
    "https://github.com/openedx/frontend-app-learner-dashboard"
    "https://github.com/openedx/frontend-app-learner-record"
    "https://github.com/openedx/frontend-app-payment.git"
    "https://github.com/openedx/frontend-app-publisher.git"
    "https://github.com/openedx/edx-analytics-dashboard.git"
    "https://github.com/openedx/edx-analytics-data-api.git"
)

non_release_repos=(
    "https://github.com/openedx/frontend-app-authn.git"
    "https://github.com/openedx/frontend-app-course-authoring.git"
    "https://github.com/openedx/frontend-app-learning.git"
    "https://github.com/openedx/frontend-app-library-authoring.git"
    "https://github.com/openedx/registrar.git"
    "https://github.com/openedx/frontend-app-program-console.git"
    "https://github.com/openedx/frontend-app-account.git"
    "https://github.com/openedx/frontend-app-profile.git"
    "https://github.com/openedx/frontend-app-ora-grading.git"
)

ssh_repos=(
    "git@github.com:openedx/course-discovery.git"
    "git@github.com:openedx/credentials.git"
    "git@github.com:openedx/cs_comments_service.git"
    "git@github.com:openedx/ecommerce.git"
    "git@github.com:openedx/edx-notes-api.git"
    "git@github.com:openedx/edx-platform.git"
    "git@github.com:openedx/xqueue.git"
    "git@github.com:openedx/edx-analytics-dashboard.git"
    "git@github.com:openedx/frontend-app-gradebook.git"
    "git@github.com:openedx/frontend-app-learner-dashboard.git"
    "git@github.com:openedx/frontend-app-learner-record.git"
    "git@github.com:openedx/frontend-app-payment.git"
    "git@github.com:openedx/frontend-app-publisher.git"
    "git@github.com:openedx/edx-analytics-dashboard.git"
    "git@github.com:openedx/edx-analytics-data-api.git"
)

non_release_ssh_repos=(
    "git@github.com:openedx/frontend-app-authn.git"
    "git@github.com:openedx/frontend-app-course-authoring.git"
    "git@github.com:openedx/frontend-app-learning.git"
    "git@github.com:openedx/frontend-app-library-authoring.git"
    "git@github.com:openedx/registrar.git"
    "git@github.com:openedx/frontend-app-program-console.git"
    "git@github.com:openedx/frontend-app-account.git"
    "git@github.com:openedx/frontend-app-profile.git"
    "git@github.com:openedx/frontend-app-ora-grading.git"
)

if [ -n "${OPENEDX_RELEASE}" ]; then
    OPENEDX_GIT_BRANCH=open-release/${OPENEDX_RELEASE}
else
    repos+=("${non_release_repos[@]}")
    ssh_repos+=("${non_release_ssh_repos[@]}")
fi

name_pattern=".*/(.*).git"

_checkout ()
{
    repos_to_checkout=("$@")

    for repo in "${repos_to_checkout[@]}"
    do
        # Use Bash's regex match operator to capture the name of the repo.
        # Results of the match are saved to an array called $BASH_REMATCH.
        [[ $repo =~ $name_pattern ]]
        name="${BASH_REMATCH[1]}"

        # If a directory exists and it is nonempty, assume the repo has been cloned.
        if [ -d "$name" ] && [ -n "$(ls -A "$name" 2>/dev/null)" ]; then
            cd "$name"
            _checkout_and_update_branch
            cd ..
        fi
    done
}

checkout ()
{
    _checkout "${repos[@]}"
}

_clone ()
{

    repos_to_clone=("$@")
    for repo in "${repos_to_clone[@]}"
    do
        # Use Bash's regex match operator to capture the name of the repo.
        # Results of the match are saved to an array called $BASH_REMATCH.
        [[ $repo =~ $name_pattern ]]
        name="${BASH_REMATCH[1]}"

        # If a directory exists and it is nonempty, assume the repo has been checked out
        # and only make sure it's on the required branch
        if [ -d "$name" ] && [ -n "$(ls -A "$name" 2>/dev/null)" ]; then
            if [ ! -d "$name/.git" ]; then
                printf "ERROR: [%s] exists but is not a git repo.\n" "$name"
                exit 1
            fi
            printf "The [%s] repo is already checked out. Checking for updates.\n" "$name"
            cd "${DEVSTACK_WORKSPACE}/${name}"
            _checkout_and_update_branch
            cd ..
        else
            if [ -n "${OPENEDX_GIT_BRANCH:-}" ]; then
                CLONE_BRANCH="-b ${OPENEDX_GIT_BRANCH}"
            else
                CLONE_BRANCH=""
            fi
            if [ "${SHALLOW_CLONE}" == "1" ]; then
                git clone ${CLONE_BRANCH} -c core.symlinks=true --depth=1 "${repo}"
            else
                git clone ${CLONE_BRANCH} -c core.symlinks=true "${repo}"
            fi
        fi
    done
    cd - &> /dev/null
}

_checkout_and_update_branch ()
{
    GIT_SYMBOLIC_REF="$(git symbolic-ref HEAD 2>/dev/null)"
    BRANCH_NAME=${GIT_SYMBOLIC_REF##refs/heads/}
    if [ -n "${OPENEDX_GIT_BRANCH}" ]; then
        CHECKOUT_BRANCH=${OPENEDX_GIT_BRANCH}
    else
        CHECKOUT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')
    fi
    echo "Checking out branch ${CHECKOUT_BRANCH}"
    if [ "${BRANCH_NAME}" == "${CHECKOUT_BRANCH}" ]; then
        git pull origin ${CHECKOUT_BRANCH}
    else
        git fetch origin ${CHECKOUT_BRANCH}:${CHECKOUT_BRANCH}
        git checkout ${CHECKOUT_BRANCH}
    fi
    find . -name '*.pyc' -not -path './.git/*' -delete
}

clone ()
{
    _clone "${repos[@]}"
}

clone_ssh ()
{
    _clone "${ssh_repos[@]}"
}

reset ()
{
    read -p "This will switch to the default branch and pull changes in your local git checkouts. Would you like to proceed? [y/n] " -r
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        echo "Cancelling."
        exit 1
    fi

    for repo in ${repos[*]}
    do
        [[ $repo =~ $name_pattern ]]
        name="${BASH_REMATCH[1]}"

        if [ -d "$name" ]; then
            DEFAULT_BRANCH=$(cd ${name}; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')
            # Try to switch branch and pull, but fail if there are uncommitted changes.
            if (cd "$name"; git checkout -q ${DEFAULT_BRANCH} && git pull -q --ff-only);
            then
                # Echo untracked files to simplify debugging and make it easier to see that resetting does not remove everything
                untracked_files="$(cd ${name} && git ls-files --others --exclude-standard)"
                if [[ $untracked_files ]];
                then
                    echo "The following untracked files are in ${name} repository:"
                    echo "$untracked_files"
                fi
            else
                echo >&2 "Failed to reset $name repo. Exiting."
                echo >&2 "Please go to the repo and clean up any issues that are keeping 'git checkout $DEFAULT_BRANCH' and 'git pull' from working."
                exit 1
            fi
        else
            printf "The [%s] repo is not cloned. Skipping.\n" "$name"
        fi
    done
}

status ()
{
    currDir=$(pwd)
    for repo in ${repos[*]}
    do
        [[ $repo =~ $name_pattern ]]
        name="${BASH_REMATCH[1]}"

        if [ -d "$name" ]; then
            printf "\nGit status for [%s]:\n" "$name"
            cd "$name";git status;cd "$currDir"
        else
            printf "The [%s] repo is not cloned. Skipping.\n" "$name"
        fi
    done
    cd - &> /dev/null
}

if [ "$1" == "checkout" ]; then
    checkout
elif [ "$1" == "clone" ]; then
    clone
elif [ "$1" == "clone_ssh" ]; then
    clone_ssh
elif [ "$1" == "reset" ]; then
    reset
elif [ "$1" == "status" ]; then
    status
fi
