#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

ask_for_sudo() {
  echo "Prompting for sudo password..."
  # sudo -v doesn't work on macOS when passwordless sudo is enabled. It still
  # asks for a password. sudo true should work for both Linux and macOS
  if sudo true; then
    # Keep-alive
    while true; do
      sudo -n true
      sleep 60
      kill -0 "$$" || exit
    done 2>/dev/null &
    echo "Sudo credentials updated."
  else
    echo "Obtaining sudo credentials failed."
    exit 1
  fi
}

# Choose a user account to use for this installation
get_user() {
  echo "Setting TARGET_USER. The order of preference is: USER (${USER-}), USERNAME (${USERNAME-}), LOGNAME (${LOGNAME-}), whoami ($(whoami))"
  TARGET_USER="${USER:-${USERNAME:-${LOGNAME:-$(whoami)}}}"
  echo "TARGET_USER set to: $TARGET_USER"

  if [ -z "$TARGET_USER" ]; then
    echo "ERROR: The TARGET_USER variable is not set, or set to an empty string"
    exit 1
  fi
}

install_brew() {
  if ! command -v brew >/dev/null 2>&1; then
    if ! xcode-select -p >/dev/null 2>&1; then
      echo "Installing Xcode CLI..."
      xcode-select --install
    else
      echo "Xcode is already installed"
    fi

    echo "Installing Homebrew to: ${HOMEBREW_PATH}"
    # Homebrew installation script asks for confirmation before running any action
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  else
    echo "Homebrew is already installed"
  fi
}

install_brew_formulae() {
  echo "Installing brew formulae..."

  if ! command -v brew >/dev/null 2>&1; then
    echo "ERROR: the brew command is not available. Exiting..."
    exit 1
  fi

  echo "Disabling homebrew usage analytics..."
  brew analytics off

  echo "Running brew cleanup..."
  brew cleanup

  echo "Installing brew formulae"
  for f in \
    zsh-autosuggestions \
    zsh-completions \
    zsh-syntax-highlighting; do
    if ! brew list --versions "$f" >/dev/null; then
      echo "Installing $f"
      if ! brew install "$f"; then
        # If the installation failed, retry with verbose output enabled.
        # Useful for CI builds.
        brew install --verbose "$f"
      fi
    else
      echo "$f is already installed"
    fi
  done

  # Other useful casks that I might need in the future:
  # docker, gimp

  echo "Installing brew casks..."
  for f in \
    iterm2 \
    visual-studio-code; do
    if ! brew list "$f" >/dev/null 2>&1; then
      echo "Installing $f cask"
      if ! brew install "$f"; then
        # If the installation failed, retry with verbose output enabled.
        # Useful for CI builds.
        brew install --verbose "$f"
      fi
    else
      echo "$f cask is already installed"
    fi
  done

  # Save Homebrew’s installed location.
  BREW_PREFIX="$(brew --prefix)"

  if [ -z "$BREW_PREFIX" ]; then
    echo "ERROR: The BREW_PREFIX variable is not set, or set to an empty string"
    exit 1
  fi
}

setup_user() {
  echo "Creating directories for the $TARGET_USER in $HOME"
  mkdir -pv "$HOME/bin"
  mkdir -pv "$HOME/Downloads"
  mkdir -pv "$HOME/workspaces"
  mkdir -pv "$HOME/workspaces-work"

  mkdir -pv "${GCLOUD_CONFIG_DIRECTORY}"
  mkdir -pv "${USER_CACHE_DIRECTORY}"

  touch "$HOME/.gitconfig-work"
}

