# Basic
mkdir -p ~/.vim/{undo,swap,backup} 2>/dev/null
export LANG=en_US.UTF-8
export EDITOR=vim
export HISTFILE=~/.zhistory
export HISTSIZE=100000
export SAVEHIST=100000

# History behavior
setopt EXTENDED_HISTORY INC_APPEND_HISTORY SHARE_HISTORY
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_VERIFY

# General quality-of-life
setopt EXTENDED_GLOB
setopt INTERACTIVE_COMMENTS
setopt MULTIOS              # pipe to multiple outputs
setopt AUTO_PUSHD           # cd becomes pushd
setopt AUTO_NAME_DIRS       # use named dirs when possible
setopt GLOB_COMPLETE        # expand globs
setopt ZLE
setopt NO_FLOW_CONTROL      # disable Ctrl-S
setopt CLOBBER              # allow > to overwrite
setopt NO_CASE_GLOB         # case insensitive globbing
setopt NUMERIC_GLOB_SORT
setopt RC_EXPAND_PARAM      # better array expansion

unsetopt AUTO_CD            # don't run directories as commands
unsetopt CORRECT            # no "did you mean?"
unsetopt BANG_HIST          # no ! history expansion

# modules
zmodload zsh/datetime
zmodload zsh/mathfunc
zmodload zsh/stat

# plugin manager
ZINIT_HOME="${XDG_DATA_HOME:-${HOME}}/.zinit.git"
[ ! -d "$ZINIT_HOME" ] && mkdir -p "$(dirname $ZINIT_HOME)"
[ ! -d "$ZINIT_HOME/.git" ] && git clone https://github.com/zdharma-continuum/zinit.git "$ZINIT_HOME"
source "${ZINIT_HOME}/zinit.zsh"
functions[@zinit-scheduler]="setopt localoptions; set +u; ${functions[@zinit-scheduler]}"

zinit light zsh-users/zsh-autosuggestions
zinit light zsh-users/zsh-syntax-highlighting
zinit light zsh-users/zsh-completions

