#!/bin/bash
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║  setup/modules/shell-setup.sh — Shell environment deployment              ║
# ║                                                                              ║
# ║  PURPOSE:                                                                    ║
# ║    Installs and configures the full shell environment: zsh, oh-my-zsh,    ║
# ║    plugins, neovim with lazy.nvim plugins, yazi, starship prompt, and     ║
# ║    deploys all dotfile symlinks to their correct locations.                ║
# ║                                                                              ║
# ║  WHEN TO RUN:                                                                ║
# ║    After core-packages.sh (needs base packages). Runs as the logged-in     ║
# ║    user — must NOT run as root.                                             ║
# ║                                                                              ║
# ║  WHAT GETS DEPLOYED:                                                         ║
# ║    ~/.bashrc, ~/.zshrc, ~/.vimrc — symlinks to Dotfiles repo, unless        ║
# ║      MARCHY_DEPLOY_SHELL_RC=0 (defaults to 1 outside the TUI installer)     ║
# ║    ~/.config/starship.toml  — symlink to repo                              ║
# ║    ~/.config/nvim/          — symlink to repo/nvim/                        ║
# ║    ~/.config/micro/         — copy from repo/micro/ (plugin state needs cp)║
# ║    ~/.config/alot/          — symlink for email client config              ║
# ║    ~/.config/yazi/          — symlink for file manager config              ║
# ║    ~/Pictures/fflogo.svg    — logo asset                                   ║
# ╚══════════════════════════════════════════════════════════════════════════════╝

set -euo pipefail
# -e: any failed package install or symlink operation aborts the module
# -u: unset variable references are errors
# -o pipefail: catch pipe failures

# Load shared logging helpers (log, skip, warn, err functions)
source "$(dirname "${BASH_SOURCE[0]}")/lib/logging.sh"

# ── System update ──────────────────────────────────────────────────────────────
# WHY: Ensure the package database is current before installing to avoid
#      dependency conflicts on a rolling-release distro like Arch.
log "Updating system..."
sudo pacman -Syu --noconfirm

# ── Base shell packages ────────────────────────────────────────────────────────
# WHY: Install package-by-package with idempotency checks rather than one big
#      pacman call. This lets us skip already-installed packages individually
#      and produce useful "already installed" log messages.
# Each package in PACKAGES is checked with `pacman -Qi` (query installed).
#
# Package notes:
#   zsh               — primary shell (replaces bash as default)
#   neovim            — primary editor (configured via dotfiles/nvim/)
#   pyright           — Python LSP server for neovim (coc/LSP plugin)
#   bash-language-server — bash LSP for neovim
#   btop              — interactive resource monitor
#   clang             — C/C++ toolchain; needed by treesitter + some nvim plugins
#   fastfetch         — system info display on terminal open
#   fzf               — fuzzy finder; used by shell history, yazi, neovim
#   hyfetch           — pride-themed neofetch variant
#   lua-language-server — Lua LSP for Hyprland config editing in neovim
#   micro             — beginner-friendly terminal text editor (alternative to nano)
#   pulsemixer        — TUI PulseAudio/PipeWire mixer
#   yazi              — terminal file manager with image preview
#   z                 — smart directory jumper (learns frequent paths)
#   qrencode          — QR code generator for terminal
#   distrobox         — run other Linux distros in containers
#   dysk              — disk usage summary (prettier df alternative)
#   glow              — render markdown in the terminal
#   notmuch           — fast email indexer (used by alot mail client)
#   alot              — TUI email client built on notmuch
log "Installing base shell packages..."
PACKAGES=(zsh neovim curl pyright bash atftp bash-language-server btop clang fastfetch fzf hyfetch lua-language-server micro nano pulsemixer yazi z qrencode distrobox dysk python python-pip glow notmuch alot)
for pkg in "${PACKAGES[@]}"; do
    # -Qi queries the local package database; if it returns non-zero, pkg is not installed
    if ! pacman -Qi "$pkg" &>/dev/null; then
        log "Installing $pkg..."
        # --needed: skip if already at latest version (double-safety with -Qi check)
        sudo pacman -S --noconfirm --needed "$pkg"
    else
        skip "$pkg already installed."
    fi
done

# ── abook (AUR) ───────────────────────────────────────────────────────────────
# WHY: abook is an address book app that integrates with mail clients like alot.
#      It is only available in the AUR (not in official repos), so we use yay.
# IDEMPOTENCY: Check if `abook` binary exists before installing.
if ! command -v abook &>/dev/null; then
    log "Installing abook (AUR)..."
    yay -S --noconfirm --needed abook
else
    skip "abook already installed."
fi

