# Bash settings file for non-login shells.
# shellcheck disable=SC1090,SC1091 shell=bash
#
# For more information, visit
# https://gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html.

# Private convenience functions.

# Path preview for Fzf file finder.
#
# Flags:
#   -d: Check if path is a directory.
_fzf_path_preview() {
  if [[ -d "${1}" ]]; then
    lsd --tree --depth 1 "${1}"
  else
    bat --color always --line-range :100 --style numbers "${1}"
  fi
}

# Paste pipe to system pager command into the commandline.
_paste_pager() {
  local program="${PAGER:-less}"
  local command=" |& ${program}"
  if [[ "${READLINE_LINE}" == *"${command}" ]]; then
    READLINE_LINE="${READLINE_LINE/%${command}/}"
  else
    READLINE_LINE="${READLINE_LINE}${command}"
  fi
}

# Prepend command line with system super user elevation command.
_paste_sudo() {
  local program point
  if [[ -x "$(command -v doas)" ]]; then
    program='doas'
  elif [ -x "$(command -v sudo)" ]; then
    program='sudo'
  else
    return
  fi

  if [[ "${READLINE_LINE}" == "${program} "* ]]; then
    point="${READLINE_POINT}"
    READLINE_LINE="${READLINE_LINE/#${program} /}"
    READLINE_POINT="$((point - 5))"
  else
    point="${READLINE_POINT}"
    READLINE_LINE="${program} ${READLINE_LINE}"
    READLINE_POINT="$((point + 5))"
  fi
}

# Public convenience functions.

# Override system implementation of command not found.
#
# Some system implementations will perform a long lookup to see if a package
# provides the command.
command_not_found_handle() {
  echo "Error: command '${1}' not found" >&2
}
export -f command_not_found_handle

# Open Bash history file with default editor.
edit-history() {
  local program="${EDITOR:-vi}"
  "${program}" "${HOME}/.bash_history"
}
export -f edit-history

# Complete commandline argument with interactive path search.
fzf-path-widget() {
  local line="${READLINE_LINE}" point
  fzf-file-widget
  if [[ "${line}" != "${READLINE_LINE}" ]]; then
    point="${READLINE_POINT}"
    READLINE_LINE="${READLINE_LINE:0:point-1}${READLINE_LINE:point}"
    READLINE_POINT="$((point - 1))"
  fi
}

# Prepend existing directories that are not in the system path.
#
# Flags:
#   -d: Check if path is a directory.
prepend-paths() {
  local folder inode
  for inode in "$@"; do
    if [[ -d "${inode}" ]]; then
      # Expand folder to its full path.
      folder="$(cd "${inode}" && pwd)"
      if [[ ! "${PATH}" =~ (^|:)${folder}(:|$) ]]; then
        export PATH="${folder}:${PATH}"
      fi
    fi
  done
}
export -f prepend-paths

# Source shell files if they exist.
#
# Flags:
#   -f: Check if file exists and is a regular file.
source-files() {
  local inode
  for inode in "$@"; do
    if [[ -f "${inode}" ]]; then
      source "${inode}"
    fi
  done
}
export -f source-files

# Check if current shell is within a remote SSH session.
#
# Since function returns an exit code, zero is true and nonzero is false.
#
# Flags:
#   -n: Check if string is nonempty.
ssh-session() {
  if [[ -n "${SSH_CLIENT}${SSH_CONNECTION}${SSH_TTY}" ]]; then
    return 0
  else
    return 1
  fi
}
export -f ssh-session

# Private convenience variables.
#
# Do not use long form flags for uname. They are not supported on some systems.
#
# Flags:
#   -s: Show operating system kernel name.
os="$(uname -s)"
tty="$([[ "$-" =~ 'i' ]] && echo 'true' || echo '')"

# System settings.

# Disable Bash history.
unset HISTFILE
# Set terminal environment variable if empty.
export TERM="${TERM:-xterm-256color}"

