#!/usr/bin/env bash
#
#   Script name: repo-manager
#   Description: Script for managing repository UBLinux
#   GitLab: https://gitlab.ublinux.ru/
#   Author: asmeron@ublinux.ru
#   Contributors: asmeron@ublinux.ru
#
#   Copyright (c) 2021-2022 UBLinux Development Team <support@ublinux.ru>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.

VERSION_SCRIPT="2.0"

usage() {
  cat <<EOF
Usage: ${0##*/} {COMMAND} [OPTIONS...] 
Version: ${VERSION_SCRIPT}
Manage package for ublinux repository

Package Commands:
  create	Create new path repository
  add		Add a package to the repository db
		If options -p PACKAGE not set, then add all new package to the repository db
  move		Move a package from repository [a] to repository [b]
  remove        Remove a package from the repository db
Meta Commands:
  help          Show this help

Options for all command: 
  -d, --dir    	  [PATH]  	  Path to the repository root
		  PATH=.	  *Default
  -r, --repo   	  [NAME]	  Name of the repository to move packages to
		  NAME=ublinux	  *Default
  -a, --arch	  [ARCH]	  Package architecture in the repository
		  ARCH=x86_64 	  *Default

Options for add, move, remove command:
  -R, --repo-src  [NAME]	  Name of the repository where the packages will be moved from
  -p, --packages  [PACKAGES]	  Package name, possible list via ","
		  PACKAGES="."	  Only command: add. Then all pakages from current directory *.${EXT_MASK}.* are moved to the repository
  not set -p, --packages  	  Then all new pakages from path repository *.${EXT}.* are moved to the repository DB
  -e, --ext	  [EXT]		  Package Archive Extension
		  EXT=pkg.tar.zst *Default
  -s, --sign-pkg  [SIGN]	  Signature of the package builder for the database
  -S, --sign-repo [SIGN]	  Signature of the repository builder for the database
  -f, --force-replace  		  Force to replace the packages, even if it is already in the database
  -h, --help            	  Show this help
  -V, --version                   Show package version
Examples:
repo-manager add -r ublinux --sign-repo ublinux@mail.com
repo-manager add -d /mnt/repository/2204 -r ublinux  --sign-repo ublinux@mail.com --sign-pkg ublinux-package@mail.com
repo-manager add -d /mnt/repository/2204 -r ublinux --sign-repo ublinux@mail.com -p /tmp/package-1/ 
repo-manager move -d /mnt/repository/2204 -R ublinux-test -r ublinux
repo-manager move -d /mnt/repository/2204 -R ublinux-test -r ublinux -p pamac-aur,libpamac-aur
repo-manager remove -d /mnt/repository/2204 -r ublinux-testing -p pamac-tray-icon-plasma,libpamac-aur-11.3.0-0-x86_64.pkg.tar.zst
repo-manager remove -d /mnt/repository/2204 -r ublinux --sign-repo ublinux@mail.com -p systemd-250.3-4-x86_64.pkg.tar.zst
repo-manager remove -d /mnt/repository/2204 -r ublinux --sign-repo ublinux@mail.com -p systemd-resolvconf,systemd-sysvcompat
EOF
exit 0
}

arguments() {
# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
    local ARGV=()
    local END_OF_OPT=
    while [[ $# -gt 0 ]]; do
	arg="$1"; shift
	case "${END_OF_OPT}${arg}" in
	    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
	    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
	    --*) ARGV+=("$arg") ;;
	    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
	    *) ARGV+=("$arg") ;;
	esac
    done
# Apply pre-processed options
    set -- "${ARGV[@]}"
# Parse options
    local END_OF_OPT=
    local POSITIONAL_ARGS=()
    [[ -z $@ ]] && usage && exit 0
    while [[ $# -gt 0 ]]; do
	case "${END_OF_OPT}${1}" in
	    -h | --help | help)	  usage ;;
	    -r | --repo)	  shift; REPO="$1" ;;
	    -R | --repo-src)	  shift; REPO_SRC="$1" ;;
	    -d | --dir)	 	  shift; PATH_ROOT_REPO="$1" ;;
	    -a | --arch)	  shift; ARCH="$1" ;;
	    -p | --packages)      shift; PACKAGES+="$1" ;;
	    -e | --ext)    	  shift; EXT="$1" ;;
	    -s | --sign-pkg) 	  shift; SIGN_PKG="$1" ;;
	    -S | --sign-repo) 	  shift; SIGN_REPO="--sign --key $1" ;;
	    -f | --force-replace) unset ONLYNEW ;; 
	    -q | --quiet)     	  QUIET=1 ;;
	    -V | --version)	  echo "Version: ${VERSION_SCRIPT}"; exit 0 ;;
	    create)    		  COMMAND="create" ;;
	    add)      		  COMMAND="add" ;;
	    move)      		  COMMAND="move" ;;
	    remove)    		  COMMAND="remove" ;;
	    --stdin)        	  READ_STDIN=1 ;;
	    --)             	  END_OF_OPT=1 ;;
	    -*|--*)         	  echo "WARNING: Unrecognized argument, skiped: $1" >&2  ;;
	    *)              	  POSITIONAL_ARGS+=("$1") ;;
	esac
	shift
    done
