#!/bin/sh
DOTS_REPO="https://git.snaile.de/luca/dotfiles"
DOTS_BRANCH="main"
STOW_DIR=".local/share/stow"
DOTS_PACKAGE="dots"

USER_GROUPS="wheel,floppy,audio,video,cdrom,optical,kvm,xbuilder,users,docker" # Comma separated list
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"

BOLD="$(tput bold)"
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
YELLOW="$(tput setaf 3)"
BLUE="$(tput setaf 4)"
RESET="$(tput sgr0)"

error() {
    printf "%b\n" "${RED}${BOLD}${1}${RESET}" >&2
    exit 1
}

prompt() {
    message=$1
    printf "%b" "${BLUE}${message}${RESET}" >"$(tty)"
    # shellcheck disable=SC3037,SC2046
    read -r x
    echo "$x"
    unset x
}

emphasize() {
    printf "%b\n" "${GREEN}${BOLD}${1}${RESET}"
}

info() {
    printf "%b\n" "${1}"
}

warn() {
    printf "%b\n" "${YELLOW}${BOLD}${1}${RESET}"
}

_loop_wrapper() {
    unset n
    unset total
    file=$1
    message=$2
    command=$3

    skip_regex="^(#.*)?$"
    total=$(grep -cvP "$skip_regex" "$file")

    while read -r x; do
        echo "$x" | grep -qvP "$skip_regex" || continue
        n=$((n + 1))
        eval "info \"(${n}/${total}) $message\""
        eval "$command"
    done <"$file"
}

check_root() {
    [ "$(id -u)" != "0" ] &&
        error "This script needs root!"
}

setup() {
    info "Synchronizing XBPS index..."
    xbps-install -S xbps void-repo-nonfree >/dev/null 2>&1 || error "Failed to synchronize XBPS index! (Try manually running xbps-install -S)"

    if ! xbps-query ntp >/dev/null 2>&1; then
        info "Installing ntp..."
        xbps-install -y ntp >/dev/null 2>&1

        info "Synchronizing time..."
        ntpdate "pool.ntp.org" >/dev/null 2>&1 || warn "Failed to synchronize time!"
    fi

    info "Done!"
}

install_packages() {
    #shellcheck disable=SC2016,SC2046
    { sed '/^[[:space:]]*#/d;/^$/d' "${SCRIPT_DIR}/packages.txt" | xargs -r xbps-install -y || error "Invalid package in packages.txt"; } | grep -v 'already installed'
    info "Done!"
    command -v git 1>/dev/null 2>&1 || error "git isn't installed even though it should be!"
    command -v stow 1>/dev/null 2>&1 || error "stow isn't installed even though it should be!"
}

install_files() {
    (
        cd "${SCRIPT_DIR}/files" || exit 1
        find . -type f,l -exec install -Dm 644 -o root -g root "{}" "/{}" \;
    )
    info "Done!"
}

create_symlinks() {
    #shellcheck disable=SC2016
    _loop_wrapper "${SCRIPT_DIR}/symlinks.txt" \
        'Creating symlink $(echo $x | cut -d',' -f1) -> $(echo $x | cut -d',' -f2)' \
        '
            source=$(echo $x | cut -d"," -f2)
            target=$(echo $x | cut -d"," -f1)
			[ -L $target ] && rm $target
			ln -s $source $target
		'

}