# Add directories to system path that are not always included.
#
# Homebrew ARM directories should appear in system path before AMD directories
# since some ARM systems might have slower emulated AMD copies of programs.
prepend-paths '/usr/sbin' '/usr/local/bin' '/opt/homebrew/sbin' \
  '/opt/homebrew/bin' "${HOME}/.local/bin"

# Alacritty settings.

# Autostart Zellij or connect to existing session if within Alacritty terminal
# and within an interactive shell for the login user. For more information,
# visit https://zellij.dev/documentation/integration.html.
#
# Flags:
#   -n: Check if string is nonempty.
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
#   -z: Check if the string is empty.
if [[ -n "${tty}" && "${TERM}" == 'alacritty' && -z "${TERM_PROGRAM}" ]]; then
  # Do not use logname command, since it sometimes incorrectly returns "root"
  # on MacOS. For more information, visit
  # https://github.com/vercel/hyper/issues/3762.
  if [[ -x "$(command -v zellij)" && -z "${ZELLIJ}" ]] && ! ssh-session &&
    [[ "${LOGNAME}" == "${USER}" ]]; then
    # Attach to a default session if it exists.
    export ZELLIJ_AUTO_ATTACH='true'
    # Exit the shell when Zellij exits.
    export ZELLIJ_AUTO_EXIT='true'
    SHELL="${BASH}" eval "$(zellij setup --generate-auto-start bash)"
  fi

  # Switch TERM variable to avoid "alacritty: unknown terminal type" errors
  # during remote connections.
  #
  # For more information, visit
  # https://github.com/alacritty/alacritty/issues/3962.
  export TERM='xterm-256color'
fi

# Bat settings.

# Set default pager to Bat.
#
# Flags:
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -x "$(command -v bat)" ]]; then
  export PAGER='bat'
fi

# Carapace settings.

# Load Carapace completions if interactive and available.
#
# Flags:
#   -n: Check if string is nonempty.
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -n "${tty}" && -x "$(command -v carapace)" ]]; then
  export CARAPACE_BRIDGES='fish,bash'
  export CARAPACE_MERGEFLAGS='1'
  eval "$(carapace _carapace bash)"
fi

# Clipboard settings.

# Add unified clipboard aliases.
#
# Command cbcopy is defined as a function instead of an alias to add logic for
# removing the final newline from text during clipboard copies.
#
# Flags:
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ "${os}" == 'Darwin' ]]; then
  cbcopy() {
    echo -n "$(cat)" | pbcopy
  }
  alias cbpaste='pbpaste'
elif [[ -x "$(command -v wl-copy)" ]]; then
  cbcopy() {
    echo -n "$(cat)" | wl-copy
  }
  alias cbpaste='wl-paste'
fi

# Docker settings.

# Ensure newer Docker features are enabled.
export COMPOSE_DOCKER_CLI_BUILD='true' DOCKER_BUILDKIT='true' \
  DOCKER_CLI_HINTS='false'

# Add LazyDocker convenience alias.
alias lzd='lazydocker'

# Fd settings.

# Always have Fd read available gitignore files.
alias fd='fd --no-require-git'
# Add edit alias for interactive Fd.
alias fde='fdi --edit'

# FFmpeg settings.

# Disable verbose FFmpeg banners.
alias ffmpeg='ffmpeg -hide_banner -stats -loglevel error'
alias ffplay='ffplay -hide_banner -loglevel error'
alias ffprobe='ffprobe -hide_banner'

# Fzf settings.

# Disable Fzf Alt-C command.
export FZF_ALT_C_COMMAND=''
# Set Fzf styles with solarized light theme based on
# https://github.com/tinted-theming/tinted-fzf/blob/main/fish/base16-solarized-light.fish.
export FZF_BASE_OPTS="--border --reverse --bind ctrl-d:backward-kill-word
--color bg:#fdf6e3,bg+:#eee8d5,fg:#657b83,fg+:#073642,header:#268bd2
--color hl:#268bd2,hl+:#268bd2,info:#b58900,marker:#2aa198
--color pointer:#2aa198,prompt:#b58900,spinner:#2aa198 --height ~80%"
export FZF_DEFAULT_OPTS="${FZF_BASE_OPTS} --with-shell 'bash -c'"

