#!/bin/bash

set -e

# ------------------------------------------------------------
# Company:  Software- und Organisations-Service GmbH
# Date:     2024-03-02
# Purpose:  Cancel orders for Daily Plan dates
# ------------------------------------------------------------
#
# Examples:
# ./cancel-dailyplan-orders.sh --url=https://joc-2-0-primary.sos:7443 --user=root --password=root --date-from=2024-03-01
#    cancels orders assigned the given daily plan date
# ./cancel-dailyplan-orders.sh --url=https://joc-2-0-primary.sos:7443 --user=root --password=root --date-from="$(TZ=Europe/London date --date="1 day ago" +'%Y-%m-%d')" --workflow-folders="/daily-plan/accounting,/daily-plan/invoicing" --recursive
#    cancels orders assigned the given daily plan date recursively from a list of workflow folders

# ------------------------------
# Global script variables
# ------------------------------

joc_url=
joc_user=
joc_password=
joc_cacert=
joc_client_cert=
joc_client_key=
controller_id=
make_dirs=
show_logs=
log_dir=
verbose=

item=
start_time=$(date +"%Y-%m-%dT%H-%M-%S")
response_json=
access_token=
date_from=
date_to=
workflow_folders=
schedule_folders=
recursive=false
workflows=
schedules=

# ------------------------------
# Inline Functions
# ------------------------------

AskPassword() {
    joc_password="$(
        exec < /dev/tty || exit
        tty_config=$(stty -g) || exit
        trap 'stty "$tty_config"' EXIT INT TERM
        stty -echo || exit
        printf 'Password: ' > /dev/tty
        IFS= read -r password; rc=$? 2> /dev/tty
        echo > /dev/tty
        printf '%s\n' "$password"
        exit "$rc"
    )"
}

Log()
{
    if [ -n "${log_file}" ] && [ -f "${log_file}" ]
    then
        echo "$@" >> "${log_file}"
    fi
    
    if [ -z "${show_logs}" ]
    then
        echo "$@"
    fi
}

LogVerbose()
{
    if [ -n "${verbose}" ]
    then
        if [ -n "${log_file}" ] && [ -f "${log_file}" ]
        then
            echo "$@" >> "${log_file}"
        fi
    
        if [ -z "${show_logs}" ]
        then
            echo "$@"
        fi
    fi
}

LogWarning()
{
    if [ -n "${log_file}" ] && [ -f "${log_file}" ]
    then
        echo "[WARN]" "$@" >> "${log_file}"
    fi
    
    >&2 echo "[WARN]" "$@"
}

LogError()
{
    if [ -n "${log_file}" ] && [ -f "${log_file}" ]
    then
        echo "[ERROR]" "$@" >> "${log_file}"
    fi
    
    >&2 echo "[ERROR]" "$@"
}

Login()
{ 
    LogVerbose ".. Login"
    curl_options=(-k -L -s -S -X POST -m 15)

    if [ -n "${joc_user}" ] && [ -n "${joc_password}" ]
    then
        curl_options+=(--user "${joc_user}":"${joc_password}")
    fi

    if [ "${joc_cacert}" != "" ]
    then
        curl_options+=(--cacert "${joc_cacert}")
    fi

    if [ "${joc_client_cert}" != "" ]
    then
        curl_options+=(--cert "${joc_client_cert}")
    fi

    if [ "${joc_client_key}" != "" ]
    then
        curl_options+=(--key "${joc_client_key}")
    fi

    LogVerbose ".... request:"
    LogVerbose "curl ${curl_options[*]} -H "Accept: application/json" -H "Content-Type: application/json" ${joc_url}/joc/api/authentication/login"

    response_json=$(curl ${curl_options[@]} -H "Accept: application/json" -H "Content-Type: application/json" "${joc_url}"/joc/api/authentication/login)
    LogVerbose ".... response:"
    LogVerbose "${response_json}"

    if echo "${response_json}" | jq -e . >/dev/null 2>&1
    then
        access_token=$(echo "${response_json}" | jq -r ".accessToken // empty" | sed 's/^"//' | sed 's/"$//')
        LogVerbose ".... access token: ${access_token}"
        if [ -z "${access_token}" ]
        then
            LogError "Login failed: ${response_json}"
            exit 4
        fi
    else
        LogError "Login failed: ${response_json}"
        exit 4
    fi
}

