# Otty zsh integration — payload (sourced from ~/.zshrc managed block)
#
# Idempotent: re-sourcing in the same shell is a no-op. `__otty_loaded` is a
# plain (non-exported) parameter, so each fresh shell process re-installs the
# hooks while a second source within one shell is skipped.
[[ -n "$__otty_loaded" ]] && return
__otty_loaded=1

# ── Bundled `otty` on PATH ──
# Otty ships a CLI binary inside the .app bundle. If "Install CLI" has not
# been run (no /usr/local/bin/otty symlink), point shells at our per-process
# bin dir so `otty …` still works without polluting the user's system. Done
# at the end of .zshrc so we win any PATH-setting they do; the case guard
# avoids appending the same dir twice across nested shells.
if [[ -n "$OTTY_BIN_DIR" && -d "$OTTY_BIN_DIR" ]]; then
    case ":$PATH:" in
        *":$OTTY_BIN_DIR:"*) ;;
        *) export PATH="$OTTY_BIN_DIR:$PATH" ;;
    esac
fi

# ── OSC 133 FTCS markers ──
__otty_cmd_executed=""
__otty_progress_active=""

# Match command line against $OTTY_PROGRESS_COMMANDS (pipe-separated list of
# whitespace-delimited prefixes, e.g. "git push").
__otty_progress_match() {
    emulate -L zsh
    setopt sh_word_split
    local cmd="$1"
    [[ -z "$OTTY_PROGRESS_COMMANDS" ]] && return 1
    # Strip leading `NAME=value` env assignments (must be followed by a space).
    while [[ "$cmd" == [A-Za-z_]*=*' '* ]]; do
        local first="${cmd%% *}"
        [[ "$first" == *=* ]] || break
        cmd="${cmd#* }"
    done
    local IFS='|'
    local p
    for p in $OTTY_PROGRESS_COMMANDS; do
        [[ -z "$p" ]] && continue
        if [[ "$cmd" == "$p" || "$cmd" == "$p "* ]]; then
            return 0
        fi
    done
    return 1
}

__otty_precmd() {
    local ret=$?
    if [[ -n "$__otty_cmd_executed" ]]; then
        builtin printf '\e]133;D;%d\a' "$ret"
    fi
    if [[ -n "$__otty_progress_active" ]]; then
        if (( ret == 0 )); then
            builtin printf '\e]9;4;5;0\a'
        else
            builtin printf '\e]9;4;2\a'
        fi
        __otty_progress_active=""
    fi
    builtin printf '\e]133;A\a'
    __otty_cmd_executed=""
}
__otty_preexec() {
    builtin printf '\e]133;C\a'
    __otty_cmd_executed=1
    if __otty_progress_match "$1"; then
        builtin printf '\e]9;4;3\a'
        __otty_progress_active=1
    fi
}
precmd_functions+=(__otty_precmd)
preexec_functions+=(__otty_preexec)

# ── OSC 7 CWD reporting (fires on every directory change) ──
__otty_chpwd() {
    builtin printf '\e]7;file://%s%s\a' "${HOST}" "${PWD}"
}
chpwd_functions+=(__otty_chpwd)
# Emit initial CWD so the sidebar knows the starting directory.
__otty_chpwd

