export AUTO_NOTIFY_VERSION="0.10.2"

# Time it takes for a notification to expire
[[ -z "$AUTO_NOTIFY_EXPIRE_TIME" ]] &&
    export AUTO_NOTIFY_EXPIRE_TIME=8000
# Threshold in seconds for when to automatically show a notification
[[ -z "$AUTO_NOTIFY_THRESHOLD" ]] &&
    export AUTO_NOTIFY_THRESHOLD=10

# List of commands/programs to ignore sending notifications for
[[ -z "$AUTO_NOTIFY_IGNORE" ]] &&
    export AUTO_NOTIFY_IGNORE=(
        'vim'
        'nvim'
        'less'
        'more'
        'man'
        'tig'
        'watch'
        'git commit'
        'top'
        'htop'
        'ssh'
        'nano'
    )

function _auto_notify_format() {
    local MESSAGE="$1"
    local command="$2"
    local elapsed="$3"
    local exit_code="$4"
    MESSAGE="${MESSAGE//\%command/$command}"
    MESSAGE="${MESSAGE//\%elapsed/$elapsed}"
    MESSAGE="${MESSAGE//\%exit_code/$exit_code}"
    printf "%s" "$MESSAGE"
}

function _auto_notify_message() {
    local command="$1"
    local elapsed="$2"
    local exit_code="$3"
    local platform="$(uname)"
    # Run using echo -e in order to make sure notify-send picks up new line
    local DEFAULT_TITLE="\"%command\" Completed"
    local DEFAULT_BODY="$(echo -e "Total time: %elapsed seconds\nExit code: %exit_code")"

    local title="${AUTO_NOTIFY_TITLE:-$DEFAULT_TITLE}"
    local text="${AUTO_NOTIFY_BODY:-$DEFAULT_BODY}"

    title="$(_auto_notify_format "$title" "$command" "$elapsed" "$exit_code")"
    body="$(_auto_notify_format "$text" "$command" "$elapsed" "$exit_code")"

    if [[ "$platform" == "Linux" ]]; then
        local urgency="normal"
        local transient="--hint=int:transient:1"
        local icon=${AUTO_NOTIFY_ICON_SUCCESS:-""}
        # Exit code 130 is returned when a process is terminated with SIGINT.
        # Since the user is already interacting with the program, there is no
        # need to make the notification persistent.
        if [[ "$exit_code" != "0" ]] && [[ "$exit_code" != "130" ]]; then
            urgency="critical"
            transient=""
            icon=${AUTO_NOTIFY_ICON_FAILURE:-""}
        fi

        local arguments=("$title" "$body" "--app-name=zsh" "$transient" "--urgency=$urgency" "--expire-time=$AUTO_NOTIFY_EXPIRE_TIME")

	if [[ -n "$icon" ]]; then
            arguments+=("--icon=$icon")
	fi
        notify-send ${arguments[@]}

    elif [[ "$platform" == "Darwin" ]]; then
        osascript \
          -e 'on run argv' \
          -e 'display notification (item 1 of argv) with title (item 2 of argv)' \
          -e 'end run' \
          "$body" "$title"
    else
        printf "Unknown platform for sending notifications: $platform\n"
        printf "Please post an issue on gitub.com/MichaelAquilina/zsh-auto-notify/issues/\n"
    fi
}

function _is_auto_notify_ignored() {
    local command="$1"
    # split the command if its been piped one or more times
    local command_list=("${(@s/|/)command}")
    local target_command="${command_list[-1]}"
    # Remove leading whitespace
    target_command="$(echo "$target_command" | sed -e 's/^ *//')"

    # If the command is being run over SSH, then ignore it
    if [[ -n ${SSH_CLIENT-} || -n ${SSH_TTY-} || -n ${SSH_CONNECTION-} ]]; then
        print "yes"
        return
    fi

    # Remove sudo prefix from command if detected
    if [[ "$target_command" == "sudo "* ]]; then
        target_command="${target_command/sudo /}"
    fi

    # If AUTO_NOTIFY_WHITELIST is defined, then auto-notify will ignore
    # any item not defined in the white list
    # Otherwise - the alternative (default) approach is used where the
    # AUTO_NOTIFY_IGNORE blacklist is used to ignore commands

    if [[ -n "$AUTO_NOTIFY_WHITELIST" ]]; then
        for allowed in $AUTO_NOTIFY_WHITELIST; do
            if [[ "$target_command" == "$allowed"* ]]; then
                print "no"
                return
            fi
        done
        print "yes"
    else
        for ignore in $AUTO_NOTIFY_IGNORE; do
            if [[ "$target_command" == "$ignore"* ]]; then
                print "yes"
                return
            fi
        done
        print "no"
    fi
}

function _auto_notify_send() {
    # Immediately store the exit code before it goes away
    local exit_code="$?"

    if [[ -z "$AUTO_COMMAND" && -z "$AUTO_COMMAND_START" ]]; then
        return
    fi

    if [[ "$(_is_auto_notify_ignored "$AUTO_COMMAND_FULL")" == "no" ]]; then
        local current="$(date +"%s")"
        let "elapsed = current - AUTO_COMMAND_START"

        if [[ $elapsed -gt $AUTO_NOTIFY_THRESHOLD ]]; then
            _auto_notify_message "$AUTO_COMMAND" "$elapsed" "$exit_code"
        fi
    fi

    # Empty tracking so that notifications are not
    # triggered for any commands not run (e.g ctrl+C when typing)
    _auto_notify_reset_tracking
}

function _auto_notify_track() {
    # $1 is the string the user typed, but only when history is enabled
    # $2 is a single-line, size-limited version of the command that is always available
    # To still do something useful when history is disabled, although with reduced functionality, fall back to $2 when $1 is empty
    AUTO_COMMAND="${1:-$2}"
    AUTO_COMMAND_FULL="$3"
    AUTO_COMMAND_START="$(date +"%s")"
}

function _auto_notify_reset_tracking() {
    # Command start time in seconds since epoch
    unset AUTO_COMMAND_START
    # Full command that the user has executed after alias expansion
    unset AUTO_COMMAND_FULL
    # Command that the user has executed
    unset AUTO_COMMAND
}

function disable_auto_notify() {
    add-zsh-hook -D preexec _auto_notify_track
    add-zsh-hook -D precmd _auto_notify_send
}

function enable_auto_notify() {
    autoload -Uz add-zsh-hook
    add-zsh-hook preexec _auto_notify_track
    add-zsh-hook precmd _auto_notify_send
}

_auto_notify_reset_tracking


platform="$(uname)"
if [[ "$platform" == "Linux" ]] && ! type notify-send > /dev/null; then
    printf "'notify-send' must be installed for zsh-auto-notify to work\n"
    printf "Please install it with your relevant package manager\n"
else
    enable_auto_notify
fi