# completions
autoload -Uz compinit
_comp_files=(${ZDOTDIR:-$HOME}/.zcompdump(Nm-20))
if (( $#_comp_files )); then
  compinit -i -C
else
  compinit -i
fi
unset _comp_files

zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'

# key definitions (terminfo + common codes)
typeset -gA key
key[Home]="${terminfo[khome]}"
key[End]="${terminfo[kend]}"
key[Delete]="${terminfo[kdch1]}"
key[Up]="${terminfo[kcuu1]}"
key[Down]="${terminfo[kcud1]}"
key[ShiftTab]="${terminfo[kcbt]}"
key[CtrlLeft]="^[[1;5D"
key[CtrlRight]="^[[1;5C"

# keybindings
bindkey -e
[[ -n ${key[Home]}     ]] && bindkey ${key[Home]}     beginning-of-line
[[ -n ${key[End]}      ]] && bindkey ${key[End]}      end-of-line
[[ -n ${key[Delete]}   ]] && bindkey ${key[Delete]}   delete-char
[[ -n ${key[ShiftTab]} ]] && bindkey ${key[ShiftTab]} reverse-menu-complete
[[ -n ${key[CtrlLeft]} ]] && bindkey ${key[CtrlLeft]} backward-word
[[ -n ${key[CtrlRight]} ]] && bindkey ${key[CtrlRight]} forward-word
bindkey '^[.' insert-last-word    # Esc,. (last arg of prev cmd)
bindkey '^[b' backward-word       # Alt+B
bindkey '^[f' forward-word        # Alt+F
bindkey '^[^?' backward-kill-word # Alt+Backspace
bindkey '^Q' push-line            # Ctrl+Q (stash line, type another, comes back)
autoload -Uz edit-command-line && zle -N edit-command-line
bindkey '^X^E' edit-command-line  # Ctrl+X,E → edit in $EDITOR
bindkey '^[e' edit-command-line   # Alt+E → edit in $EDITOR

# search from prefix
zinit light zsh-users/zsh-history-substring-search
[[ -n ${key[Up]}   ]] && bindkey ${key[Up]}   history-substring-search-up
[[ -n ${key[Down]} ]] && bindkey ${key[Down]} history-substring-search-down
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down

ZLE_RPROMPT_INDENT=0
# starship prompt
export STARSHIP_CONFIG=~/.starship.toml
zinit ice as"command" from"gh-r" \
          atclone"./starship init zsh > init.zsh; ./starship completions zsh > _starship" \
          atpull"%atclone" src"init.zsh"
zinit light starship/starship

# async dotfiles pull (once/day)
{
  sleep 1
  local marker=~/.cache/.dotfiles-pull
  [[ -f $marker && $((EPOCHSECONDS - $(zstat +mtime $marker))) -lt 86400 ]] && return
  mkdir -p ~/.cache && touch $marker
  git --git-dir=$HOME/.dotgit/ --work-tree=$HOME pull --ff-only -q 2>/dev/null
} &!

# paths live in ~/.zshenv so non-interactive shells (cron, ssh cmd, GUI) see them

# fzf (if available)
if (( $+commands[fzf] )); then
  source <(fzf --zsh 2>/dev/null) || {
    # fallback for older fzf versions
    [[ -f ~/.fzf.zsh ]] && source ~/.fzf.zsh
    [[ -f /usr/share/fzf/key-bindings.zsh ]] && source /usr/share/fzf/key-bindings.zsh
    [[ -f /usr/share/fzf/completion.zsh ]] && source /usr/share/fzf/completion.zsh
  }
  export FZF_DEFAULT_OPTS='--height 40% --reverse'
fi

# ls defaults
if (( $+commands[gls] )); then
  [[ -z "$LS_COLORS" ]] && eval "$(gdircolors --sh)"
  alias ls='gls --group-directories-first --color=auto'
elif (( $+commands[dircolors] )); then
  [[ -z "$LS_COLORS" ]] && eval "$(dircolors --sh)"
  alias ls='ls --group-directories-first --color=auto'
else
  export CLICOLOR=1
  export LSCOLORS=ExGxFxdaCxDaDahbadacec
  alias ls='ls -GF'
fi

# aliases: dotfiles (bare repo)
alias .g='git --git-dir=$HOME/.dotgit/ --work-tree=$HOME'

# aliases: tmux
alias t="tmux new-session -A -s auto"
alias tmuxr="tmux new-session -A -s auto"       # attach-or-create "auto"
alias tmuxn="tmux new-session -A -s auto \; new-window" # same + new window
alias tmuxa="tmux new-session -A -s"            # attach-or-create named

# yazi wrapper (cd to dir on exit)
function y() {
  local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd
  command yazi "$@" --cwd-file="$tmp"
  IFS= read -r -d '' cwd < "$tmp"
  [[ "$cwd" != "$PWD" ]] && [[ -d "$cwd" ]] && builtin cd -- "$cwd"
  rm -f -- "$tmp"
}

# functions
function pj {
  local dir roots=()
  for d in /projects ~/projects; do
    [[ -d $d ]] && roots+=(${d:A})
  done
  (( ${#roots} )) || { echo "no project dirs found"; return 1 }
  dir=$(find -L ${(u)roots} -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | awk '{printf "%3d %s\n", NR, $0}' | fzf --reverse --prompt="project> " --query="$1" --select-1 --exit-0 --with-nth=2.. | sed 's/^ *[0-9]* //') && cd "$dir"
}

function clip {
  echo -ne "\033]52;c;$(base64 | tr -d '\n')\a"
}

function mkcd {
  : "${1:?where?}"
  [[ -e $1 ]] && { echo "file exists"; return 1 }
  mkdir -p "$1" && cd "$1"
}

function cdtmp {
  local dir="${HOME}/tmp/auto_$(date +%Y_%m_%d_%H_%M_%S)"
  [[ -n "$1" ]] && dir="${dir}_$1"
  mkcd "$dir"
}

# aliases: docker compose
alias ,dcdu='docker compose --profile dev up service_dev --force-recreate --detach'
alias ,dcdd='docker compose --profile dev down --remove-orphans'
alias ,dcdl='docker compose --profile dev logs -f service_dev'
alias dcu='docker compose up --force-recreate --detach'
alias dcd='docker compose down --remove-orphans'
alias dcl='docker compose logs -f'

# aliases: rsync with sane defaults
alias rsync2='rsync --info=PROGRESS2 --progress --recursive --compress --human-readable --update --times --no-whole-file --itemize-changes --stats --verbose --links --no-perms --chmod=ugo=rwX'

# aliases: misc
alias c="claude"
alias ,cd="IS_SANDBOX=1 claude --dangerously-skip-permissions"
alias wm="workmux"
alias zshreload="exec zsh"
alias -g LATEST='*(om[1])'            # glob: most recently modified
alias FUNCTION_PRELUDE="setopt LOCAL_OPTIONS PIPE_FAIL XTRACE ERR_RETURN"

# machine-local overrides (not vcs tracked)
export IS_SANDBOX=1

[[ -f ~/.zshrc-extra ]] && source ~/.zshrc-extra || touch ~/.zshrc-extra
