#!/usr/bin/env zsh
# WHAT IS THIS

{ # Wrap the entire program in a braced block so it won't run at all if you do curl | zsh and the connection drops.
global_ignore=(
	'^\.git.*'
	'README\.md'
	'\.editorconfig'
	'\.stow-no-folding'
	'\.mypy_cache'
	'\..*\.swp'
)

supported_stow_versions=(
	2.3.2-fixbug56727
)

call-git() {
	git $argv ${VERBOSE:+-v}
}

call-gnu-stow() {
	$STOW --dotfiles -d $DOTFILES -t ~ --ignore=${^global_ignore} ${VERBOSE:+-vvv} $argv
}

do-bootstrap() {
	echo 'Bootstrapping your dotfiles...' >&2
	mkdir -p $DOTFILES
	touch $DOTFILES/.stow
	if (( $+commands[stow] )); then
		local stow_version=${"$(stow -V)"#stow*version }
		if (( 0 == $supported_stow_versions[(Ie)$stow_version] )); then
			echo "GNU Stow is installed, but version $stow_version is bugged." >&2
			echo 'See https://github.com/aspiers/stow/issues/33 for info.' >&2
			echo 'Will fetch patched version of Stow...' >&2
			install-custom-gnu-stow || return $?
		else
			echo "GNU Stow version $stow_version is installed and is compatible with dots." >&2
		fi
	else
		echo 'GNU Stow is not installed, fetching it...' >&2
		install-custom-gnu-stow || return $?
	fi

	do-clone dots git vim zsh || return $?
  do-stow
}

install-custom-gnu-stow() {
	clone-one stow || return $?
	STOW=$DOTFILES/stow/dot-local/bin/stow
}

do-clone() {
	echo "Requested packages: $argv" >&2
	for package in $argv; do
		clone-one $package || return $?
	done
}

clone-one() {
	local url=$1
	local package=${${url##*/}%.git}
	# Simple package names are fetched from the configured source prefix.
	[[ $url != */* ]] && url=$DOTS_SOURCE_PREFIX/$package
	# user/repo packages are fetched from that user's GitHub repos.
	[[ $url != *:* ]] && url=https://github.com/$url
	if [[ -d $DOTFILES/$package ]]; then
		echo "Looks like you already have $package cloned." >&2
		return 1
	fi
	echo "Retrieving $package from $url now..." >&2
	call-git clone $url $DOTFILES/$package
}

do-stow() {
	local -a packages
	packages=($argv)
	if (( $#packages == 0 )); then
		packages=( $DOTFILES/*(N:t) )
		if (( $#packages == 0 )); then
			echo "No installed packages! Do you want to clone some first?" >&2
			return 1
		fi
		echo "Stowing all packages ($packages) into $HOME now..." >&2
	else
		echo "Stowing $packages into $HOME now..." >&2
	fi

	for nofold in $DOTFILES/${^packages}/.stow-no-folding(N); process-stow-no-folding $nofold
	call-gnu-stow -S $packages
}

do-unstow() {
	local -a packages
	packages=($argv)
	if (( $#packages == 0 )); then
		packages=( $DOTFILES/*(N:t) )
		if (( $#packages == 0 )); then
			echo "No installed packages! There's nothing to unstow!" >&2
			return 1
		fi
		echo "Unstowing all packages ($packages) from $HOME now..." >&2
	else
		echo "Unstowing $packages from $HOME now..." >&2
	fi

	call-gnu-stow -D $packages
}

do-restow() {
	local -a packages
	packages=($argv)
	if (( $#packages == 0 )); then
		packages=( $DOTFILES/*(N:t) )
		if (( $#packages == 0 )); then
			echo "No installed packages! Do you want to clone some first?" >&2
			return 1
		fi
		echo "Restowing all packages ($packages) into $HOME now..." >&2
	else
		echo "Restowing $packages into $HOME now..." >&2
	fi

	for nofold in $DOTFILES/${^packages}/.stow-no-folding(N); process-stow-no-folding $nofold
	call-gnu-stow -R $packages
}

process-stow-no-folding() {
	zmodload zsh/mapfile
	for file in ${(f)mapfile[$1]}; do
		file=~/$file
		[[ -e $file ]] && continue
		mkdir -p ${file:h}
		touch $file
	done
}

do-fetch() {
	local -a packages
	packages=($argv)
	(( $#packages == 0 )) && packages=( $DOTFILES/*(N:t) )
	for p in $packages; do
		cd $DOTFILES/$p
		call-git fetch -p || return $?
	done
}

do-pull() {
	local -a packages
	packages=($argv)
	(( $#packages == 0 )) && packages=( $DOTFILES/*(N:t) )
	for p in $packages; do
		cd $DOTFILES/$p
		call-git pull || return $?
	done
}


do-status() {
	local -a packages
	packages=($argv)
	(( $#packages == 0 )) && packages=( $DOTFILES/*(N:t) )
	local length=${#${(O@)packages//?/X}[1]} git_status remote_status
	for p in $packages; do
		cd $DOTFILES/$p
		# Always show the repo's name at the beginning of the line.
		printf %${length}s'  ' $p

		# Check if the package is actually a repo. It might not be if it's brand-new.
		git_status="$(call-git status --porcelain --ignore-submodules -unormal 2>/dev/null)"
		if (( $? != 0 )); then
			printf '%9s' '' # space across to the last column
			print -P %F{247}not a git repository%f
			continue
		fi

		# Indicate the working tree status.
		if [[ -z $git_status ]]; then
			print -Pn %F{green}clean%f'  '
		else
			print -Pn %F{red}unclean%f
		fi
		printf '%2s' ''

		# If the repository has an upstream remote configured, indicate the number
		# of commits out of sync we are from the remote.
		if git rev-parse --abbrev-ref @{u} &>/dev/null; then
			remote_status="$(git rev-list --left-right --count HEAD...@{u} 2>/dev/null)"
			remote_status=(${(ps:\t:)remote_status})
			print -Pn %F{cyan}
			(( $remote_status[1] > 0 )) && print -n $remote_status[1]⇡
			(( $remote_status[2] > 0 )) && print -n $remote_status[2]⇣
			print -P %f
		else
			print -P %F{247}no remote%f
		fi
	done
}

main() {
	: ${DOTFILES:=~/dotfiles} ${STOW:=stow} ${DOTS_SOURCE_PREFIX:=https://git.00dani.me/dot}

	local opt_index=$argv[(I)(-v|--verbose)]
	if (( opt_index != 0 )); then
		VERBOSE=yes
		argv[opt_index]=()
	fi

	comm=$1
	if (( $# == 0 )); then
		if [[ -d $DOTFILES ]]; then
			comm=status
		else
			comm=bootstrap
		fi
	fi

	case $comm in
		bootstrap) do-bootstrap ;;
		clone) do-clone ${argv[2,-1]} ;;
		fetch) do-fetch ${argv[2,-1]} ;;
		stow) do-stow ${argv[2,-1]} ;;
		unstow) do-unstow ${argv[2,-1]} ;;
		restow) do-restow ${argv[2,-1]} ;;
		pull) do-pull ${argv[2,-1]} ;;
		st|status) do-status ${argv[2,-1]} ;;
		*) echo "Unknown subcommand $comm" >&2; return 2 ;;
	esac
}

main "$@"
}