#! /bin/bash
#
# This script should be run using curl:
# bash -c "$(curl -fsSL https://raw.githubusercontent.com/EricOgie/maczshconfigurator/main/setup.sh)"

# or using wget:
# bash -c "$(wget -qO- https://raw.githubusercontent.com/EricOgie/maczshconfigurator/main/setup.sh)"

# For a more personalised usage, you can download the setup.sh script, tweak  and run afterward.
#
set -e

# Define color functions
blue() { printf "\e[34m%s\e[0m\n" "$1"; }

green() { printf "\e[32m%s\e[0m\n" "$1"; }

red() { printf "\e[31m%s\e[0m\n" "$1"; }

yellow() { printf "\e[33m%s\e[0m\n" "$1"; }

info() { printf "\e[34m%s\e[0m\n" "$1"; }

# Ensure required variables exist.
# Establish a min ruby version of 3.1.0
MIN_RUBY_VERSION="3.1.0"

# Get current Ruby version.
# A simple ruby -v | awk '{print $2}' can output x.x.xpx instead of x.x.x
# Remove any unwanted [a-zA-Z] from the version output using sed
CURRENT_RUBY_VERSION=$(ruby -v | awk '{print $2}' | sed 's/[a-zA-Z].*//')

# Compute the lower version between CURRENT_RUBY_VERSION and MIN_RUBY_VERSION
LOWER_VERSION=$(printf '%s\n%s\n' "$CURRENT_RUBY_VERSION" "$MIN_RUBY_VERSION" | sort -V | head -n 1)

# Login User and User home
USER=${USER:-$(id -u -n)}
HOME="${HOME:-$(eval echo ~$USER)}"
PLUGINS_DIR=${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/

# Array to keep track of tools and their correspondings action
# Iterms appendded to this array should be in key-value pair formatted as "tool_name=action_done". e.g., "Zsh=Set as user default shell"
tools_installed=()

command_exists(){
    # Check if a command is available on the system.
    #
    # Arguments:
    #   $@ - Command(s) to check.
    #
    # Returns:
    #   0 if the command exists and is executable.
    #   1 if the command does not exist or is not executable.
    command -v "$@" > /dev/null 2>&1 
}

handle_error() {
    # Print the error message using formatted output
    red "Error: $1" >&2
    exit 1
}

execute_command() {
    # This function executes a command passed as arguments and captures its output or error message.
    # If the command fails, it handles the error by invoking a custom error handler.

    # Arguments:
    #   $@: Command and its arguments to be executed (passed as a series of arguments to the function).

    local output

    # Execute the command and capture any output or error message
    output=$("$@" 2>&1) || handle_error "Command failed: $* : Msg: $output"
    echo "$output"
}

run_remote_installer(){
    # This function downloads a remote installer script from a given URL, executes it, and cleans up afterward.
    # It also accepts an optional argument to pass additional options to the installer script.

    # Arguments:
    #   $1: installerUrl (required) - The URL to the remote installer script to be downloaded.
    #   $2: installerOption (optional) - An optional argument that can be passed to the installer script. Default is an empty string.

    local installerUrl="$1"
    local installerOption=${2:-""}
    local installerScript

    # create tmp location for installer
    installerScript=$(mktemp) || { echo "Failed to create tmp file for installer"; return 1; }

    # Download installer script
    execute_command curl -fsSL "$installerUrl" -o "$installerScript" || { rm -f "$installerScript"; return 1; }

    # Run installer script
    execute_command /bin/bash "$installerScript" "$installerOption"

    # Cleanup
    rm -f "$installerScript"
}

can_sudo() {
    # This function checks if the user can use the sudo command.
    # It performs two checks:
    # 1. Checks if sudo is installed.
    # 2. Attempts to refresh the sudo timestamp to verify if the user has valid sudo permissions.
    #    - If there is an active sudo session, this will succeed without prompting for a password.
    #    - If there is no active session or the user needs to authenticate, this will prompt for a password.

    # Check if sudo is installed
    command_exists sudo || return 1

    # Attempt to refresh the sudo timestamp to validate sudo permissions
    # Redirect output to /dev/null to avoid displaying any prompts or errors
    sudo -v >/dev/null 2>&1
}

print_section_header() {
    echo
    blue "***********************************************"
    blue "${2:-Installing} $1"
    blue "***********************************************"
    echo
}

print_finish_feedback () {
    echo 
    green "✅ Successfully installed $1"
    echo
}

install_prerequisites() {
    print_section_header "Preliminary Checks and Configurations" "Running"
    # Check if Homebrew is installed - Install if not
    if ! command_exists brew; then
        yellow "Homebrew not found. Installing Homebrew..."

        run_remote_installer "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"

        # Homebrew does not automatically configure environment path upon successful installation
        # its environment path has to be calculated and configured manually.

        # Detect CPU architecture (M series chip or Intel)
        if [[ $(uname -m) == "arm64" ]]; then 
            BREW_PREFIX="/opt/homebrew"
        else
            BREW_PREFIX="/usr/local"
        fi

        SHELL_NAME=$(basename "$SHELL")

        case "$SHELL_NAME" in
            zsh)
                SHELL_CONFIG="$HOME/.zshrc"
                ;;
            bash)
                # On macOS, bash configuration file can be named .bashrc or .bash_profile 
                if [[ -f "$HOME/.bash_profile" ]]; then
                    SHELL_CONFIG="$HOME/.bash_profile"
                else
                    SHELL_CONFIG="$HOME/.bashrc"
                fi
                ;;
            *)
                echo "Unsupported shell: $SHELL_NAME. Please add Homebrew to your PATH manually."
                exit 1
                ;;
        esac

        # Add Homebrew to PATH
        echo 'eval "$('$BREW_PREFIX'/bin/brew shellenv)"' >> "$SHELL_CONFIG"
        
        # Apply changes immediately
        eval "$($BREW_PREFIX/bin/brew shellenv)"

        if ! command_exists brew; then
            handle_error "❌ Could not install homebrew successfully. Please visit https://brew.sh/ for details on how to install Homebrew on your machine."
            exit 1 
            
        fi

        # Run update
        execute_command brew update

        # Add Homebrew to the list of tools installed
        tools_installed+=("Homebrew Package Manager=✅ Installed successfully")

        print_finish_feedback  Homebrew

    fi

    # Check if zsh is installed - Install if not
    if ! command_exists zsh; then
        yellow "Zsh shell not found.  Installing..."
        execute_command brew install zsh

        tools_installed+=("Zsh Shell=✅ Installed successfully")

        print_finish_feedback  "zsh shell"
    fi

    # Setup Zsh as default shell if not set
    if [[ "$SHELL" != *"zsh" && "$SHELL" != "$(which zsh)" ]]; then

        info "Default login shell is not zsh. Configuring zsh as default shell for user, $USER..."

        # Change to MacOs pre installed zsh or fallback to homebrew installed zsh shell
        if [[ -x "/bin/zsh" ]]; then
            ZSH="/bin/zsh"
        else
            ZSH="$(which zsh)"  # Fallback to Homebrew-installed Zsh
        fi

        execute_command sudo chsh -s "$ZSH" "$USER"

        tools_installed+=("Zsh Shell=💡 Set as default shell for user, $USER")

        info "zsh shell is set as default login shell for $USER"
        echo
    fi
}

