# rbterm shell integration (zsh).
#
# Emits OSC 133 prompt marks so rbterm can:
#   - paint a green/red gutter badge next to each command
#   - (future) jump to prev/next prompt with a chord
#
# Source this once from ~/.zshrc:
#     source /path/to/rbterm-shell-integration.zsh
#
# Idempotent: re-sourcing the file is safe.

# Skip if not interactive or not actually inside a terminal.
[[ -o interactive ]] || return 0
[[ -t 1 ]]          || return 0

# Internal: emit OSC 133;A (prompt about to draw) right before $PS1
# is rendered. Stash the previous command's $? so D can carry it.
_rbterm_precmd() {
    local _ec=$?
    print -Pn "\e]133;D;${_ec}\e\\"
    print -Pn "\e]133;A\e\\"
}

# Internal: emit OSC 133;C (command output starts) right after the
# user hits Enter, before the command runs.
#
# Skip for wrapper commands (ssh/mosh/telnet/container exec) that
# hand the terminal to a child program with its own prompt loop —
# the local 133;C would otherwise leave the rbterm "command running"
# stripe lit for the whole remote session, which is misleading
# (most of the time the user is at the *remote* prompt, idle). If
# the remote shell has its own rbterm-shell-integration installed,
# its own 133;C / D will mark commands accurately.
_rbterm_preexec() {
    local cmd=${1%% *}     # first word of the command line as typed
    cmd=${cmd##*/}         # strip any path prefix
    case $cmd in
        ssh|mosh|telnet|docker|kubectl|podman|nsenter|lxc|incus) return ;;
    esac
    print -Pn "\e]133;C\e\\"
}

# Hook into zsh's standard arrays (idempotent — won't double-add).
typeset -ga precmd_functions preexec_functions
(( ${precmd_functions[(I)_rbterm_precmd]} ))   || precmd_functions+=(_rbterm_precmd)
(( ${preexec_functions[(I)_rbterm_preexec]} )) || preexec_functions+=(_rbterm_preexec)

# Inline media: `cat photo.jpg` / `cat song.mp3` shows the image or a
# player card in the rbterm pane — locally or on a host you ssh'd into
# (source this file there too; rbterm exports LC_TERMINAL=rbterm, which
# ssh forwards). Text and pipes are untouched. RBTERM_CAT_PLAIN=1 turns
# it off. `function` keeps an existing `cat` alias from mangling this.
# >>> rbterm-inline-media (keep identical in rbterm-shell-integration.{bash,zsh})
_rbterm_inline_max=50000000
_rbterm_in_rbterm() {
    if [ "${LC_TERMINAL:-}" = rbterm ] || [ "${TERM_PROGRAM:-}" = rbterm ] ||
       [ "${RBTERM_INLINE:-}" = 1 ]; then
        return 0
    fi
    # Over ssh LC_TERMINAL only survives if sshd's AcceptEnv allows it
    # (Debian/Ubuntu: LC_*; RHEL/CentOS: a fixed list without it). So ask
    # the terminal itself — XTVERSION, answered as "rbterm(<ver>)" — once
    # per shell. Not inside tmux (tmux answers for itself; set
    # RBTERM_INLINE=1 there).
    case ${_rbterm_probed:-} in yes) return 0 ;; no) return 1 ;; esac
    _rbterm_probed=no
    [ -t 0 ] && [ -t 1 ] && [ -z "${TMUX:-}" ] || return 1
    _rb_stty=$(stty -g 2>/dev/null) || return 1
    stty -echo -icanon min 0 time 10 2>/dev/null
    printf '\033[>q'
    _rb_reply=$(dd bs=256 count=1 2>/dev/null)
    stty "$_rb_stty" 2>/dev/null
    case $_rb_reply in *rbterm*) _rbterm_probed=yes; return 0 ;; esac
    return 1
}
_rbterm_is_media() {
    if command -v file >/dev/null 2>&1; then
        case $(file -b --mime-type -- "$1" 2>/dev/null) in
            image/png|image/jpeg|image/gif|image/bmp|image/x-ms-bmp) return 0 ;;
            audio/mpeg|audio/mp3|audio/x-wav|audio/wav|audio/vnd.wave) return 0 ;;
            audio/ogg|audio/x-vorbis+ogg|application/ogg) return 0 ;;
        esac
        return 1
    fi
    case $1 in
        *.png|*.PNG|*.jpg|*.JPG|*.jpeg|*.JPEG|*.gif|*.GIF|*.bmp|*.BMP) return 0 ;;
        *.mp3|*.MP3|*.wav|*.WAV|*.ogg|*.OGG) return 0 ;;
    esac
    return 1
}
# Sent as iTerm2's multipart form: a header, 3 KB base64 chunks each in its
# own complete escape, then FileEnd. One escape per small write matters on
# Windows: the console host closes an unfinished escape at every write
# boundary, so a single File= escape split across printf/base64/printf
# arrived with an empty body. Chunks also suit tmux passthrough.
_rbterm_send_file() {
    _rb_size=$(wc -c < "$1" | tr -d ' ')
    if [ "$_rb_size" -gt "$_rbterm_inline_max" ]; then
        printf 'rbterm: %s is too large to show inline (%s bytes, limit %s)\n' \
            "$1" "$_rb_size" "$_rbterm_inline_max" >&2
        return 1
    fi
    _rb_name=$(printf '%s' "${1##*/}" | base64 | tr -d '\n')
    if [ -n "${TMUX:-}" ]; then
        _rb_pre='\033Ptmux;\033\033]'; _rb_post='\a\033\\'
    else
        _rb_pre='\033]'; _rb_post='\a'
    fi
    printf "${_rb_pre}1337;MultipartFile=name=%s;size=%s;inline=1${_rb_post}" "$_rb_name" "$_rb_size"
    # awk, not a shell read loop (~40x faster); fflush per chunk keeps
    # each escape in its own write. Delimiters go via ENVIRON so awk
    # doesn't reinterpret their backslashes.
    base64 < "$1" | tr -d '\n' | fold -w 3000 |
        _RB_P=$(printf "$_rb_pre") _RB_Q=$(printf "$_rb_post") awk \
            '{ printf "%s1337;FilePart=%s%s", ENVIRON["_RB_P"], $0, ENVIRON["_RB_Q"]; fflush() }'
    printf "${_rb_pre}1337;FileEnd${_rb_post}"
}
# The wrapper: media files go to the pane inline, anything else — text,
# pipes (`cat x | grep`), options, stdin — goes through the real cat. The
# terminal is only probed when a media file is actually involved.
_rbterm_cat() {
    if [ -t 1 ] && [ $# -gt 0 ] && [ -z "${RBTERM_CAT_PLAIN:-}" ]; then
        _rb_any=
        for _rb_a in "$@"; do
            case $_rb_a in -*) command cat "$@"; return ;; esac
            [ -f "$_rb_a" ] || { command cat "$@"; return; }
            _rbterm_is_media "$_rb_a" && _rb_any=1
        done
        if [ -n "$_rb_any" ] && _rbterm_in_rbterm; then
            _rb_rc=0
            for _rb_a in "$@"; do
                if _rbterm_is_media "$_rb_a"; then
                    _rbterm_send_file "$_rb_a" || _rb_rc=1
                else
                    command cat -- "$_rb_a" || _rb_rc=1
                fi
            done
            return $_rb_rc
        fi
    fi
    command cat "$@"
}
# <<< rbterm-inline-media
function cat { _rbterm_cat "$@"; }