# Load Fzf if interactive and available.
#
# Flags:
#   -c: Run commands in Bash shell.
#   -f: Make name refer to a function.
#   -n: Check if string is nonempty.
#   -r: Remove keybinding.
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -n "${tty}" && -x "$(command -v fzf)" ]]; then
  eval "$(fzf --bash)"
  if [[ -x "$(command -v bat)" && -x "$(command -v lsd)" ]]; then
    export -f _fzf_path_preview
    export FZF_CTRL_T_OPTS="--preview '_fzf_path_preview {}'
--preview-window border-left"
  fi
  if [[ -x "$(command -v fd)" ]]; then
    export FZF_CTRL_T_COMMAND='fd --hidden --no-require-git'
  fi

  # Change Fzf file search keybinding to Ctrl+F.
  bind -r "\C-t"
  bind -r "\ec"
  bind -x '"\C-f": fzf-path-widget'
fi

# Helix settings.

# Set default editor to Helix if available.
#
# Flags:
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -x "$(command -v hx)" ]]; then
  export EDITOR='hx'
  export SUDO_EDITOR='hx'
fi

# Homebrew settings

# Avoid Homebrew hints after installing a package.
export HOMEBREW_NO_ENV_HINTS='true'

# Just settings.

# Add alias for account wide Just recipes.
alias jt='just --global-justfile'

# Kubernetes settings.

# Add Kubectl plugins to system path.
prepend-paths "${HOME}/.krew/bin"

# Lsd settings.

# Set solarized light color theme for several Unix tools.
#
# Uses output of command "vivid generate solarized-light" from
# https://github.com/sharkdp/vivid.
#
# Flags:
#   -f: Check if file exists and is a regular file.
if [[ -f "${HOME}/.ls_colors" ]]; then
  # shellcheck disable=SC2155
  export LS_COLORS="$(cat "${HOME}/.ls_colors")"
fi

# Replace Ls with Lsd if available.
#
# Flags:
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -x "$(command -v lsd)" ]]; then
  alias ls='lsd'
fi

# Miniserve settings.

# Serve index file if available.
export MINISERVE_INDEX='index.html'

# Python settings.

# Add Python debugger alias.
alias pdb='python3 -m pdb'

# Make Poetry create virtual environments inside projects.
export POETRY_VIRTUALENVS_IN_PROJECT='true'
# Disable Python history.
export PYTHON_HISTORY='/dev/null'
# Fix Poetry package install issue on headless systems.
export PYTHON_KEYRING_BACKEND='keyring.backends.fail.Keyring'

# Make numerical compute libraries findable for MacOS.
if [[ "${os}" == 'Darwin' ]]; then
  if [[ -d '/opt/homebrew' ]]; then
    export OPENBLAS='/opt/homebrew/opt/openblas'
  else
    export OPENBLAS='/usr/local/opt/openblas'
  fi
  prepend-paths "${OPENBLAS}"
fi

# Rclone settings.

# Make Rclone create empty intermediate folders.
export RCLONE_CREATE_EMPTY_SRC_DIRS='true'
# Make Rclone skip modification time updates.
export RCLONE_NO_UPDATE_DIR_MODTIME='true'
export RCLONE_NO_UPDATE_MODTIME='true'
# Make Rclone show progress bars.
export RCLONE_PROGRESS='true'
export RCLONE_STATS_ONE_LINE='true'

# Ripgrep settings.

# Add edit alias for interactive Ripgrep.
alias rge='rgi --edit'
# Set Ripgrep settings file location.
export RIPGREP_CONFIG_PATH="${HOME}/.ripgreprc"

# Rust settings.

# Add Rust debugger aliases.
alias rgd='rust-gdb --quiet'
alias rld='rust-lldb --source-quietly'

# Add Rust binaries to system path.
prepend-paths "${HOME}/.cargo/bin"

# Shell settings.