setup_debian() {
  echo "Setting up a Debian or Debian-based system"

  DEBIAN_FRONTEND=noninteractive
  export DEBIAN_FRONTEND

  echo "Installing the minimal set of packages"
  sudo apt-get -qq update || true
  sudo apt-get -qqy install \
    apt-transport-https \
    apt-utils \
    ca-certificates \
    curl \
    dialog \
    dirmngr \
    git \
    gnupg2 \
    locales \
    lsb-release \
    software-properties-common \
    --no-install-recommends

  echo "Ensuring the $LANG locale is available..."
  sudo locale-gen "$LANG"
  sudo dpkg-reconfigure --frontend=noninteractive locales
  sudo update-locale LANG="$LANG" LANGUAGE="$LANGUAGE" LC_ALL="$LC_ALL"

  # Add the Google Chrome distribution URI as a package source if needed
  # Don't install it if we're in crostini (Chrome OS linux environment) or if it's already installed
  if ! is_crostini && ! dpkg -s google-chrome-stable >/dev/null 2>&1; then
    echo "Installing Chrome browser..."

    echo "Downloading Chrome package..."
    TEMP_DIRECTORY="$(mktemp -d)"
    CHROME_ARCHIVE_NAME=google-chrome-stable_current_amd64.deb
    CHROME_ARCHIVE_PATH="$TEMP_DIRECTORY/$CHROME_ARCHIVE_NAME"
    curl -fsLo "$CHROME_ARCHIVE_PATH" https://dl.google.com/linux/direct/"$CHROME_ARCHIVE_NAME"

    echo "Making the temporary directory world-accessible..."
    chmod -Rv 777 "$TEMP_DIRECTORY"

    echo "Installing Chrome package..."
    sudo apt-get -qqy install "$CHROME_ARCHIVE_PATH"

    echo "Removing Chrome package..."
    rm "$CHROME_ARCHIVE_PATH"

    echo "Removing the temporary directory..."
    rm -rf "$TEMP_DIRECTORY"

    echo "Installing missing dependencies..."
    sudo apt-get -fqq install

    unset TEMP_DIRECTORY
    unset CHROME_ARCHIVE_NAME
    unset CHROME_ARCHIVE_PATH
  fi

  DISTRIBUTION="$(lsb_release -ds)"
  DISTRIBUTION_CODENAME="$(lsb_release -cs)"
  echo "Configuring the distribution: ${DISTRIBUTION}, codename: ${DISTRIBUTION_CODENAME}..."

  docker_apt_repository_url=

  docker_distribution=

  if is_official_debian || is_ubuntu; then

    docker_distribution="debian"

    echo "Enabling main repository"
    sudo add-apt-repository --yes main

    if is_ubuntu; then
      echo "Enabling universe, multiverse, and restricted repositories"
      sudo add-apt-repository --yes universe
      sudo add-apt-repository --yes multiverse
      sudo add-apt-repository --yes restricted
      docker_distribution="ubuntu"
    fi

    docker_apt_repository_url="https://download.docker.com/linux/${docker_distribution}"
    if ! is_apt_repo_available "${docker_apt_repository_url}"; then
      add_apt_repo \
        "${docker_apt_repository_url}/gpg" \
        "docker.gpg" \
        "${docker_apt_repository_url}" \
        "docker.list"
    fi

    vs_code_apt_repository_url="https://packages.microsoft.com/repos/code"
    if ! is_apt_repo_available "${vs_code_apt_repository_url}"; then
      add_apt_repo \
        "https://packages.microsoft.com/keys/microsoft.asc" \
        "packages.microsoft.gpg" \
        "${vs_code_apt_repository_url}" \
        "vscode.list" \
        "stable main"
    fi
    unset vs_code_apt_repository_url
  elif is_debian && ! is_official_debian; then
    echo "This is a non-official Debian distribution."
  else
    echo "WARNING: distribution ${DISTRIBUTION} is not supported. Skipping distribution-specific configuration..."
  fi

  clone_git_repository_if_not_cloned_already "$(dirname "${ZSH_AUTOSUGGESTIONS_CONFIGURATION_PATH}")" "https://github.com/zsh-users/zsh-autosuggestions.git"
  clone_git_repository_if_not_cloned_already "$(dirname "${ZSH_COMPLETIONS_PATH}")" "https://github.com/zsh-users/zsh-completions.git"

  sudo apt-get -qq update

  sudo DEBIAN_FRONTEND=noninteractive apt-get -qqy install \
    bash-completion \
    bridge-utils \
    bzip2 \
    coreutils \
    dnsutils \
    file \
    findutils \
    grep \
    gzip \
    hostname \
    less \
    locales \
    lsof \
    mount \
    nano \
    net-tools \
    python3-pip \
    python3-venv \
    tree \
    unzip \
    xclip \
    xz-utils \
    zip \
    zsh \
    zsh-syntax-highlighting \
    --no-install-recommends

  if is_official_debian || is_ubuntu; then
    sudo DEBIAN_FRONTEND=noninteractive apt-get -qqy install \
      code
  fi

  if is_codespaces; then
    # Workaround for https://github.com/microsoft/WSL/issues/2775
    sudo DEBIAN_FRONTEND=noninteractive apt-get -qqy remove \
      azure-cli
  fi

  echo "Installing packages from the additional APT repositories..."
  # Don't use docker_apt_repository_url so this check works on distributions that
  # don't use the standard Docker APT repository
  if is_apt_repo_available "docker"; then
    echo "Installing Docker..."
    sudo DEBIAN_FRONTEND=noninteractive apt-get -qqy install \
      containerd.io \
      docker-ce \
      docker-ce-cli

    if is_official_debian || is_ubuntu; then
      sudo DEBIAN_FRONTEND=noninteractive apt-get -qqy install \
        docker-buildx-plugin \
        docker-compose-plugin
    else
      echo "Setting Docker Buildx as the default builder using the legacy installation method..."
      sudo docker buildx install

      echo "Install Docker Compose using the legacy installation method"
      sudo DEBIAN_FRONTEND=noninteractive apt-get -qqy install \
        docker-compose
    fi

    DOCKER_GROUP_NAME="docker"
    echo "Creating the $DOCKER_GROUP_NAME group for Docker"
    getent group "$DOCKER_GROUP_NAME" >/dev/null 2>&1 || sudo groupadd "$DOCKER_GROUP_NAME"
    echo "Adding $TARGET_USER user to the $DOCKER_GROUP_NAME group"
    sudo gpasswd -a "$TARGET_USER" "$DOCKER_GROUP_NAME"
    unset DOCKER_GROUP_NAME
  else
    echo "WARNING: Skipping Docker installation because its APT repository is not available"
  fi

  unset docker_apt_repository_url

  sudo apt-get -qqy autoremove
  sudo apt-get -qqy autoclean
  sudo apt-get -qqy clean

  if [ -z "$TARGET_USER" ]; then
    echo "ERROR: The TARGET_USER variable is not set, or set to an empty string"
    exit 1
  fi

  echo "Adding $TARGET_USER user to the sudoers group"
  sudo gpasswd -a "$TARGET_USER" sudo
}