Logout()
{
    LogVerbose ".. Logout"
    curl_options=(-k -L -s -S -X POST -m 15)

    if [ "${joc_cacert}" != "" ]
    then
        curl_options+=(--cacert "${joc_cacert}")s
    fi

    if [ "${joc_client_cert}" != "" ]
    then
        curl_options+=(--cert "${joc_client_cert}")
    fi

    if [ "${joc_client_key}" != "" ]
    then
        curl_options+=(--key "${joc_client_key}")
    fi

    LogVerbose ".... request:"
    LogVerbose "curl ${curl_options[*]} -H \"X-Access-Token: ${access_token}\" -H \"Accept: application/json\" -H \"Content-Type: application/json\" ${joc_url}/joc/api/authentication/logout"

    response_json=$(curl ${curl_options[@]} -H "X-Access-Token: ${access_token}" -H "Accept: application/json" -H "Content-Type: application/json" "${joc_url}"/joc/api/authentication/logout)
    LogVerbose ".... response:"
    LogVerbose "${response_json}"

    if echo "${response_json}" | jq -e . >/dev/null 2>&1
    then
        item=$(echo "${response_json}" | jq -r "select(.isAuthenticated == false) // empty" | sed 's/^"//' | sed 's/"$//')
        if [ -z "${item}" ]
        then
            LogError "Logout failed: ${response_json}"
            exit 4
        fi
        access_token=
    else
        LogError "Logout failed: ${response_json}"
        exit 4
    fi
}

Get_Timezone()
{
    if [ -z "${time_zone}" ]
    then
        if [ -n "${TZ}" ]
        then
            time_zone=${TZ}
        else
            if command -v timedatectl &> /dev/null
            then
                time_zone=$(timedatectl | grep -E -o  'Time zone: (.*)[ ]?.*\1' | cut -d' ' -f3)
                if [ ! "${time_zone}" = "$(timedatectl list-timezones | grep "${time_zone}")" ]
                then
                    time_zone=
                fi
            fi
        fi

        if [ -z "${time_zone}" ]
        then
            if [ -f /etc/timezone ]
            then
                time_zone=$(cat /etc/timezone)
            else
                if [ -f /etc/localtime ]
                then
                   full_info=$(readlink -f /etc/localtime)
                   zone_info="/usr/share/zoneinfo/"
                   time_zone=$(printf '%s' "${full_info//${zone_info}/}")
                fi
            fi
        fi

        if [ -z "${time_zone}" ]
        then
            LogError "could not determine system time zone, specify time zone using: --time-zone=<time-zone>"
            exit 1
        fi
    fi
}

Cancel_DailyPlan_Orders()
{
    LogVerbose ".. Cancel_DailyPlan_Orders()"
    curl_options=(-k -L -s -S -X POST -m 15)

    if [ "${joc_cacert}" != "" ]
    then
        curl_options+=(--cacert "${joc_cacert}")
    fi

    if [ "${joc_client_cert}" != "" ]
    then
        curl_options+=(--cert "${joc_client_cert}")
    fi

    if [ "${joc_client_key}" != "" ]
    then
        curl_options+=(--key "${joc_client_key}")
    fi

    request_body="{ \"controllerId\": \"${controller_id}\""

    if [ -n "${states}" ]
    then
        request_body="${request_body}, \"states\": ["
        comma=
        set -- "$(echo "${states}" | sed -r 's/[,]+/ /g')"
        for i in $@; do
            request_body="${request_body}${comma} \"${i}\""
            comma=,
        done
        request_body="${request_body} ]"
    fi

    if [ -n "${workflows}" ]
    then
        request_body="${request_body}, \"workflowflowPaths\": ["
        comma=
        set -- "$(echo "${workflows}" | sed -r 's/[,]+/ /g')"
        for i in $@; do
            request_body="${request_body}${comma} \"${i}\""
            comma=,
        done
        request_body="${request_body} ]"
    fi

    if [ -n "${workflow_folders}" ]
    then
        request_body="${request_body}, \"workflowFolders\": ["
        comma=
        set -- "$(echo "${workflow_folders}" | sed -r 's/[,]+/ /g')"
        for i in $@; do
            request_body="${request_body}${comma} {\"folder\": \"${i}\", \"recursive\": ${recursive}}"
            comma=,
        done
        request_body="${request_body} ]"
    fi

    if [ -n "${schedules}" ]
    then
        request_body="${request_body}, \"schedulePaths\": ["
        comma=
        set -- "$(echo "${schedules}" | sed -r 's/[,]+/ /g')"
        for i in $@; do
            request_body="${request_body}${comma} \"${i}\""
            comma=,
        done
        request_body="${request_body} ]"
    fi

    if [ -n "${schedule_folders}" ]
    then
        request_body="${request_body}, \"scheduleFolders\": ["
        comma=
        set -- "$(echo "${schedule_folders}" | sed -r 's/[,]+/ /g')"
        for i in $@; do
            request_body="${request_body}${comma} {\"folder\": \"${i}\", \"recursive\": ${recursive}}"
            comma=,
        done
        request_body="${request_body} ]"
    fi

    if [ -n "${date_from}" ]
    then
        request_body="${request_body}, \"dailyPlanDateFrom\": \"${date_from}\""
    fi

    if [ -n "${date_to}" ]
    then
        request_body="${request_body}, \"dailyPlanDateTo\": \"${date_to}\""
    fi

    request_body="${request_body} }"

    LogVerbose ".... request:"
    LogVerbose "curl ${curl_options[*]} -H \"X-Access-Token: ${access_token}\" -H \"Accept: application/json\" -H \"Content-Type: application/json\" -d ${request_body} ${joc_url}/joc/api/daily_plan/orders/cancel"

    response_json=$(curl ${curl_options[@]} -H "X-Access-Token: ${access_token}" -H "Accept: application/json" -H "Content-Type: application/json" -d "${request_body}"  "${joc_url}"/joc/api/daily_plan/orders/cancel)
    LogVerbose ".... response:"
    LogVerbose "${response_json}"

    if echo "${response_json}" | jq -e . >/dev/null 2>&1
    then
        ok=$(echo "${response_json}" | jq -r ".ok // empty" | sed 's/^"//' | sed 's/"$//')
        if [ -z "${ok}" ]
        then
            error_code=$(echo "${response_json}" | jq -r ".error.code // empty" | sed 's/^"//' | sed 's/"$//')
            if [ "${error_code}" = "JOC-400" ]
            then
                LogWarning "Cancel_DailyPlan_Orders() did not find orders: ${response_json}"
                exit 3
            else
                LogError "Cancel_DailyPlan_Orders() failed: ${response_json}"
                exit 4
            fi
        fi
    else
        LogError "Cancel_DailyPlan_Orders() failed: ${response_json}"
        exit 4
    fi
}