install_oh_my_zsh() {
     # Install Oh-my-zsh
    print_section_header "oh-my-zsh"
    if [ ! -d "$HOME/.oh-my-zsh" ]; then
        echo "Installing oh-my-zsh..."

        run_remote_installer "https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh" "--unattended"

        tools_installed+=("oh-my-zsh=✅ Installed successfully")

        # Disable automatic update set by default. Most users find the auto update feature prompt to be a little too much.
        if [[ -f "$HOME/.zshrc" ]]; then
            sed -i '' "s/# zstyle ':omz:update' mode disabled/zstyle ':omz:update' mode disabled/" "$HOME/.zshrc"
            tools_installed+=("oh-my-zsh=💡 Disabled automatic update prompt")
        fi
 
        print_finish_feedback  oh-my-zsh

    else
        info "Oh-My-Zsh is already installed."
    fi
}

install_themes_and_fonts() {
    print_section_header "Themes and Fonts"

    # Install Powerlevel10k theme
    if [ ! -d "$HOME/.oh-my-zsh/custom/themes/powerlevel10k" ]; then
        info "Installing Powerlevel10k theme..."
        execute_command git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k

        # Set the theme to Powerlevel10k in ~/.zshrc file
        sed -i '' 's/^ZSH_THEME=".*"/ZSH_THEME="powerlevel10k\/powerlevel10k"/' ~/.zshrc

        tools_installed+=("powerlevel10k Plugin=✅ Installed successfully")

        print_finish_feedback  "Powerlevel10k theme"

    else
        info "Powerlevel10k theme already installed."
        echo "Moving on to other installations..."
        echo
    fi

    # Install and configure Nerd Fonts
    if [ ! -f "$HOME/Library/Fonts/HackNerdFont-Regular.ttf" ]; then

        info "Installing Hack Nerd Font..."

        execute_command env HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask font-hack-nerd-font
        tools_installed+=("Nerd-Font=✅ Installed successfully")

        print_finish_feedback  "Hack Nerd Font"
    else
        info "Hack Nerd Font already installed"
    fi

    if [ -d "/Applications/iTerm.app" ]; then

        # Update iTerm2 preferences to use Hack Nerd Font
        info "Configuring Iterm2 to use Hack Nerd Font for Non Ascii Fonts"
        # Set Non-ASCII Font
        defaults write com.googlecode.iterm2 "Non Ascii Font" -string "HackNF-Regular 12"
        # Set Normal Font
        defaults write com.googlecode.iterm2 "Normal Font" -string "MesloLGS-NF-Regular 13"
        # Ensure Non-ASCII Font usage is enabled
        defaults write com.googlecode.iterm2 "Use Non-ASCII Font" -bool true

    fi
}