setup_macos() {
  echo "Setting up macOS"

  ###############################################################################
  # General UI/UX                                                               #
  ###############################################################################

  echo "Configuring macOS UI/UX"

  # Disable the sound effects on boot
  sudo nvram SystemAudioVolume=%00

  # Enable the sound effects on boot
  # sudo nvram SystemAudioVolume=%01

  ###############################################################################
  # Mac App Store                                                               #
  ###############################################################################

  echo "Configuring macOS App Store"

  # Enable the automatic update check
  sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate.plist AutomaticCheckEnabled -bool true

  # Check for software updates daily, not just once per week
  sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate.plist ScheduleFrequency -int 1

  # Download newly available updates in background
  sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate.plist AutomaticDownload -int 1

  # Install System data files & security updates
  sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate.plist CriticalUpdateInstall -int 1

  # Automatically download apps purchased on other Macs
  sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate.plist ConfigDataInstall -int 1

  # Automatically install macOS Updates
  sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate.plist AutomaticallyInstallMacOSUpdates -int 1

  # Turn on app auto-update in App Store
  sudo defaults write /Library/Preferences/com.apple.commerce.plist AutoUpdate -bool true

  # Allow the App Store to reboot machine on macOS updates
  sudo defaults write /Library/Preferences/com.apple.commerce.plist AutoUpdateRestartRequired -bool true

  # Enable automatic updates in System Preferences -> Software Update -> Advanced -> Check for updates
  sudo softwareupdate --schedule ON

  ###############################################################################
  # Dock                                                                        #
  ###############################################################################

  echo "Configuring macOS Dock"

  # Change minimize/maximize window effect
  defaults write com.apple.dock mineffect -string "scale"

  # Minimize windows into their application’s icon
  defaults write com.apple.dock minimize-to-application -bool true

  # Don't move the dock to secondary screens
  defaults write com.apple.Dock position-immutable -bool yes

  ###############################################################################
  # Terminal & iTerm 2                                                          #
  ###############################################################################

  echo "Configuring macOS Terminal"

  # Only use UTF-8 in Terminal.app
  defaults write com.apple.terminal StringEncodings -array 4

  # Don’t display the annoying prompt when quitting iTerm
  defaults write com.googlecode.iterm2 PromptOnQuit -bool false

  ###############################################################################
  # Trackpad                                                                    #
  ###############################################################################

  echo "Configuring macOS Trackpad"

  # Disable lookup and data detectors
  defaults write NSGlobalDomain com.apple.trackpad.forceClick -bool false

  # Trackpad: enable tap to click for this user
  defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

  # Trackpad: enable tap to click for the login screen
  defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -int 1
  defaults write NSGlobalDomain com.apple.mouse.tapBehavior -int 1

  ###############################################################################
  # Keyboard                                                                    #
  ###############################################################################

  # Keyboard: enable press-and-hold popup to write accented characters
  defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool true

  ###############################################################################
  # Menu bar                                                                    #
  ###############################################################################

  echo "Configuring macOS menu bar"

  # Show icons in the menu bar
  defaults write com.apple.systemuiserver menuExtras -array \
    "/System/Library/CoreServices/Menu Extras/AirPort.menu" \
    "/System/Library/CoreServices/Menu Extras/Bluetooth.menu" \
    "/System/Library/CoreServices/Menu Extras/Clock.menu" \
    "/System/Library/CoreServices/Menu Extras/Volume.menu"

  # Ensure we're using a digital clock
  defaults write com.apple.menuextra.clock IsAnalog -bool false

  # Show date and time in the menu bar
  defaults write com.apple.menuextra.clock "DateFormat" "EEE d MMM HH:mm:ss"

  # Don't flash time and date separators
  defaults write com.apple.menuextra.clock FlashDateSeparators -bool false

  ###############################################################################
  # Adobe stuff                                                                 #
  ###############################################################################

  echo "Configuring Adobe stuff in macOS"

  # Kill those processes
  killall AGSService ACCFinderSync "Core Sync" AdobeCRDaemon "Adobe Creative" AdobeIPCBroker node "Adobe Desktop Service" "Adobe Crash Reporter" CCXProcess CCLibrary || true

  # Disable Adobe autostart agents and daemons
  for i in /Library/LaunchAgents/com.adobe* \
    "$HOME"/Library/LaunchAgents/com.adobe* \
    /Library/LaunchDaemons/com.adobe* \
    /System/Library/LaunchAgents/com.adobe* \
    /System/Library/LaunchDaemons/com.adobe*; do

    # Exit the loop if there are no matching files
    # Safeguard for when nullglob is disabled in bash
    [ -f "$i" ] || break

    # Disable the agent
    launchctl unload -w "$i" 2>/dev/null

    # Avoid further edits
    sudo chmod 000 "$i"
  done

  # Disable the "Core Sync" finder extension
  if defaults read "com.apple.finder.SyncExtensions" 2>/dev/null; then
    defaults delete "com.apple.finder.SyncExtensions"
  fi

  # Remove the "Core Sync" finder extension
  sudo rm -rf "/Applications/Utilities/Adobe Sync/CoreSync/Core Sync.app"

  if is_ci; then
    # Workaround for https://github.com/actions/runner-images/issues/6817
    if is_command_available "2to3"; then
      rm -fv "$(command -v 2to3)"
    fi
  fi

  ###############################################################################
  # Kill affected applications                                                  #
  ###############################################################################

  echo "Killing and restarting affected apps"

  for app in "Activity Monitor" \
    "cfprefsd" \
    "Dock" \
    "Finder" \
    "SystemUIServer"; do
    killall "${app}" >/dev/null 2>&1 || true
  done
  echo "Done. Note that some of these changes require a logout/restart to take effect."
}