# ── yay fallback ──────────────────────────────────────────────────────────────
# WHY: shell-setup.sh can run standalone (e.g. on an existing system that didn't
#      go through package-managers.sh first). This guards against yay being absent.
# HOW: Build yay from the AUR using its PKGBUILD. /tmp/yay is ephemeral but fine
#      for a build that completes synchronously.
if ! command -v yay &>/dev/null; then
    log "Installing yay..."
    sudo pacman -S --noconfirm --needed git base-devel
    git clone https://aur.archlinux.org/yay.git /tmp/yay
    cd /tmp/yay && makepkg -si --noconfirm
    cd ~
else
    skip "yay already installed."
fi

# ── Rust / Cargo ──────────────────────────────────────────────────────────────
# WHY: Some shell tools are installed via `cargo install`. This provides the
#      Rust toolchain in the user's home directory (~/.cargo/) separate from
#      any system-wide Rust installation.
# NOTE: Uses the official rustup installer script rather than the pacman package
#       because it installs to ~/.cargo which is user-owned (no sudo needed for
#       cargo install later). The -y flag disables interactive prompts.
if ! command -v cargo &>/dev/null; then
    log "Installing Rust & Cargo..."
    # --proto: restrict to HTTPS only for security
    # --tlsv1.2: require TLS 1.2 minimum
    # -s -- -y: pass -y to the installer script (non-interactive)
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    # Source cargo env so `cargo` is available in this shell session immediately
    . "$HOME/.cargo/env"
else
    skip "Rust & Cargo already installed."
fi

# ── nvm + Node.js ─────────────────────────────────────────────────────────────
# WHY: Same rationale as in package-managers.sh. shell-setup.sh can run
#      independently so nvm is bootstrapped here as a fallback.
if ! command -v node &>/dev/null; then
    log "Installing nvm and Node.js..."
    if [ ! -d "$HOME/.nvm" ]; then
        curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
    fi
    # Source nvm into current shell to make the `nvm` command available immediately
    . "$HOME/.nvm/nvm.sh"
    nvm install 22
else
    skip "Node.js already installed."
fi

# ── Git global configuration ───────────────────────────────────────────────────
# WHY: Set neovim as the commit message editor so that `git commit` without -m
#      opens neovim instead of vi/nano. This is user-global config (not per-repo).
log "Configuring git..."
git config --global core.editor nvim

# ── Dotfile deployment ────────────────────────────────────────────────────────
# WHY: We use symlinks (ln -sf) rather than copies wherever possible so that
#      edits to files in ~/Dotfiles/ are immediately reflected in the live config
#      without needing to re-run this script.
# EXCEPTION: micro/ is copied because micro stores plugin state files in its
#      config directory — symlinks would write plugin data back to the repo.
log "Deploying dotfiles..."
mkdir -p ~/.config ~/Pictures

# Defensive: an earlier root/sudo step (e.g. the installer creating
# ~/.config/Yubico for FIDO before the user-level config exists) can leave
# ~/.config owned by root. `mkdir -p` above won't fix that, and every symlink
# below would then fail with "Permission denied". If ~/.config isn't writable by
# us, reclaim the whole tree. Non-fatal so it never blocks the rest of setup.
if [[ -e "$HOME/.config" && ! -w "$HOME/.config" ]]; then
    warn "~/.config is not writable — reclaiming ownership for $(id -un)..."
    sudo chown -R "$(id -un):$(id -gn)" "$HOME/.config" 2>/dev/null || true
fi

# Shell init files — symlink so edits in the repo apply immediately.
# MARCHY_DEPLOY_SHELL_RC lets the caller install the shell tools/plugins below
# (zsh, oh-my-zsh, neovim, yazi, micro, starship, …) without overwriting the
# user's existing .bashrc/.zshrc/.vimrc with the_miro's personal versions.
# Defaults to deploying them when run standalone (outside the TUI installer).
if [[ "${MARCHY_DEPLOY_SHELL_RC:-1}" == "1" ]]; then
    ln -sf ~/Dotfiles/.bashrc ~/.bashrc
    ln -sf ~/Dotfiles/.zshrc ~/.zshrc
    ln -sf ~/Dotfiles/.vimrc ~/.vimrc
else
    skip "Personal shell rc's not selected — leaving .bashrc/.zshrc/.vimrc as-is."
fi

# Starship prompt config — symlink into ~/.config/
ln -sf ~/Dotfiles/starship.toml ~/.config/starship.toml

# Micro editor — copy (not symlink) because micro writes plugin/state data into
# its config directory and we don't want those writes going into the git repo
rm -rf ~/.config/micro
cp -r ~/Dotfiles/micro ~/.config/

# Neovim — symlink the entire config directory to the repo
rm -rf ~/.config/nvim
ln -sf ~/Dotfiles/nvim ~/.config/nvim

