#!/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,plugdev" # 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")

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

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

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

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

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

	info "Done!"
}

install_packages() {
	#shellcheck disable=SC2016,SC2046
	xbps-install -y $(xargs -a "$1") 2>&1 | grep -q "not found in repository pool." && error "Invalid package in packages.txt, run validate.sh"
	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 "{}" "/{}" \;
	)
	info "Done!"
}

create_user() {
	tput sc

	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
		tput rc
		tput el
	done

	if id -u "$username" >/dev/null 2>&1; then
		warn "User \"$username\" already exists, Skipping user creation!"
		usermod -aG "$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!"
			tput rc
			tput el
			pass1=$(prompt "Input Password: ")
			tput rc
			tput el
			pass2=$(prompt "Repeat Password: ")
			tput rc
			tput el
			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 "$1" \
		'Creating directory ${x}' \
		"mkdir -p $user_home/\${x}"
	info "Done!"
}

install_dotfiles() {
	tput sc
	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
			tput rc
			tput el
			warn "Failed to clone dotfiles"
			return 1
		fi
	fi
	tput rc
	tput el
	info "Symlinking dotfiles..."
	if ! stow -d "$user_home/$STOW_DIR" -t "$user_home" "$DOTS_PACKAGE" 1>/dev/null 2>&1; then
		tput rc
		tput el
		warn "Failed to symlink dotfiles"
		return 2
	fi
	tput rc
	tput el
	info "Done!"
}

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

enable_services() {
	tput sc
	info "Installing user service service..."
	target="/etc/sv/runsvdir-${username}"
	mkdir -p "$target"
	sed "s/<U>/$username/" "${SCRIPT_DIR}/runsvdir-user" >"$target/run"
	[ ! -L "/var/service/$(basename "$target")" ] &&
		ln -s "$target" "/var/service/"
	chmod 755 "$target"
	tput rc
	tput el
	#shellcheck disable=SC2016
	_loop_wrapper "$1" \
		'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" "$user_home"
	info "Done!"
}

### CONTROL FLOW BEGINS HERE ###

check_root

emphasize "-- Copying Files --"
install_files

emphasize "-- Preparing Installation --"
setup

emphasize "-- Installing Packages --"
install_packages "${SCRIPT_DIR}/packages.txt"

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 "${SCRIPT_DIR}/directories.txt"

emphasize "-- Installing Dotfiles --"
install_dotfiles
select_keymap

emphasize "-- Enabling Services --"
enable_services "${SCRIPT_DIR}/services.txt"

emphasize "-- Finalizing Installation --"
finalize

emphasize "-- Installation Complete --"