# ==============================================================================
# .zshrc - Zsh 配置文件
# 环境: macOS (Intel/Apple Silicon) + Ghostty + Zsh
# 最后更新: 2026-04-05
# ==============================================================================

# 性能分析工具(调试时取消注释)
# zmodload zsh/zprof

# ------------------------------------------------------------------------------
# 1. 基础环境变量
# ------------------------------------------------------------------------------

# [优化] 用 brew --prefix 检测是目前最可靠的方式,避免路径硬编码失效。
# 但 brew 命令本身有启动开销,因此先尝试常见路径快速判断,仅在失败时 fallback。
if [[ -x /opt/homebrew/bin/brew ]]; then
  export HOMEBREW_PREFIX="/opt/homebrew"       # Apple Silicon
elif [[ -x /usr/local/bin/brew ]]; then
  export HOMEBREW_PREFIX="/usr/local"          # Intel Mac
else
  export HOMEBREW_PREFIX="$(brew --prefix 2>/dev/null || echo /usr/local)"
fi

export EDITOR='code'
export VISUAL='code'
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export COLORTERM=truecolor

# 包管理器 home(集中声明,后续 PATH 引用)
export PNPM_HOME="$HOME/Library/pnpm"
export BUN_INSTALL="$HOME/.bun"
export SDKMAN_DIR="$HOME/.sdkman"

# ------------------------------------------------------------------------------
# 2. PATH 配置(集中管理,避免分散追加)
# ------------------------------------------------------------------------------
# [修复] 原文件末尾有 antigravity / bun / codemaker 三处零散 PATH 追加,
# 已统一移入此处。typeset -U 自动去重,防止 PATH 膨胀。

typeset -U path
path=(
  "$HOME/.local/bin"
  "$HOME/bin"
  "$HOME/.antigravity/antigravity/bin"         # Antigravity
  "$HOME/.codemaker/bin"                       # CodeMaker
  "$BUN_INSTALL/bin"                           # Bun
  "$PNPM_HOME"                                 # pnpm 全局包
  "$HOMEBREW_PREFIX/bin"
  "$HOMEBREW_PREFIX/sbin"
  $path
)
export PATH

# ------------------------------------------------------------------------------
# 3. 补全系统
# ------------------------------------------------------------------------------
if [[ -d "$HOMEBREW_PREFIX/share/zsh/site-functions" ]]; then
  FPATH="$HOMEBREW_PREFIX/share/zsh/site-functions:$FPATH"
fi

autoload -Uz compinit