# ── Omit `otty` prefix (live-toggleable) ──
# Install `edit`/`view`/`watch`/`jump`/`learn` wrappers ONCE; each one stat()s the
# omit_prefix sentinel under $OTTY_FLAGS_DIR at call time so the Settings
# toggle takes effect immediately, without waiting for the next precmd
# cycle. Sentinel absent → fall through to `command <name>` so PATH lookup
# behaves exactly as if the wrapper weren't there (system `watch` on Linux
# still works). By default, names already defined as user functions/aliases
# are left untouched; the `allow_overwrite` sentinel (evaluated at shell
# init — restart the shell for the toggle to take effect) opts into
# replacing them.
__otty_should_wrap() {
    if [[ -n "$OTTY_FLAGS_DIR" && -f "$OTTY_FLAGS_DIR/allow_overwrite" ]]; then
        # zsh resolves aliases BEFORE function lookup at parse time, so a
        # leftover alias would shadow the function we are about to install.
        # Drop it here so our wrapper actually wins.
        (( ${+aliases[$1]} )) && unalias "$1"
        return 0
    fi
    (( ${+functions[$1]} || ${+aliases[$1]} )) && return 1
    return 0
}
__otty_should_wrap edit && edit() {
    if [[ -n "$OTTY_FLAGS_DIR" && -f "$OTTY_FLAGS_DIR/omit_prefix" ]]; then
        command otty edit "$@"
    else
        command edit "$@"
    fi
}
__otty_should_wrap view && view() {
    if [[ -n "$OTTY_FLAGS_DIR" && -f "$OTTY_FLAGS_DIR/omit_prefix" ]]; then
        command otty view "$@"
    else
        command view "$@"
    fi
}
__otty_should_wrap watch && watch() {
    if [[ -n "$OTTY_FLAGS_DIR" && -f "$OTTY_FLAGS_DIR/omit_prefix" ]]; then
        command otty watch "$@"
    else
        command watch "$@"
    fi
}
__otty_should_wrap jump && jump() {
    if [[ -n "$OTTY_FLAGS_DIR" && -f "$OTTY_FLAGS_DIR/omit_prefix" ]]; then
        # Zoxide-style: ask the app to resolve only (no `cd` injection into
        # the focused pane) and `builtin cd` locally — keeps the transcript
        # silent (no echoed `cd '<path>'` from line-discipline, no duplicate
        # prompt after CLI exits).
        if (( $# == 0 )); then
            # No-args: toggle between $HOME and the last jump-source dir.
            # Local-only state ($OTTY_JUMP_PREV) so up-arrow still recalls
            # the user's literal `jump` invocation, not an injected `cd`.
            if [[ "$PWD" == "$HOME" && -n "$OTTY_JUMP_PREV" ]]; then
                local _otty_dest="$OTTY_JUMP_PREV"
                OTTY_JUMP_PREV="$PWD"
                builtin cd -- "$_otty_dest"
            else
                OTTY_JUMP_PREV="$PWD"
                builtin cd -- "$HOME"
            fi
            return
        fi
        local _otty_p
        _otty_p=$(command otty jump --no-cd "$@") || return
        if [[ -n "$_otty_p" ]]; then
            OTTY_JUMP_PREV="$PWD"
            builtin cd -- "$_otty_p"
        fi
    else
        command jump "$@"
    fi
}
__otty_should_wrap learn && learn() {
    if [[ -n "$OTTY_FLAGS_DIR" && -f "$OTTY_FLAGS_DIR/omit_prefix" ]]; then
        command otty learn "$@"
    else
        command learn "$@"
    fi
}
# `watch:<agent>` — wait for a Claude/Codex/OpenCode session to reach
# idle. The colon is part of the command name as far as the shell is
# concerned, so the bare `watch` wrapper above never catches it; install
# one wrapper per agent. Keep in sync with WATCH_AGENTS in cli/src/main.rs.
for __otty_a in claude codex opencode; do
    __otty_should_wrap "watch:$__otty_a" && eval "watch:$__otty_a() {
        if [[ -n \"\$OTTY_FLAGS_DIR\" && -f \"\$OTTY_FLAGS_DIR/omit_prefix\" ]]; then
            command otty watch:$__otty_a \"\$@\"
        else
            command watch:$__otty_a \"\$@\"
        fi
    }"
done
unset __otty_a

# ── Custom CLI aliases ($OTTY_CLI_ALIASES = "name=cmd|name=cmd|…") ──
# Define `name() { command otty <cmd> "$@" }` for each pair the bridge passed
# in. The `allow_overwrite` sentinel still gates whether we replace names the
# user has already defined.
if [[ -n "$OTTY_CLI_ALIASES" ]]; then
    () {
        emulate -L zsh
        setopt sh_word_split
        local IFS='|'
        local pair name cmd
        for pair in $OTTY_CLI_ALIASES; do
            [[ -z "$pair" ]] && continue
            name="${pair%%=*}"
            cmd="${pair#*=}"
            [[ -z "$name" || -z "$cmd" || "$name" == "$pair" ]] && continue
            # Validate identifier and command. `=~` uses POSIX ERE in both
            # bash and zsh and is unaffected by KSH_GLOB / EXTENDED_GLOB,
            # both of which `emulate -L zsh` turns off — using `==` with a
            # ksh-style `*(...)` pattern here would silently reject every
            # alias under emulate.
            [[ "$name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
            [[ "$cmd"  =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
            __otty_should_wrap "$name" || continue
            # When the alias maps to a wrapped command (jump/edit/view/watch/
            # learn), delegate to that wrapper so its `--no-cd`/builtin-cd
            # logic still runs. Calling `command otty $cmd` directly would
            # bypass the wrapper and re-introduce the visible `cd '<path>'`
            # echo + spurious shell-history entry that the wrapper exists
            # to suppress.
            case "$cmd" in
                jump|edit|view|watch|learn)
                    functions[$name]="$cmd \"\$@\""
                    ;;
                *)
                    functions[$name]="command otty $cmd \"\$@\""
                    ;;
            esac
        done
    }
fi

unfunction __otty_should_wrap

# Dump aliases + the resolved interactive $PATH to temp files so the
# autocomplete engine can read them without spawning a shell. The app
# process only inherits the bare launchd PATH (/usr/bin:/bin:…) when
# launched from Finder, so command-name completion relies on this dump to
# discover Homebrew/cargo/mise/etc. binaries on the user's real PATH.
__otty_dump_aliases() {
    builtin alias > "${TMPDIR:-/tmp}/otty-aliases-${EUID}" 2>/dev/null
    print -rn -- "$PATH" > "${TMPDIR:-/tmp}/otty-path-${EUID}" 2>/dev/null
    precmd_functions=("${(@)precmd_functions:#__otty_dump_aliases}")
}
precmd_functions+=(__otty_dump_aliases)

# Emit B mark (command-input-start) after each prompt via zle.
if [[ -o interactive ]] 2>/dev/null; then
    # Capture the existing widget's backing function (if any),
    # but skip if it's already ours (avoid infinite recursion).
    __otty_orig_line_init_fn=""
    if [[ "${widgets[zle-line-init]}" == user:* ]]; then
        local orig_fn="${widgets[zle-line-init]#user:}"
        if [[ "$orig_fn" != __otty_zle_line_init ]]; then
            __otty_orig_line_init_fn="$orig_fn"
        fi
    fi

    __otty_zle_line_init() {
        builtin printf '\e]133;B\a'
        # Call the original function directly (not via zle) to
        # avoid re-entering our own widget.
        if [[ -n "$__otty_orig_line_init_fn" ]] && (( ${+functions[$__otty_orig_line_init_fn]} )); then
            "$__otty_orig_line_init_fn"
        fi
    }
    builtin zle -N zle-line-init __otty_zle_line_init
fi

# ── SSH Integration ──
if [[ "${OTTY_SSH_INTEGRATION:-}" == "1" ]]; then
    ssh() {
        local _otty_term="xterm-256color"
        local -a _otty_opts=(
            -o "SetEnv=COLORTERM=truecolor TERM_PROGRAM=otty"
            -o "SendEnv=COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION"
        )

        # ── Resolve hostname via ssh -G ──
        local _otty_user="" _otty_hostname=""
        while IFS=' ' read -r _k _v; do
            case "$_k" in
                user) _otty_user="$_v" ;;
                hostname) _otty_hostname="$_v" ;;
            esac
            [[ -n "$_otty_user" && -n "$_otty_hostname" ]] && break
        done < <(command ssh -G "$@" 2>/dev/null)

        local _otty_target="${_otty_user}@${_otty_hostname}"
        local _otty_meta_file="${TMPDIR:-/tmp}/otty-ssh-shell-$$.meta"
        local _otty_ctl_path=""

        if [[ -n "$_otty_hostname" ]]; then
            # Always set up a ControlMaster socket so the main connection
            # can be reused for SFTP/exec by the file/git panels.
            # ControlPath must stay short: macOS caps Unix-domain socket
            # paths at 104 bytes and $TMPDIR (/var/folders/...) already eats
            # ~49 of them, so the socket goes directly under /tmp instead.
            # mktemp -d yields a fresh 0700 dir, keeping this race-free.
            local _otty_ctl_dir
            _otty_ctl_dir="$(mktemp -d /tmp/otty-ssh.XXXXXX 2>/dev/null)" || _otty_ctl_dir="/tmp/otty-ssh.$$"
            _otty_ctl_path="$_otty_ctl_dir/socket"
            _otty_opts+=(
                -o "ControlMaster=auto"
                -o "ControlPath=$_otty_ctl_path"
                -o "ControlPersist=60"
            )

            # Terminfo install (cached per user@host)
            local _otty_cache_dir="${TMPDIR:-/tmp}/otty-terminfo-cache"
            command mkdir -p "$_otty_cache_dir" 2>/dev/null
            local _otty_cache_file="$_otty_cache_dir/$_otty_target"

            if [[ -f "$_otty_cache_file" ]]; then
                _otty_term="${TERM:-xterm-256color}"
            elif command -v infocmp >/dev/null 2>&1; then
                local _otty_terminfo
                _otty_terminfo="$(infocmp -0 -x 2>/dev/null)"

                if [[ -n "$_otty_terminfo" ]]; then
                    if builtin printf '%s' "$_otty_terminfo" | command ssh \
                        -o "ControlMaster=yes" \
                        -o "ControlPath=$_otty_ctl_path" \
                        -o "ControlPersist=60" \
                        "$@" \
                        'command -v tic >/dev/null 2>&1 || exit 1; mkdir -p ~/.terminfo 2>/dev/null; tic -x - 2>/dev/null && exit 0; exit 1' \
                        2>/dev/null; then
                        _otty_term="${TERM:-xterm-256color}"
                        command touch "$_otty_cache_file"
                    fi
                fi
            fi

            # Write metadata file so Otty's Swift side can find the resolved
            # session info (user, hostname, socket) even when ~/.ssh/config
            # aliases the user or hostname.
            {
                builtin print "user=${_otty_user}"
                builtin print "hostname=${_otty_hostname}"
                builtin print "socket=${_otty_ctl_path}"
            } > "$_otty_meta_file" 2>/dev/null
        fi

        # Notify Otty we're entering an SSH session.
        builtin printf '\e]7;ssh://%s\a' "${_otty_hostname:-unknown}"

        TERM="$_otty_term" command ssh "${_otty_opts[@]}" "$@"
        local _ret=$?

        command rm -f "$_otty_meta_file" 2>/dev/null

        # Restore local CWD reporting.
        builtin printf '\e]7;file://%s%s\a' "${HOST}" "${PWD}"
        return $_ret
    }
fi