create_user() {
    failed=false
    while ! echo "$username" | grep "^[a-z_][a-z0-9_-]*$" | grep -qv "root"; do
        $failed && warn "Invalid username, try again!"
        username=$(prompt "Input Username: ")
        failed=true
    done

    if id -u "$username" >/dev/null 2>&1; then
        warn "User \"$username\" already exists, Skipping user creation!"
        usermod -G "$USER_GROUPS" "$username"
    else
        info "Creating user \"$username\" with the following groups: \"$USER_GROUPS\"..."
        useradd -m -G "$USER_GROUPS" "$username"
        failed=false
        while [ -z "$pass1" ] || [ "$pass1" != "$pass2" ]; do
            $failed && warn "Passwords do not match or are empty, try again!"
            pass1=$(prompt "Input Password: ")
            pass2=$(prompt "Repeat Password: ")
            failed=true
        done
        echo "$username:$pass1" | chpasswd
    fi

    user_home=$(getent passwd "$username" | cut -d ':' -f 6)
    [ -z "$username" ] &&
        error "\$username variable is empty, this script is bugged!"
    [ -z "$user_home" ] &&
        error "\$user_home variable is empty, this script is bugged!"
    sudo -u "$username" [ -w "$user_home" ] || error "$username can't write to '$user_home'!"

    info "Done!"
}

create_directories() {
    #shellcheck disable=SC2016
    _loop_wrapper "${SCRIPT_DIR}/directories.txt" \
        'Creating directory $(echo $x | cut -d"," -f1)' \
        '
            dir=$(echo $x | cut -d"," -f1)
            mod=$(echo $x | cut -d"," -f2)
            sudo -u "$username" mkdir -p "${user_home}/${dir}"
            chmod "${mod}" "${user_home}/${dir}"
        '
    info "Done!"
}

install_dotfiles() {
    info "Cloning dotfiles..."
    mkdir -p "${user_home}/${STOW_DIR}"
    if [ ! -d "${user_home}/${STOW_DIR}/${DOTS_PACKAGE}/.git" ]; then
        if ! git -C "${user_home}/${STOW_DIR}" clone -q --recurse-submodules -b "$DOTS_BRANCH" "$DOTS_REPO" "$DOTS_PACKAGE"; then
            warn "Failed to clone dotfiles"
            return 1
        fi
    fi
    info "Symlinking dotfiles..."
    if ! stow -d "$user_home/$STOW_DIR" -t "$user_home" "$DOTS_PACKAGE" 1>/dev/null 2>&1; then
        warn "Failed to symlink dotfiles"
        return 2
    fi
    info "Done!"
}

select_keymap() {
    [ -L "${user_home}/.local/share/xkb/compiled/keymap" ] && return
    map="$(find "${user_home}/.local/share/xkb/compiled" -type f -printf "%f\n" | fzf --header="Select a keymap keymap:")"
    ln -s "$map" "${user_home}/.local/share/xkb/compiled/keymap"
}

enable_services() {
    for sv in /var/service/*; do
        grep -qx "$(basename "$sv")" "${SCRIPT_DIR}/services.txt" || rm "$sv"
    done
    # shellcheck disable=2016
    _loop_wrapper "${SCRIPT_DIR}/services.txt" \
        'Enabling ${x} service' \
        '[ ! -L /var/service/${x} ] && ln -s "/etc/sv/${x}" "/var/service/"'
    info "Done!"
}

finalize() {
    gid=$(getent passwd "$username" | cut -d ':' -f 4)
    groupname=$(getent group "$gid" | cut -d ':' -f 1)
    chown "$username:$groupname" -R "$user_home"
    info "Done!"
}

### CONTROL FLOW BEGINS HERE ###

check_root

emphasize "-- Copying Files --"
install_files

emphasize "-- Creating Symlinks --"
create_symlinks

emphasize "-- Preparing Installation --"
setup

emphasize "-- Installing Packages --"
install_packages

username="$SUDO_USER"
if [ -z "$username" ]; then
    emphasize "-- Creating User Account --"
    create_user
else
    user_home=$(getent passwd "$username" | cut -d ':' -f 6)
fi

emphasize "-- Creating Standard Home Directories --"
create_directories

emphasize "-- Installing Dotfiles --"
install_dotfiles
select_keymap

emphasize "-- Enabling Services --"
enable_services

emphasize "-- Finalizing Installation --"
finalize

emphasize "-- Installation Complete --"