# [说明] (N) = 空值安全; .mh+24 = 文件修改时间超过 24 小时
# 超过 24h 才重新生成缓存,其余时间用 -C 跳过安全检查加速启动
local zcompdump="$HOME/.zcompdump"
if [[ ! -f "$zcompdump" ]] || [[ -n "$zcompdump"(#qnmh24-|qN.mh+24) ]]; then
  compinit
else
  compinit -C
fi

zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
zstyle ':completion:*:descriptions' format '%B%d%b'
zstyle ':completion:*:warnings' format 'No matches for: %d'

# ------------------------------------------------------------------------------
# 4. 历史记录
# ------------------------------------------------------------------------------
HISTFILE=~/.zsh_history
HISTSIZE=50000
SAVEHIST=50000

setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_REDUCE_BLANKS
setopt SHARE_HISTORY
setopt EXTENDED_HISTORY
setopt HIST_VERIFY

# ------------------------------------------------------------------------------
# 5. Shell 选项
# ------------------------------------------------------------------------------
setopt AUTO_CD
setopt AUTO_PUSHD
setopt PUSHD_IGNORE_DUPS
setopt CORRECT
setopt INTERACTIVE_COMMENTS
setopt GLOB_DOTS
# [删除] NO_BEEP 在现代 macOS 上由系统设置控制,此处无效,已移除。

# ------------------------------------------------------------------------------
# 6. SSH Agent(异步启动,不阻塞 shell 加载)
# ------------------------------------------------------------------------------
# [优化] 改为后台异步执行(&!),避免 keychain 访问阻塞启动。
# 权衡:极少情况下首次 git push 时 agent 可能还未就绪,重试一次即可。
ssh_agent_start() {
  local ssh_env_file="${XDG_RUNTIME_DIR:-$HOME/.cache}/ssh-agent.env"

  ssh-add -l >/dev/null 2>&1
  local agent_status=$?

  if [[ $agent_status -eq 0 ]]; then
    return 0
  fi

  if [[ $agent_status -eq 2 ]]; then
    if [[ -f "$ssh_env_file" ]]; then
      source "$ssh_env_file" >/dev/null
      ssh-add -l >/dev/null 2>&1
      agent_status=$?
    fi

    if [[ $agent_status -eq 2 ]]; then
      mkdir -p "$(dirname "$ssh_env_file")"
      ssh-agent -t 8h > "$ssh_env_file"
      source "$ssh_env_file" >/dev/null
    fi
  fi

  if [[ "$OSTYPE" == "darwin"* ]]; then
    ssh-add --apple-load-keychain 2>/dev/null || ssh-add -A 2>/dev/null
  fi

  local keys=(~/.ssh/{id_ed25519,id_netease})
  for key in "${keys[@]}"; do
    [[ -f "$key" ]] || continue
    if [[ "$OSTYPE" == "darwin"* ]]; then
      ssh-add --apple-use-keychain "$key" 2>/dev/null || ssh-add "$key" 2>/dev/null
    else
      ssh-add "$key" 2>/dev/null
    fi
  done
}

# [优化] &! 表示在后台异步运行,且不受 shell 退出影响(disowned)
ssh_agent_start &!

# ------------------------------------------------------------------------------
# 7. 开发工具(延迟加载)
# ------------------------------------------------------------------------------

# pyenv
if [[ -d "$HOME/.pyenv" ]]; then
  pyenv() {
    unset -f pyenv
    export PYENV_ROOT="$HOME/.pyenv"
    path=("$PYENV_ROOT/bin" $path)
    eval "$(command pyenv init - zsh)"
    eval "$(pyenv virtualenv-init -)" 2>/dev/null
    pyenv "$@"
  }
fi

# fnm - Node.js 版本管理
# [说明] --use-on-cd 会在每次 cd 时检查 .nvmrc/.node-version,按需切换版本。
if command -v fnm &>/dev/null; then
  eval "$(fnm env --use-on-cd)"
fi

# SDKMAN - Java 版本管理(延迟加载)
if [[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ]]; then
  # [修复] 加 -L 跟随符号链接,并检查目录是否存在再 export,避免空值
  if [[ -d "$SDKMAN_DIR/candidates/java/current" ]]; then
    export JAVA_HOME="$SDKMAN_DIR/candidates/java/current"
    # [优化] 同步更新 path 数组,确保 java/javac 命令可用
    path=("$JAVA_HOME/bin" $path)
  fi

  sdk() {
    unset -f sdk
    source "$SDKMAN_DIR/bin/sdkman-init.sh"
    sdk "$@"
  }
fi

# Bun 补全(按需加载)
[[ -s "$BUN_INSTALL/_bun" ]] && source "$BUN_INSTALL/_bun"

# ------------------------------------------------------------------------------
# 8. 别名
# ------------------------------------------------------------------------------

# 文件操作
alias ll='ls -lah'
alias la='ls -A'
alias l='ls -CF'
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# 目录导航
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'

# [修复] alias -- -= 兼容性不稳定,用函数替代更健壮
cdprev() { cd - }
alias -- -='cdprev'

# Git
alias g='git'
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gcm='git commit -m'
alias gca='git commit --amend'
alias gp='git push'
alias gl='git pull'
alias gd='git diff'
alias gdc='git diff --cached'
alias gco='git checkout'
alias gcb='git checkout -b'
alias gb='git branch'
alias gbd='git branch -d'
alias glog='git log --oneline --graph --decorate -15'
alias gloga='git log --oneline --graph --decorate --all -20'
alias gst='git stash'
alias gstp='git stash pop'
alias grb='git rebase'
alias grbi='git rebase -i'
alias gcp='git cherry-pick'

# 实用工具
alias cls='clear'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
alias df='df -h'
alias du='du -h'
alias reload='source ~/.zshrc && echo "✓ .zshrc reloaded"'
alias path='echo $PATH | tr ":" "\n" | nl'
alias serve='python3 -m http.server'
alias ports='lsof -i -P | grep LISTEN'

# macOS 特定
alias showfiles='defaults write com.apple.finder AppleShowAllFiles YES; killall Finder'
alias hidefiles='defaults write com.apple.finder AppleShowAllFiles NO; killall Finder'
alias cleanup='find . -type f -name "*.DS_Store" -ls -delete'

# ------------------------------------------------------------------------------
# 9. 自定义函数
# ------------------------------------------------------------------------------

mkcd() { mkdir -p "$1" && cd "$1" }

ff() { find . -type f -name "*$1*" 2>/dev/null }

showpath() { echo "$PATH" | tr ':' '\n' | nl }

extract() {
  if [[ ! -f "$1" ]]; then
    echo "'$1' 不是有效文件"
    return 1
  fi
  case "$1" in
    *.tar.bz2) tar xjf "$1" ;;
    *.tar.gz)  tar xzf "$1" ;;
    *.bz2)     bunzip2 "$1" ;;
    *.rar)     unrar x "$1" ;;
    *.gz)      gunzip "$1" ;;
    *.tar)     tar xf "$1" ;;
    *.tbz2)    tar xjf "$1" ;;
    *.tgz)     tar xzf "$1" ;;
    *.zip)     unzip "$1" ;;
    *.Z)       uncompress "$1" ;;
    *.7z)      7z x "$1" ;;
    *)         echo "'$1' 无法被 extract() 解压" ;;
  esac
}