Usage()
{
    >&2 echo ""
    >&2 echo "Usage: $(basename "$0") [Options] [Switches]"
    >&2 echo ""
    >&2 echo "  Options:"
    >&2 echo "    --url=<url>                       | required: JOC Cockpit URL"
    >&2 echo "    --user=<account>                  | required: JOC Cockpit user account"
    >&2 echo "    --password=<password>             | optional: JOC Cockpit password"
    >&2 echo "    --controller-id=<identifier>      | optional: Controller ID"
    >&2 echo "    --ca-cert=<path>                  | optional: path to CA Certificate used for JOC Cockpit login"
    >&2 echo "    --client-cert=<path>              | optional: path to Client Certificate used for login"
    >&2 echo "    --client-key=<path>               | optional: path to Client Key used for login"
    >&2 echo "    --date-from=<date>                | optional: orders added to the daily plan starting from the given date will be cancelled, default: ${date_from}"
    >&2 echo "    --date-to=<date>                  | optional: orders added to the given daily plan before the given date will be cancelled, default: ${date_to}"
    >&2 echo "    --workflow-folders=<path[,path]>  | optional: list of folders holding workflows for which orders will be cancelled"
    >&2 echo "    --workflows=<path[,path]>         | optional: list of workflows for which orders will be cancelled"
    >&2 echo "    --schedule-folders=<path[,path]>  | optional: list of folders holding schedules for which orders will be cancelled"
    >&2 echo "    --schedules=<path[,path]>         | optional: list of schedules for which orders will be cancelled"
    >&2 echo "    --log-dir=<directory>             | optional: path to the directory holding the script's batch log files"
    >&2 echo ""
    >&2 echo "  Switches:"
    >&2 echo "    -h | --help                       | displays usage"
    >&2 echo "    -v | --verbose                    | displays verbose output"
    >&2 echo "    -p | --password                   | asks for password"
    >&2 echo "    -r | --recursive                  | specifies folders to be looked up recursively"
    >&2 echo "    --show-logs                       | shows log output if --log-dir is used"
    >&2 echo "    --make-dirs                       | creates directories if they do not exist"
    >&2 echo ""
}