# ── Neovim plugin sync ────────────────────────────────────────────────────────
# WHY: lazy.nvim (the Neovim plugin manager) needs to download and compile plugins
#      on first launch. We run it headlessly here so the first interactive launch
#      is fast and plugins are ready.
# HOW: --headless runs neovim without a UI; +Lazy! sync triggers lazy.nvim's
#      sync command; +qa quits after sync completes.
# || true: a failed sync is non-fatal — user can run :Lazy sync manually later.
log "Syncing neovim plugins (lazy.nvim)..."
nvim --headless "+Lazy! sync" +qa 2>/dev/null || true

# alot email client config — symlink
rm -rf ~/.config/alot
ln -sf ~/Dotfiles/alot ~/.config/alot

# yazi file manager config — symlink
rm -rf ~/.config/yazi
ln -sf ~/Dotfiles/yazi ~/.config/yazi

# spotify-tui config — symlink (for the TUI Spotify client)
rm -rf ~/.config/spotify-tui
ln -sf ~/Dotfiles/spotify-tui ~/.config/spotify-tui

# Copy FF (Fastfetch) logo SVG to ~/Pictures for the fastfetch config to reference
cp -f ~/Dotfiles/resources/fflogo.svg ~/Pictures/fflogo.svg

# ── Starship prompt ────────────────────────────────────────────────────────────
# WHY: Starship is a fast, customizable cross-shell prompt. The config is in
#      dotfiles/starship.toml (symlinked above). The binary itself must be
#      installed separately — the official install script handles arch/platform
#      detection automatically.
# IDEMPOTENCY: Check for the `starship` binary before running the installer.
if ! command -v starship &>/dev/null; then
    log "Installing Starship..."
    # -sS: silent but show errors; --yes: non-interactive
    curl -sS https://starship.rs/install.sh | sh -s -- --yes
else
    skip "Starship already installed."
fi

# ── oh-my-zsh ─────────────────────────────────────────────────────────────────
# WHY: oh-my-zsh provides the plugin framework, completion system, and themes
#      that our .zshrc configuration depends on. It installs to ~/.oh-my-zsh/.
# HOW: Download and run the official install script.
#   --unattended:  skip ALL interactive prompts — crucially the "overwrite .zshrc?"
#                  question, which otherwise hangs an automated install. It also
#                  implies RUNZSH=no and CHSH=no.
#   KEEP_ZSHRC=yes: do NOT replace ~/.zshrc with the oh-my-zsh template. We already
#                  symlinked ~/.zshrc to the dotfiles copy above; without this the
#                  installer would clobber that symlink with its own template.
#   RUNZSH=no / CHSH=no kept explicit for clarity (we change the shell ourselves below).
if [ ! -d "$HOME/.oh-my-zsh" ]; then
    log "Installing oh-my-zsh..."
    KEEP_ZSHRC=yes RUNZSH=no CHSH=no \
        sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
else
    skip "oh-my-zsh already installed."
fi

# ── oh-my-zsh plugins ─────────────────────────────────────────────────────────
# WHY: These two plugins are referenced in .zshrc and must exist in ZSH_CUSTOM.
# They are not bundled with oh-my-zsh and must be cloned separately.
# ZSH_CUSTOM defaults to ~/.oh-my-zsh/custom/ — the standard plugin install location.
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"

# zsh-syntax-highlighting: colors commands as you type (green=valid, red=invalid)
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ]; then
    log "Installing zsh-syntax-highlighting..."
    git clone https://github.com/zsh-users/zsh-syntax-highlighting.git \
        "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting"
else
    skip "zsh-syntax-highlighting already installed."
fi

# zsh-autosuggestions: suggests previously-typed commands in gray as you type
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ]; then
    log "Installing zsh-autosuggestions..."
    git clone https://github.com/zsh-users/zsh-autosuggestions \
        "$ZSH_CUSTOM/plugins/zsh-autosuggestions"
else
    skip "zsh-autosuggestions already installed."
fi

# ── Default shell change ───────────────────────────────────────────────────────
# WHY: New login shells still default to bash unless explicitly changed.
#      This writes the new shell to /etc/passwd for this user.
# HOW: Compare current $SHELL to /usr/bin/zsh and change if different.
#      Use `sudo chsh` (not bare `chsh`): a bare chsh authenticates the calling
#      user via PAM and prompts "Password:", which hangs an unattended install.
#      Routing through sudo uses the passwordless setup rule, so it runs silently;
#      `whoami` targets the right account whether run as the user or via runuser.
if [ "$SHELL" != "/usr/bin/zsh" ]; then
    log "Setting zsh as default shell..."
    sudo chsh -s /usr/bin/zsh "$(whoami)"
else
    skip "zsh is already the default shell."
fi

log "Shell setup complete."