proj() {
  local projects_dir="$HOME/projj"
  cd "${projects_dir}${1:+/$1}"
}

port() {
  [[ -z "$1" ]] && { echo "Usage: port <port_number>"; return 1 }
  lsof -i ":$1"
}

killport() {
  [[ -z "$1" ]] && { echo "Usage: killport <port_number>"; return 1 }
  local pids=$(lsof -ti ":$1")
  if [[ -z "$pids" ]]; then
    echo "No process found on port $1"
    return 1
  fi
  echo "$pids" | xargs kill -9 && echo "✓ Killed process(es) on port $1"
}

# ------------------------------------------------------------------------------
# 10. 插件加载(必须在自定义配置之后,syntax-highlighting 必须最后)
# ------------------------------------------------------------------------------
if [[ -f "$HOMEBREW_PREFIX/share/zsh-autosuggestions/zsh-autosuggestions.zsh" ]]; then
  source "$HOMEBREW_PREFIX/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
  ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
  ZSH_AUTOSUGGEST_STRATEGY=(history completion)
  ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20
fi

# ⚠️ zsh-syntax-highlighting 必须最后加载
if [[ -f "$HOMEBREW_PREFIX/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ]]; then
  source "$HOMEBREW_PREFIX/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
fi

# Starship 提示符
eval "$(starship init zsh)"

# ------------------------------------------------------------------------------
# 11. Ghostty 终端集成
# ------------------------------------------------------------------------------
if [[ "$TERM_PROGRAM" == "ghostty" ]]; then
  if [[ -f "$HOME/.config/ghostty/shell-integration.zsh" ]]; then
    source "$HOME/.config/ghostty/shell-integration.zsh"
  fi
fi

# ------------------------------------------------------------------------------
# 12. 本地配置(机器特定、敏感信息,不纳入版本控制)
# ------------------------------------------------------------------------------
[[ -f ~/.zshrc.local ]] && source ~/.zshrc.local

# ------------------------------------------------------------------------------
# 13. 性能分析(调试用,取消注释后配合顶部 zmodload 使用)
# ------------------------------------------------------------------------------
# zprof

# ==============================================================================
# End of .zshrc
#
# 快速参考:
# - reload          重新加载配置
# - showpath        显示 PATH
# - mkcd <dir>      创建并进入目录
# - extract <file>  智能解压文件
# - port <number>   查看端口占用
# - killport <num>  杀死端口进程
# - proj [name]     切换到项目目录
# ==============================================================================