setup_shell() {
  echo "Setting up the shell..."

  clone_git_repository_if_not_cloned_already "$(dirname "$ZSH_THEME_PATH")" "https://github.com/romkatv/powerlevel10k.git"

  user_default_shell=
  if is_macos; then
    user_default_shell="$(dscl . -read ~/ UserShell | sed 's/UserShell: //')"
  elif is_linux; then
    user_default_shell="$(awk -F: -v user="${TARGET_USER}" '$1 == user {print $NF}' /etc/passwd)"
    if [ -z "${user_default_shell}" ]; then
      user_default_shell="$(getent passwd "${TARGET_USER}" | awk -F: -v user="${TARGET_USER}" '$1 == user {print $NF}')"
    fi
  fi

  if [ -z "$DEFAULT_SHELL" ]; then
    echo "ERROR: The DEFAULT_SHELL variable is not set, or set to an empty string"
    exit 1
  fi

  if [ -z "${user_default_shell}" ]; then
    echo "ERROR: The user_default_shell variable is not set, or set to an empty string"
    exit 1
  fi

  if [ "${user_default_shell}" != "$DEFAULT_SHELL" ]; then
    echo "Changing default shell from ${user_default_shell} to $DEFAULT_SHELL"
    sudo chsh -s "$DEFAULT_SHELL" "$TARGET_USER"
  fi

  unset user_default_shell
  echo "The default shell for $TARGET_USER is set to $DEFAULT_SHELL"

  if [ -z "$USER_FONTS_DIRECTORY" ]; then
    echo "ERROR: The USER_FONTS_DIRECTORY variable is not set, or set to an empty string"
    exit 1
  fi

  echo "Downloading fonts in ${USER_FONTS_DIRECTORY}"
  mkdir -pv "${USER_FONTS_DIRECTORY}"
  ! [ -e "${USER_FONTS_DIRECTORY}/MesloLGS NF Regular.ttf" ] && curl -fsLo "${USER_FONTS_DIRECTORY}/MesloLGS NF Regular.ttf" https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Regular.ttf
  ! [ -e "${USER_FONTS_DIRECTORY}/MesloLGS NF Bold.ttf" ] && curl -fsLo "${USER_FONTS_DIRECTORY}/MesloLGS NF Bold.ttf" https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold.ttf
  ! [ -e "${USER_FONTS_DIRECTORY}/MesloLGS NF Italic.ttf" ] && curl -fsLo "${USER_FONTS_DIRECTORY}/MesloLGS NF Italic.ttf" https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Italic.ttf
  ! [ -e "${USER_FONTS_DIRECTORY}/MesloLGS NF Bold Italic.ttf" ] && curl -fsLo "${USER_FONTS_DIRECTORY}/MesloLGS NF Bold Italic.ttf" https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold%20Italic.ttf

  if is_default_shell_zsh; then
    echo "Configuring ZSH"
    mkdir -pv "${ZSH_CACHE_DIR}"
  fi

  echo "Shell configuration completed."
}