Arguments()
{
    Get_Timezone

    args="$*"
    for option in "$@"
    do
        case "${option}" in
            --url=*)                joc_url=$(echo "${option}" | sed 's/--url=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --user=*)               joc_user=$(echo "${option}" | sed 's/--user=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --password=*)           joc_password=$(echo "${option}" | sed 's/--password=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --cacert=*)             joc_cacert=$(echo "${option}" | sed 's/--cacert=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --client-cert=*)        joc_client_cert=$(echo "${option}" | sed 's/--client-cert=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --client-key=*)         joc_client_key=$(echo "${option}" | sed 's/--client-key=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --controller-id=*)      controller_id=$(echo "${option}" | sed 's/--controller-id=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --date-from=*)          date_from=$(echo "${option}" | sed 's/--date-from=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --date-to=*)            date_to=$(echo "${option}" | sed 's/--date-to=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --workflow-folders=*)   workflow_folders=$(echo "${option}" | sed 's/--workflow-folders=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --schedule-folders=*)   schedule_folders=$(echo "${option}" | sed 's/--schedule-folders=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --workflows=*)          workflows=$(echo "${option}" | sed 's/--workflows=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --schedules=*)          schedules=$(echo "${option}" | sed 's/--schedules=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            --log-dir=*)            log_dir=$(echo "${option}" | sed 's/--log-dir=//' | sed 's/^"//' | sed 's/"$//' | sed 's/^\(.*\)\/$/\1/')
                                    ;;
            # Switches
            -h|--help)              Usage
                                    exit
                                    ;;
            -v|--verbose)           verbose=1
                                    ;;
            -p|--password)          AskPassword
                                    ;;
            -r|--recursive)         recursive=true
                                    ;;
            --make-dirs)            make_dirs=1
                                    ;;
            --show-logs)            show_logs=1
                                    ;;
            *)                      >&2 echo "unknown option: ${option}"
                                    Usage
                                    exit 1
                                    ;;
        esac
    done

    if ! command -v jq &> /dev/null
    then
        LogError "jq utility not found"
        exit 1
    fi

    if [ -z "${joc_url}" ]
    then
        LogError "JOC Cockpit URL not specified: --url=<url>"
        Usage
        exit 1
    fi

    if [ -z "${joc_user}" ]
    then
        LogError "JOC Cockpit user account not specified: --user=<account>"
        Usage
        exit 1
    fi

    if [ -n "${joc_cacert}" ] && [ ! -f "${joc_cacert}" ]
    then
        LogError "Root CA Certificate file not found: --cacert=${joc_cacert}"
        Usage
        exit 1
    fi

    if [ -n "${joc_client_cert}" ] && [ ! -f "${joc_client_cert}" ]
    then
        LogError "Client Certificate file not found: --client-cert=${joc_client_cert}"
        Usage
        exit 1
    fi

    if [ -n "${joc_client_key}" ] && [ ! -f "${joc_client_key}" ]
    then
        LogError "Client Private Key file not found: --client-key=${joc_client_key}"
        Usage
        exit 1
    fi

    if [ -n "${show_logs}" ] && [ -z "${log_dir}" ]
    then
        LogError "Log directory not specified and --show-logs switch is present: --log-dir="
        Usage
        exit 1
    fi

    if [ -z "${make_dirs}" ] && [ -n "${log_dir}" ] && [ ! -d "${log_dir}" ]
    then
        LogError "Log directory not found and --make-dirs switch not present: --log-dir=${log_dir}"
        Usage
        exit 1
    fi

    if [ -z "${date_from}" ] && [ -z "${date_to}" ]
    then
        LogError "Specify the daily plan date from which orders will be cancelled: --date-from=<date>"
        Usage
        exit 1
    fi

    if [ -z "${date_from}" ] && [ -n "${date_to}" ]
    then
        date_from=${date_to}
    fi

    if [ -n "${date_from}" ] && [ -z "${date_to}" ]
    then
        date_to=${date_from}
    fi

    # initialize logging
    if [ -n "${log_dir}" ]
    then
        # create log directory if required
        if [ ! -d "${log_dir}" ] && [ -n "${make_dirs}" ]
        then
            mkdir -p "${log_dir}"
        fi
    
        log_file="${log_dir}"/create-flag-files."${start_time}".log
        while [ -f "${log_file}" ]
        do
            sleep 1
            start_time=$(date +"%Y-%m-%dT%H-%M-%S")
            log_file="${log_dir}"/create-flag-files."${start_time}".log
        done
        
        touch "${log_file}"
    fi

    Log "-- begin of log --------------"
    Log "$0" "$(echo "${args}" | sed 's/--password=\([^--]*\)//')"
    Log "-- begin of output -----------"
}

# ------------------------------
# Main
# ------------------------------

Process()
{
    LogVerbose ".. Processing"
    
    Login
    Cancel_DailyPlan_Orders
    Logout
}

# ------------------------------
# Cleanup trap
# ------------------------------

End()
{
    if [ -n "${access_token}" ]
    then
        Logout
    fi

    if [ "$1" = "EXIT" ]
    then
        Log "-- end of log ----------------"

        if [ -n "${show_logs}" ] && [ -f "${log_file}" ]
        then
            cat "${log_file}"
        fi        
    fi

    unset joc_url
    unset joc_cacert
    unset joc_client_cert
    unset joc_client_key
    unset joc_user
    unset joc_password

    unset make_dirs
    unset show_logs
    unset verbose
    unset log_dir
    unset date_from
    unset date_to
    unset workflow_folders
    unset workflows
    unset schedule_folders
    unset schedules
    unset recursive

    unset log_file
    unset start_time

    unset response_json
    unset access_token
    unset curl_options

    set +e
}

# ------------------------------
# Enable trap and start
# ------------------------------

trap 'End EXIT' EXIT
trap 'End SIGTERM' TERM
trap 'End SIGINT' INT

Arguments "$@"
Process
