#!/usr/bin/env bash

set -u
set -o pipefail

usage() {
    printf 'usage: incidentcheck --interval SECONDS --timeout SECONDS --successes COUNT --evidence FILE [--sleep-command PATH] -- HEALTH_COMMAND [ARG ...]\n' >&2
}

die_usage() {
    printf 'incidentcheck: %s\n' "$1" >&2
    usage
    exit 64
}

is_unsigned_integer() {
    [[ $1 =~ ^[0-9]+$ ]]
}

interval=
timeout=
required_successes=
evidence=
sleep_command=

while (( $# > 0 )); do
    case $1 in
        --interval)
            (( $# >= 2 )) || die_usage 'missing value for --interval'
            interval=$2
            shift 2
            ;;
        --timeout)
            (( $# >= 2 )) || die_usage 'missing value for --timeout'
            timeout=$2
            shift 2
            ;;
        --successes)
            (( $# >= 2 )) || die_usage 'missing value for --successes'
            required_successes=$2
            shift 2
            ;;
        --evidence)
            (( $# >= 2 )) || die_usage 'missing value for --evidence'
            evidence=$2
            shift 2
            ;;
        --sleep-command)
            (( $# >= 2 )) || die_usage 'missing value for --sleep-command'
            sleep_command=$2
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            die_usage "unknown option: $1"
            ;;
    esac
done

[[ -n $interval ]] || die_usage '--interval is required'
[[ -n $timeout ]] || die_usage '--timeout is required'
[[ -n $required_successes ]] || die_usage '--successes is required'
[[ -n $evidence ]] || die_usage '--evidence is required'
(( $# > 0 )) || die_usage 'a health command is required after --'

is_unsigned_integer "$interval" || die_usage '--interval must be a decimal integer'
is_unsigned_integer "$timeout" || die_usage '--timeout must be a decimal integer'
is_unsigned_integer "$required_successes" || die_usage '--successes must be a decimal integer'
interval=$(( 10#$interval ))
timeout=$(( 10#$timeout ))
required_successes=$(( 10#$required_successes ))
(( interval > 0 )) || die_usage '--interval must be greater than zero'
(( required_successes > 0 )) || die_usage '--successes must be greater than zero'

if [[ -n $sleep_command && ! -x $sleep_command ]]; then
    die_usage '--sleep-command must be executable'
fi

if ! : > "$evidence"; then
    printf 'incidentcheck: cannot write evidence: %s\n' "$evidence" >&2
    exit 73
fi
printf 'event\tsample\telapsed_seconds\tstatus\texit_code\tstreak\n' >> "$evidence"

sample=0
elapsed=0
streak=0

while :; do
    (( sample += 1 ))

    if "$@" >/dev/null 2>&1; then
        health_status=0
        health_label=healthy
        (( streak += 1 ))
    else
        health_status=$?
        health_label=unhealthy
        streak=0
    fi

    printf 'probe\t%d\t%d\t%s\t%d\t%d\n' \
        "$sample" "$elapsed" "$health_label" "$health_status" "$streak" \
        >> "$evidence"

    if (( health_status == 0 )); then
        printf 'result\t-\t%d\trecovered\t0\t%d\n' "$elapsed" "$streak" \
            >> "$evidence"
        exit 0
    fi

    if (( elapsed >= timeout )); then
        break
    fi

    delay=$interval
    remaining=$(( timeout - elapsed ))
    if (( delay > remaining )); then
        delay=$remaining
    fi

    if [[ -n $sleep_command ]]; then
        if ! "$sleep_command" "$delay"; then
            printf 'result\t-\t%d\tscheduler-error\t70\t%d\n' \
                "$elapsed" "$streak" >> "$evidence"
            exit 70
        fi
    else
        sleep "$delay"
    fi
    (( elapsed += delay ))
done

printf 'result\t-\t%d\ttimeout\t1\t%d\n' "$elapsed" "$streak" \
    >> "$evidence"
exit 1