# Add alias for make directory with parents.
alias mkdir='mkdir -p'
# Add alias for remove by force.
alias rmf='rm -fr'
# Add alias for Rsync with progress bars and ignored files.
alias rsync='rsync --partial --progress --filter ":- .gitignore"'
# Disable MacOS default shell is now Zsh message. Value must be 1.
export BASH_SILENCE_DEPRECATION_WARNING='1'
# Prevent `Ctrl+D` keybinding from closing the shell on an empty line.
export IGNOREEOF='-1'

# Configure completions and keybindings if interactive.
#
# Custom Bash keybindings are loaded from ~/.inputrc with readline.
#
# Flags:
#   -d: Check if path is a directory.
#   -n: Check if string is nonempty.
if [[ -n "${tty}" ]]; then
  # Load Bash completion.
  if [[ "${os}" == 'Darwin' ]]; then
    if [[ -d '/opt/homebrew' ]]; then
      source-files "/opt/homebrew/etc/profile.d/bash_completion.sh"
    else
      source-files "/usr/local/etc/profile.d/bash_completion.sh"
    fi
  elif [[ "${os}" == 'FreeBSD' ]]; then
    source-files '/usr/local/share/bash-completion/bash_completion'
  else
    source-files '/usr/share/bash-completion/bash_completion'
  fi

  # Add common Fish keybindings to Bash.
  bind -x '"\ep": _paste_pager'
  bind -x '"\es": _paste_sudo'
fi

# Starship settings.

# Disable Starship warnings about command timeouts.
export STARSHIP_LOG='error'

# Initialize Starship if interactive and available.
#
# Flags:
#   -n: Check if string is nonempty.
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -n "${tty}" ]]; then
  if [[ -x "$(command -v starship)" ]]; then
    eval "$(starship init bash)"
  else
    PS1="\n\u at \h in \w\n❯ "
  fi
fi

# TypeScript settings.

# Disable Deno update messages.
export DENO_NO_UPDATE_CHECK='true'
# Add Deno binaries to system path.
prepend-paths "${HOME}/.deno/bin"

# Add NPM global binaries to system path.
prepend-paths "${HOME}/.npm/global/bin"

# Add PNPM binaries to system path.
if [[ "${os}" == 'Darwin' ]]; then
  export PNPM_HOME="${HOME}/Library/pnpm"
else
  export PNPM_HOME="${HOME}/.local/share/pnpm"
fi
prepend-paths "${PNPM_HOME}"

# Visual Studio Code settings.

# Add Visual Studio Code binaries to system path.
prepend-paths '/usr/share/code/bin' \
  '/Applications/Visual Studio Code.app/Contents/Resources/app/bin'

# Yazi settings.

# Disable Yazi Zoxide plugin directory preview window.
export YAZI_ZOXIDE_OPTS="${FZF_BASE_OPTS} --preview-window hidden"

# Yazi wrapper to change directory on program exit.
#
# Flags:
#   -n: Check if string is nonempty.
yz() {
  local cwd='' tmp=''
  tmp="$(mktemp)"
  yazi --cwd-file "${tmp}" "$@"
  cwd="$(cat "${tmp}")"
  if [[ -n "${cwd}" && "${cwd}" != "${PWD}" ]]; then
    # shellcheck disable=SC2164
    cd "${cwd}"
  fi
  rm "${tmp}"
}

# Zoxide settings.

# Disable Zoxide directory preview window.
export _ZO_FZF_OPTS="${FZF_BASE_OPTS} --preview-window hidden"

# Initialize Zoxide if interactive and available.
#
# Flags:
#   -n: Check if string is nonempty.
#   -v: Only show file path of command.
#   -x: Check if file exists and execute permission is granted.
if [[ -n "${tty}" && -x "$(command -v zoxide)" ]]; then
  eval "$(zoxide init --cmd cd bash)"
fi

# Remove private convenience variables.

unset os
unset tty

# User settings.

# Load user aliases, secrets, and variables.
source-files "${HOME}/.env" "${HOME}/.secrets" "${HOME}/.env.bash" \
  "${HOME}/.secrets.bash"
