#!/bin/sh
# apkg-bin - a simple binary package manager helper script

REMOTE="${REMOTE:-https://emmett1.my/pub/alice/packages}"
PKGDIR="${PKGDIR:-/var/cache/apkg-bin}"
REPODB="${REPODB:-REPO}"
DBFILE="$PKGDIR/$REPODB"

usage() {
	echo "Usage: $0 [options]"
	echo "  g <path>     Generate database from package dir"
	echo "  S            Sync database from remote server"
	echo "  l            List packages"
	echo "  s <pattern>  Search packages"
	echo "  a <pkg>      Show package info"
	echo "  d <pkg...>   Download package(s)"
	echo "  i <pkg...>   Install package(s)"
	echo "  u <pkg...>   Upgrade/reinstall package(s)"
	echo "  c            Clean mismatched/unknown packages"
	exit 0
}

msg() {
	m=${pkg%#*}
	echo "[${m:-...}] $*"
}

pkg_existence() {
	need_db
	[ "$(cut -d '#' -f1 "$DBFILE" | grep -x "$1" | uniq)" ] || return 1
	return 0
}

get_pkg() {
	for p in $@; do
		pkg_existence $p && pkg="$pkg $p" || msg "No package found matching: $p"
	done
}

get_entry() {
	entry=$(grep ^$1# "$DBFILE" | sort -V -t'#' -k2 | tail -n1)
}

need_db() {
	[ -f "$DBFILE" ] || { msg "Run '$0 S' first to sync database."; exit 1; }
}

need_root() {
	[ $(id -u) = 0 ] || { msg "This option need root access."; exit 1; }
}

fetch_pkg() {
	get_entry $1
	expected_sha=$(echo "$entry" | cut -d'|' -f4)
	pkg=$(echo "$entry" | cut -d'|' -f1)

	mkdir -p "$PKGDIR"
	if [ -f "$PKGDIR/$pkg" ]; then
		msg "$PKGDIR/$pkg found."
	else
		url="$REMOTE/$(echo "$pkg" | sed 's/#/%23/g')"
		msg "Downloading $url..."
		curl -fsSLo "$PKGDIR/$pkg" "$url" || { msg "Download failed: $pkg" >&2; return 1; }
	fi

	actual_sha=$(sha256sum "$PKGDIR/$pkg" | awk '{print $1}')
	if [ "$expected_sha" = "$actual_sha" ]; then
		msg "Verification PASSED: $pkg"
	else
		msg "Verification FAILED: $pkg" >&2
		rm -f "$pkg"
		return 1
	fi
	
	if [ "$mode" ]; then
		spm $mode "$PKGDIR/$pkg"
	fi
}

generate_db() {
	[ -d "$1" ] || { msg "Invalid path: $1"; exit 1; }
	> "$1/$REPODB"
	msg "Generating database, please wait..."
	for f in "$1"/*.spm; do
		[ -f "$f" ] || continue
		size=$(stat -c%s "$f")
		mtime=$(stat -c%Y "$f")
		sha=$(sha256sum "$f" | awk '{print $1}')
		name=$(basename "$f")
		echo "$name|$size|$mtime|$sha" >> "$1/$REPODB"
	done
	msg "Database generated at $1/$REPODB"
}

sync_db() {
	need_root
	mkdir -p "${DBFILE%/*}"
	msg "Downloading database $REMOTE/$REPODB"
	curl -fsSo "$DBFILE" "$REMOTE/$REPODB" || { msg "Failed to sync $REPODB"; exit 1; }
	msg "Database synced to $DBFILE"
}

list_pkg() {
	need_db
	cut -d'|' -f1 "$DBFILE" | sed 's/#/ /g;s/.spm//g'
}

search_pkg() {
	need_db
	cut -d '|' -f1 "$DBFILE" | grep -i "$1" | sed 's/#/ /g;s/.spm//g'
	exit 0
}

info_pkg() {
	need_db
	get_pkg $@	
	for p in $pkg; do
		get_entry $p
		name=$p
		verrel=$(echo "$entry" | cut -d '#' -f2 | sed 's/\.spm.*//')
		size=$(echo "$entry"   | cut -d '|' -f2)
		mtime=$(echo "$entry"  | cut -d '|' -f3)
		sha=$(echo "$entry"	| cut -d '|' -f4)

		# convert size
		if [ "$size" -lt 1024 ]; then
			hsize="${size} B"
		elif [ "$size" -lt 1048576 ]; then
			hsize="$(awk "BEGIN{printf \"%.1f\", $size/1024}") KB"
		else
			hsize="$(awk "BEGIN{printf \"%.1f\", $size/1048576}") MB"
		fi

		# convert date
		hdate=$(date -d @"$mtime" '+%Y-%m-%d %H:%M:%S')

		echo "Package: $name"
		echo "Version: $verrel"
		echo "Size:	$hsize"
		echo "Date:	$hdate"
		echo "SHA256:  $sha"
	done
}

download_pkg() {
	need_db
	get_pkg $@
	for p in $pkg; do
		fetch_pkg $p
	done
}

install_pkg() {
	need_root
	need_db
	get_pkg $@
	for p in $pkg; do
		[ "$(spm -a | grep -x $p)" ] && {
			msg "Package '$p' is installed, skipped."
		} || {
			pkg2="$pkg2 $p"
		}
	done
	for p in $pkg2; do
		mode='-i' fetch_pkg $p
	done
}

upgrade_pkg() {
	need_root
	need_db
	get_pkg $@
	for p in $pkg; do
		[ "$(spm -a | grep -x $p)" ] || {
			msg "Package '$p' not installed, skipped."
		} && {
			pkg2="$pkg2 $p"
		}
	done
	for p in $pkg2; do
		mode='-u' fetch_pkg $p
	done
}

clean_pkg() {
	need_db
	need_root
	msg "Checking local packages in $PKGDIR..."
	known_pkgs=$(cut -d '|' -f1 "$DBFILE")
	for f in "$PKGDIR"/*.spm; do
		[ -f "$f" ] || continue
		fname=${f##*/}
		if echo "$known_pkgs" | grep -qx "$fname"; then
			entry=$(grep "^$fname|" "$DBFILE")
			expected_size=$(echo "$entry" | cut -d'|' -f2)
			expected_sha=$(echo "$entry" | cut -d'|' -f4)
			actual_size=$(stat -c%s "$f")
			actual_sha=$(sha256sum "$f" | awk '{print $1}')
			if [ "$expected_size" != "$actual_size" ] || [ "$expected_sha" != "$actual_sha" ]; then
				msg "Mismatch: $fname (removed)"
				rm -f "$f"
			fi
		else
			msg "Unknown package: $fname (removed)"
			rm -f "$f"
		fi
	done
	msg "Cleanup done."
}

main() {
	if [ ! "$1" ]; then
		usage
	else
		case $1 in
			g) generate_db $2 ;;
			S) sync_db ;;
			l) list_pkg ;;
			s) search_pkg $2 ;;
			a) shift; info_pkg $@ ;;
			d) shift; download_pkg $@ ;;
			i) shift; install_pkg $@ ;;
			u) shift; upgrade_pkg $@ ;;
			c) clean_pkg ;;
			*) usage ;;
		esac
		exit 0
	fi
}

main $@

exit 0