install_plugins() {
    print_section_header "Plugins"
    # Install zsh plugins
    # - Install zsh-syntax-highlighting
    if [ ! -d "$PLUGINS_DIR/zsh-syntax-highlighting" ]; then
        info "Installing zsh-syntax-highlighting..."
        execute_command git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting

        tools_installed+=("zsh-syntax-highlighting Plugin=✅ Installed successfully")

        print_finish_feedback  "zsh-syntax-highlighting"
    else
        info "zsh-syntax-highlighting is already installed"
        echo "Moving on to other installations..."
        echo
    fi

    if [ ! -d "$PLUGINS_DIR/zsh-autosuggestions" ]; then
        # - Install zsh-autosuggestions
        echo
        info "Installing zsh-autosuggestions..."
        execute_command git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
        tools_installed+=("zsh-autosuggestions=✅ Installed successfully")

        print_finish_feedback  "zsh-autosuggestions"
    else
        info "zsh-autosuggestions is already installed"
        echo
    fi

    # Add plugins to .zshrc
    sed -i '' 's/^plugins=(.*)/plugins=(git zsh-syntax-highlighting zsh-autosuggestions)/' ~/.zshrc
}

install_colorls() {
    print_section_header "colorls"
    if ! command_exists colorls; then

        # Check if CURRENT_RUBY_VERSION meets the min requirment of 3.1.0. If not, Install a compartible version using rbenv.
        # Note: CURRENT_RUBY_VERSION here is system installed ruby version.
        if [[ "$LOWER_VERSION" != "$MIN_RUBY_VERSION" ]]; then

            yellow "Your current Ruby version, $CURRENT_RUBY_VERSION is below the minimum required version, $MIN_RUBY_VERSION."
            info "Installing a compartible Ruby version..."
            echo 

            if ! command_exists rbenv ; then
                execute_command brew install rbenv
                execute_command brew install ruby-build

                tools_installed+=("rbenv=✅ Installed successfully")
                print_finish_feedback  "rbenv"
            fi


            if ! rbenv versions | grep -q "3.1.0"; then
                info "Installing ruby 3.1.0 using rbenv"
                echo
                execute_command rbenv install 3.1.0
                execute_command rbenv global 3.1.0
                execute_command rbenv rehash

                print_finish_feedback  "ruby@v3.1.0"
            fi

        fi

        # Ensure rbenv initialized in each terminal session
        echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.zshrc

        # If rbenv is used to install a compartible ruby (as seen above), we may have a situation where two ruby versions exist in parallel -
        # System installed ruby and rbenv managed ruby. 
        # When Ruby gems are installed, bash will naturally default to the system installed Ruby (whose version may be < the min required) 
        # To ensure rbenv Ruby is use for gem installations, we init rbenv in the running bash session
        info "Initialized rbenv in current bash session..."
        if command_exists rbenv; then
            eval "$(rbenv init - bash)"
        fi

        # Install colorls
        execute_command sudo gem install colorls
        tools_installed+=("Colorls=✅ Installed successfully")

        print_finish_feedback  "colorls"
   
    fi

    if ! (grep -q "alias ls=colorls" ~/.zshrc || grep -q "alias ls='colorls'" ~/.zshrc); then
        # Prompt user to add alias ls=colorls
        blue "Would you like to set up 'ls' alias to use colorls? (y/n): "
        read setup_alias

        # Check user's response and respond accordingly
         if [[ $setup_alias == "y" || $setup_alias == "Y" ]]; then
            echo "Adding alias 'ls=colorls' to ~/.zshrc..."

            tools_installed+=("Colorls=💡 Added ls as alias for colorls")
            echo "alias ls=colorls" >> ~/.zshrc
         else
            echo "Skipping alias setup..."
         fi
    fi
}

print_success_message() {
    # Prints a success message block upon successfull completion, 
    # along with a formatted table that lists the tools installed and actions performed.
    echo
    echo
    green "   🔥 Installation complete - You are all set!"
    echo

    blue "Take a look at your Trophies 🏆🏆🏆 below"
    # Print Headers
    printf "%-87s\n" | tr ' ' '-'
    printf "| %-3s | %-32s | %-42s |\n" "S/N" "Tools" "Action Done"
    printf "%-87s\n" | tr ' ' '-'

    # Populate table installed tool's table
    for i in "${!tools_installed[@]}"; do
        tool="${tools_installed[i]%%=*}"      # Extract tool name
        action_done="${tools_installed[i]#*=}" # Extract action done

        # Adjust width for Action Done column to fit longer content
        printf "| %-3d | %-32s | %-43s |\n" "$((i + 1))" "$tool" "$action_done"
    done

    printf "%-87s\n" | tr ' ' '-'
    echo
    blue "Restart your terminal and follow Powerlevel10k configuration wizard to customize your terminal looks"
}

main() {
    can_sudo

    install_prerequisites

    install_oh_my_zsh

    install_themes_and_fonts

    install_plugins

    install_colorls
    
    print_success_message
}


main "$@"