set_repository_path() {
  SCRIPT_PATH="$0"
  echo "The path to this script is: $SCRIPT_PATH. Checking if it's a link and following it..."

  CURRENT_WORKING_DIRECTORY="$(pwd)"
  echo "The current working directory is $CURRENT_WORKING_DIRECTORY"

  # Cannot use the is_XXXX or other functions defined in functions.sh because they might not be available at this point
  os_name="$(uname -s)"
  if test "${os_name#*"Darwin"}" != "$os_name"; then
    DIR="$(dirname "$SCRIPT_PATH")"
    echo "Changing directory to $DIR..."
    cd "$DIR"
    TARGET_FILE="$(basename "$SCRIPT_PATH")"

    echo "Checking if $TARGET_FILE is a link..."
    # Iterate down a (possible) chain of symlinks
    while [ -L "$TARGET_FILE" ]; do
      LINK_TARGET="$(readlink "$TARGET_FILE")"
      echo "$TARGET_FILE is a link to $LINK_TARGET. Following the link..."

      TARGET_FILE="$LINK_TARGET"
      DIR="$(dirname "$TARGET_FILE")"
      echo "Changing directory to $DIR..."
      cd "$DIR"
      TARGET_FILE="$(basename "$TARGET_FILE")"
      echo "Checking if $TARGET_FILE is a link..."
    done

    unset DIR
    unset LINK_TARGET

    echo "$TARGET_FILE is not a link. Reached the end of the chain."

    # Compute the canonicalized name by finding the physical path
    # for the directory we're in and appending the target file.
    PHYS_DIR="$(pwd -P)"
    echo "The current working directory is: $PHYS_DIR. Using it to build the absolute path to $SCRIPT_PATH"
    ABSOLUTE_SCRIPT_PATH="$PHYS_DIR/$TARGET_FILE"

    unset TARGET_FILE
  elif test "${os_name#*"Linux"}" != "$os_name"; then
    # Use readlink -f directly
    ABSOLUTE_SCRIPT_PATH="$(readlink -f "$0")"
  fi
  unset os_name

  echo "The absolute path to this script is: $ABSOLUTE_SCRIPT_PATH. Using it to build the absolute path to the repository directory..."
  SCRIPT_DIRECTORY="$(dirname "$ABSOLUTE_SCRIPT_PATH")"
  unset ABSOLUTE_SCRIPT_PATH
  echo "The script directory is: $SCRIPT_DIRECTORY. Using it to set the working directory for git..."

  # This is a git repository, so use this fact to get the root of the repository
  REPOSITORY_PATH="$(git -C "$SCRIPT_DIRECTORY" rev-parse --show-toplevel)"
  unset SCRIPT_DIRECTORY
  echo "The repository path is: $REPOSITORY_PATH"

  echo "Going back to the previous working directory: $CURRENT_WORKING_DIRECTORY"
  cd "$CURRENT_WORKING_DIRECTORY"
  unset CURRENT_WORKING_DIRECTORY
}