# Restore positional parameters
    set -- "${POSITIONAL_ARGS[@]}"
    [[ -z ${COMMAND} && -z ${PATH_ROOT_REPO} ]] && usage
}

move_to_repo() {
    local FILES_PKG
    PACKAGES=()
    if [[ -n $1 ]]; then 
	[[ -d $1 ]] && FILES_PKG=$(ls -1 ${1}/*.${EXT} 2>/dev/null) || FILES_PKG=$1 
    else
	FILES_PKG="${PATH_WORK}/*.${EXT_MASK}.*[!sig]"
    fi
    for package_file in ${FILES_PKG//,/ }; do
	package_file="$(realpath ${package_file})"
	[[ ! -f "${package_file}" ]] \
	    && echo "WARNING: not found the package :: ${package_file}" \
	    && continue
	[[ -e "${package_file}.sig" && -z ${ONLYNEW} ]] && rm -f ${package_file}.sig
        [[ ! -e "${package_file}.sig" ]] && sign_package ${package_file}
        if [[ "$(realpath ${package_file})" != "$(realpath ${PATH_DB_REPO}/${package_file##*/})" ]] \
           && [[ -z ${ONLYNEW} || ! -f "${PATH_ROOT_REPO}/pool/overlay/${package_file##*/}" ]]; then
    	    echo "INFO: copy to repository the package :: ${package_file##*/}"
    	    cp ${package_file} ${PATH_ROOT_REPO}/pool/overlay/
	    ln -fsr ${PATH_ROOT_REPO}/pool/overlay/${package_file##*/} ${PATH_DB_REPO}/${package_file##*/}
    	    cp ${package_file}.sig ${PATH_ROOT_REPO}/pool/overlay/
	    ln -fsr ${PATH_ROOT_REPO}/pool/overlay/${package_file##*/}.sig ${PATH_DB_REPO}/${package_file##*/}.sig
	else 
	    echo "INFO: package allready exist in repository, skip copy the package :: ${package_file##*/}"
	fi
	PACKAGES="${PACKAGES} ${PATH_DB_REPO}/${package_file##*/}"
    done
}

sign_package() {
    if [[ ! -f "$1" ]]; then
	echo "ERROR: Provide a package for signed and try again."
	exit 1
    else
	# file="$1"
	# touch -amht '198101010000' systemd-250.3-4-x86_64.pkg.tar.zst
	gpg --detach-sign --local-user ${SIGN_PKG} "$1"
	touch -am -r "$1" "$1.sig"
    fi
}

trim_name_pkg() {
    local TRIM_PACKAGES
    if [[ ${PACKAGES} =~ ${EXT_MASK} ]]; then
        for ITEM in ${PACKAGES//,/ }; do
    	    TRIM_PACKAGES="${TRIM_PACKAGES} ${ITEM%*-*-*-*.${EXT_MASK}.*}"
	done
	PACKAGES=${TRIM_PACKAGES}
    fi
}

##################
#####	MAIN
##################

    PKGNAME=${0##*/}
    PATH_WORK=${PWD}
    PACKAGES=()
    SIGN_REPO=()
    SIGN_PKG=()
    PATH_ROOT_REPO="."
    ARCH="x86_64"
    EXT_MASK="pkg.tar"
    EXT="pkg.tar.zst"
    REPO="ublinux"
    # if null ${ONLYNEW} then reset and new DB
    ONLYNEW="--new"

    arguments $@
    PATH_DB_REPO="${PATH_ROOT_REPO}/${REPO}/${ARCH}"
    [[ ! -d ${PATH_DB_REPO} ]] && echo "WARNING: path to the repository root not exist (arg: -d, --dir), created ./ !" && mkdir -p ${PATH_DB_REPO} ${PATH_ROOT_REPO}/pool/overlay/

    if [[ ${COMMAND} == "create" ]]; then
	true
    elif [[ ${COMMAND} == "add" ]]; then
	if [[ -z ${PACKAGES} ]]; then 
	    echo "INFO: add all new packages to '${REPO}' repository"
	    repo-add --verify ${ONLYNEW} --prevent-downgrade ${SIGN_REPO} ${PATH_DB_REPO}/${REPO}.db.tar.gz ${PATH_DB_REPO}/*.${EXT_MASK}.*[!sig]
	else
	    [[ ${PACKAGES} == "." ]] && move_to_repo || move_to_repo ${PACKAGES}
	    if [[ -n ${PACKAGES} ]]; then
		echo -e "INFO: add assigned packages to '${REPO}' repository :: ${PACKAGES//,/ }"
		repo-add --verify ${ONLYNEW} --prevent-downgrade ${SIGN_REPO} ${PATH_DB_REPO}/${REPO}.db.tar.gz ${PACKAGES//,/ }
	    else
		echo "INFO: no packages"
	    fi
	fi
    elif [[ ${COMMAND} == "move" ]]; then
	[[ -z ${REPO_SRC} ]] && echo "WARNING: the source repository is not set!" && exit 1
	PATH_DB_REPO_SRC="${PATH_ROOT_REPO}/${REPO_SRC}/${ARCH}"
	if [[ ${PACKAGES} == "." ]]; then 
	    PACKAGES=$(ls -1 *.${EXT_MASK}.*[!sig] 2>/dev/null)
	    [[ -z ${PACKAGES} ]] && echo "ERROR: packages from '${REPO_SRC}' repository not found !" && exit 1
	elif [[ -z ${PACKAGES} ]]; then 
	    echo "INFO: move all packages from '${REPO_SRC}' to '${REPO}' repository"
	    PACKAGES=$(cd ${PATH_DB_REPO_SRC}/  2>/dev/null && ls -1 *.${EXT_MASK}.*[!sig] 2>/dev/null)
	    [[ -z ${PACKAGES} ]] && echo "ERROR: packages from '${REPO_SRC}' repository not found !" && exit 1
	fi
	echo -e "INFO: add assigned packages to '${REPO}' repository :: \n${PACKAGES//,/\\n==> }\n"
	mv -f -t ${PATH_DB_REPO} $(echo -e "${PACKAGES//,/\\n}" | sed "s|^|${PATH_DB_REPO_SRC}/|g") $(echo -e "${PACKAGES//,/\\n}" | sed "s|^|${PATH_DB_REPO_SRC}/|g; s|$|.sig|g")
	cd ${PATH_DB_REPO}
	repo-add --verify ${ONLYNEW} --prevent-downgrade ${SIGN_REPO} ${PATH_DB_REPO}/${REPO}.db.tar.gz ${PACKAGES//,/ }
	trim_name_pkg
	echo "INFO: Remove packages from current db ::${PACKAGES//,/ }"
	repo-remove --verify ${SIGN_REPO} ${PATH_DB_REPO_SRC}/${REPO_SRC}.db.tar.gz ${PACKAGES//,/ }
    elif [[ ${COMMAND} == "remove" ]]; then
	[[ -z ${PACKAGES} ]] && echo "WARNING: remove packages not set!" && exit 1
	trim_name_pkg
	echo -e "INFO: Remove packages from current db :: ${PACKAGES//,/ }"
        for ITEM in ${PACKAGES//,/ }; do
    	    rm -f ${PATH_DB_REPO}/${ITEM}-[0-9]*-*-*.${EXT_MASK}.*
	done
	repo-remove --verify ${SIGN_REPO} ${PATH_DB_REPO}/${REPO}.db.tar.gz ${PACKAGES//,/ }
    else
	usage
    fi