main() {
  get_user
  ask_for_sudo

  echo "Current working directory: $(pwd)"
  echo "Current home directory: ${HOME}"

  echo "Setting the REPOSITORY_PATH variable..."
  set_repository_path
  if [ -z "$REPOSITORY_PATH" ]; then
    echo "ERROR: The REPOSITORY_PATH variable is empty or not set. Exiting..."
    exit 1
  fi
  echo "Path to the repository: $REPOSITORY_PATH"

  # The source_file_if_available function might not be available, so
  # sourcing the functions files it explicitly
  FUNCTIONS_FILE_ABSOLUTE_PATH="${REPOSITORY_PATH}/.shells/.all/functions.sh"

  # Set the DOCKERFUNCTIONS_PATH because it's not yet available in the default location set in environment.sh
  DOCKERFUNCTIONS_PATH="${REPOSITORY_PATH}"/.shells/.all/dockerfunctions.sh
  export DOCKERFUNCTIONS_PATH

  echo "Sourcing ${FUNCTIONS_FILE_ABSOLUTE_PATH}..."
  if [ -f "${FUNCTIONS_FILE_ABSOLUTE_PATH}" ]; then
    # shellcheck source=/dev/null
    . "${FUNCTIONS_FILE_ABSOLUTE_PATH}"
  else
    echo "ERROR: Cannot find the ${FUNCTIONS_FILE_ABSOLUTE_PATH} file. Exiting..."
    exit 1
  fi
  # From now on, the source_file_if_available function is available

  ENVIRONMENT_FILE_ABSOLUTE_PATH="$REPOSITORY_PATH/.shells/.all/environment.sh"
  echo "Sourcing environment variables configuration file from ${ENVIRONMENT_FILE_ABSOLUTE_PATH}..."
  source_file_if_available "${ENVIRONMENT_FILE_ABSOLUTE_PATH}" "ENVIRONMENT_FILE_ABSOLUTE_PATH"

  if is_linux || is_macos; then
    if is_debian; then
      setup_debian
    elif is_macos; then
      setup_macos
      install_brew
    fi

    echo "Refresh the environment variables from $ENVIRONMENT_FILE_ABSOLUTE_PATH because there could be stale values."
    source_file_if_available "$ENVIRONMENT_FILE_ABSOLUTE_PATH" "ENVIRONMENT_FILE_ABSOLUTE_PATH"

    if is_macos; then
      install_brew_formulae

      echo "Refresh the environment variables from $ENVIRONMENT_FILE_ABSOLUTE_PATH because there could be stale values."
      source_file_if_available "$ENVIRONMENT_FILE_ABSOLUTE_PATH" "ENVIRONMENT_FILE_ABSOLUTE_PATH"
    fi

    setup_shell
    setup_user
    install_dotfiles "${REPOSITORY_PATH}"
    install_vs_code_extensions
    update_dotfiles
  else
    echo "The current OS or distribution is not supported. Terminating..."
    exit 1
  fi
}

main