#!/usr/bin/env bash

declare -A iniConfig
configDefault=$(cat <<EOF
[general]
pathFrom = 
pathTo = 
otherBackPath = 
dateChooseBackup = 

[distribSett]
bootloader = false
sysKernel = false
basicModule = false
additSysModule = false

[userDataSett]
sysChanges = false
userProf = false
customModAndUpd = false
tmpSysChange = false
macSysChange = false
cacheModule = false
notLoadSysModule = false
sysSettings = false

[addition]
runSettAfterDone = false
secureRecover = false
pathToNewIniFile = 

EOF
)

path_root="/memory/data/from/0"
ublinux_path_root="$path_root/ublinux"
ublinux_data_path_root="$path_root/ublinux-data"

read_conf_file() {
    [[ -f $1 ]] && local configTxt=$(cat $1) || local configTxt=$1
    if [[ -n $2 ]]; then
	local -n arrayConfig=$2
	local sep="."
	while IFS='=' read -r key value; do
		arrayConfig[${key}]=${value}
	done < <(printf "%s\n" "${configTxt}" |
		awk -v sep="${sep}" '/^\[/{ app=substr($0,2,length-2) }
				    /=/ { sub(/^[ \t]+/,"");
				    sub(/[ \t]+=[ \t]+/,"=");
				   if (( $0 !~ /^[#;/]/ ) && ( app != /""/ ))
				    {print app sep $0}
				    }')
    fi
}
export -f read_conf_file

write_conf_file() {
    local -n arrayConfig=$2
    [[ ! -f $1 ]] && echo "$(i18n "ERROR: file \"%s\" not found!" $1)" && exit 1
    local sep="."
    for key in "${!arrayConfig[@]}"; do
        if [[ ${key} == *${sep}* ]]; then
            sed -i "/^\[${key%%${sep}*}]/,/^\[.*]/{s|^[ \t]\+||; s|[ \t]\+=[ \t]\+|=|; s|^${key#*${sep}}=.*|${key#*${sep}}=${arrayConfig[$key]}|}" "${1}"
        else
            sed -i "/^\[$2]/,/^\[.*]/{s/^[ \t]\+//; s/[ \t]\+=[ \t]\+/=/; s/^${key}=.*/${key}=${arrayConfig[$key]}/}" "${1}"
        fi
    done
}
export -f write_conf_file

get_list_part(){
    declare -a listParts
    while read -r line; do
        listParts+=("${line}")
    done < <(lsblk -rbo maj:min,path,type,size,mountpoint | grep "part" | sort -nk1 -nk2 | awk '{print $2, $4, $5}')

    for i in "${listParts[@]}"; do
        echo "$i"
    done
}
export -f get_list_part

create_conf(){
    tmpConfPath=$(mktemp -p /dev/shm)
    echo "${configDefault}" > "$tmpConfPath"
    echo "$tmpConfPath"
}
export -f create_conf

save_boot(){
    rsync -aAXruR "$path_root/boot" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_boot

save_kernel(){
    rsync -aAXruR "$ublinux_path_root/vmlinuz-"* "$1" && errCode0=0 || errCode0=1
    rsync -aAXruR "$ublinux_path_root/ublinux-"* "$1" && errCode1=0 || errCode1=1
    [ "$errCode0" == 0 ] && [ "$errCode1" == "0" ] && return 0 || return 1
}
export -f save_kernel

save_base_module(){
    rsync -aAXruR "$ublinux_path_root/base" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_base_module

save_addit_module_sys(){
    rsync -aAXruR "$ublinux_path_root/modules" "$1" && errCode0=0 || errCode0=1
    rsync -aAXruR "$ublinux_path_root/machines" "$1" && errCode1=0 || errCode1=1
    rsync -aAXruR "$ublinux_path_root/optional" "$1" && errCode2=0 || errCode2=1
    [ "$errCode0" == 0 ] && [ "$errCode1" == "0" ] [ "$errCode2" == "0" ] && return 0 || return 1
}
export -f save_addit_module_sys

save_changes(){
    rsync -aAXruR "$ublinux_data_path_root/changes" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_changes

save_profile_user(){
    rsync -aAXruR "$ublinux_data_path_root/homes" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_profile_user

save_user_module_and_updates(){
    rsync -aAXruR "$ublinux_data_path_root/modules" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_user_module_and_updates

save_tmp_changes(){
    rsync -aAXruR "$ublinux_data_path_root/rootcopy" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_tmp_changes

save_sett_sys(){
    rsync -aAXruR "$ublinux_data_path_root/ublinux.ini" "$1" && errCode=0 || errCode=1
    [ "$errCode" == 0 ] && return 0 || return 1
}
export -f save_sett_sys


start_create_backup(){
    declare -A config
    read_conf_file "$argPathConfFile" "config"
    local oldPath="$(pwd)"
    local srcPath="${config["general.pathFrom"]}"
    local destPath="${config["general.pathTo"]}"
    local pathMount="/mnt/recover"
    local tmpDirRecover="ubbackup-$(date '+%Y-%m-%d')"
    local destMount="/mnt/backup.part"
    local chekNewDiskSrc="false"
    local chekNewDiskDest="false"
    local tmpPathRecover=""


    if [ -z "$(mount | grep "$srcPath" )" ]; then
        mkdir -p "$pathMount" &>/dev/null
        mount "$srcPath" "$pathMount"
        chekNewDiskSrc="true"
        if [ -d "$destPath" ]; then
            mkdir -p "${destPath}/${tmpDirRecover}" &>/dev/null
            tmpPathRecover="${destPath}/${tmpDirRecover}"
        elif [ -b "$destPath" ]; then
            if [ -n "$(lsblk -ro path,fstype | grep "$destPath" | awk '{print $2}')" ]; then
                mkdir -p "${destMount}" &>/dev/null
                mount "$destPath" "$destMount" &>/dev/null
                rm -rf "${destMount}/ubbackup" &>/dev/null
                mkdir -p "${destMount}/ubbackup/${tmpDirRecover}" &>/dev/null
                tmpPathRecover="${destMount}/ubbackup/${tmpDirRecover}"
            else
                mkfs.ext4 -L '' "$destPath" &>/dev/null
                mkdir -p "${destMount}" &>/dev/null
                mount "$destPath" "$destMount" &>/dev/null
                rm -rf "${destMount}/ubbackup" &>/dev/null
		        mkdir -p "${destMount}/ubbackup/${tmpDirRecover}" &>/dev/null
                tmpPathRecover="${destMount}/ubbackup/${tmpDirRecover}"
            fi
            chekNewDiskDest="true"
        fi
    else
        if [ -d "$destPath" ]; then
            mkdir -p "${destPath}/${tmpDirRecover}" &>/dev/null
            tmpPathRecover="${destPath}/${tmpDirRecover}"
        elif [ -b "$destPath" ]; then
            if [ -n "$(lsblk -ro path,fstype | grep "$destPath" | awk '{print $2}')" ]; then
                mkdir -p "${destMount}" &>/dev/null
                mount "$destPath" "$destMount" &>/dev/null
                rm -rf "${destMount}/ubbackup" &>/dev/null
                mkdir -p "${destMount}/ubbackup/${tmpDirRecover}" &>/dev/null
                tmpPathRecover="${destMount}/ubbackup/${tmpDirRecover}"
            else
                mkfs.ext4 -L '' "$destPath" &>/dev/null
                mkdir -p "${destMount}" &>/dev/null
                mount "$destPath" "$destMount" &>/dev/null
                rm -rf "${destMount}/ubbackup" &>/dev/null
		        mkdir -p "${destMount}/ubbackup/${tmpDirRecover}" &>/dev/null
                tmpPathRecover="${destMount}/ubbackup/${tmpDirRecover}"
            fi
            chekNewDiskDest="true"
        fi
    fi


    [ "${config["distribSett.bootloader"]}" == "true" ] && ( save_boot "$tmpPathRecover" || return 1 )
    [ "${config["distribSett.sysKernel"]}" == "true" ] && ( save_kernel "$tmpPathRecover" || return 1 )
    [ "${config["distribSett.basicModule"]}" == "true" ] && ( save_base_module "$tmpPathRecover" || return 1 )
    [ "${config["distribSett.additSysModule"]}" == "true" ] && ( save_addit_module_sys "$tmpPathRecover" || return 1 )

    [ "${config["userDataSett.sysChanges"]}" == "true" ] && ( save_changes "$tmpPathRecover" || return 1 )
    [ "${config["userDataSett.userProf"]}" == "true" ] && ( save_profile_user "$tmpPathRecover" || return 1 )
    [ "${config["userDataSett.customModAndUpd"]}" == "true" ] && ( save_user_module_and_updates "$tmpPathRecover" || return 1 )
    [ "${config["userDataSett.tmpSysChange"]}" == "true" ] && ( save_tmp_changes "$tmpPathRecover" || return 1 )
    [ "${config["userDataSett.sysSettings"]}" == "true" ] && ( save_sett_sys "$tmpPathRecover" || return 1 )

    cd "${tmpPathRecover}"
    tar -cf - * | zstd -19 -T0 -o ../"${tmpDirRecover}.tar.zst" &>/dev/null && exitCodeTar="0" || exitCodeTar="1"

    rm -rf "$tmpPathRecover"

    [ "$chekNewDiskSrc" == "true" ] && umount -l "$pathMount" &>/dev/null && rm -rf "$pathMount"
    [ "$chekNewDiskDest" == "true" ] && umount -l "$destMount" &>/dev/null && rm -rf "$destMount"
    [ "$exitCodeTar" == "0" ] && return 0 || return 1
}
export -f start_create_backup

start_restore_backup(){
    declare -A config
    read_conf_file "$argPathConfFile" "config"

    local pathFromRecover="${config["general.pathFrom"]}"
    local pathToRecover="${config["general.pathTo"]}"
    local pathOtherBackup="${config["general.otherBackPath"]}"
    local chsRecoverDate="${config["general.dateChooseBackup"]}"
    local dirsPaths=""
    local recoverPathFrom=""
    local recoverPathTo=""
    local pathMount="/mnt/ubrecovery_to"
    local pathFromMount="/mnt/ubrecovery_from"
    local checkNewMountFrom="false"
    local checkNewMountTo="false"
    local exitCodeTar=""
    local filesBackupArr

    if [ -z "$(mount | grep "$pathFromRecover" )" ]; then
        mkdir -p "$pathFromMount"
        mount "$pathFromRecover" "$pathFromMount"

        filesBackupArr=($(find "$pathFromMount/ubbackup" \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
        if [[ -n "${filesBackupArr[@]}" ]]; then
            if [ "${#filesBackupArr[@]}" -gt "1" ]; then
                for i in "${filesBackupArr[@]}"; do
                    [ -n "$(echo "$i" | grep "$chsRecoverDate")" ] && recoverPathFrom="$i"
                done
            else
                recoverPathFrom="${filesBackupArr[0]}"
            fi
        else
            filesBackupArr=($(find "$pathFromMount/home" \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
            if [[ -n "${filesBackupArr[@]}" ]]; then
                if [ "${#filesBackupArr[@]}" -gt "1" ]; then
                    for i in "${filesBackupArr[@]}"; do
                        [ -n "$(echo "$i" | grep "$chsRecoverDate")" ] && recoverPathFrom="$i"
                    done
                else
                    recoverPathFrom="${filesBackupArr[0]}"
                fi
            else
                [ -z "$pathOtherBackup" ] && return 1
            fi
        fi

        checkNewMountFrom="true"
    else
        filesBackupArr=($(find /ubbackup \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
        if [[ -n "${filesBackupArr[@]}" ]]; then
                if [ "${#filesBackupArr[@]}" -gt "1" ]; then
                    for i in "${filesBackupArr[@]}"; do
                        [ -n "$(echo "$i" | grep "$chsRecoverDate")" ] && recoverPathFrom="$i"
                    done
                else
                    recoverPathFrom="${filesBackupArr[0]}"
                fi
        else
            filesBackupArr=($(find /home \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
            if [[ -n "${filesBackupArr[@]}" ]]; then
                if [ "${#filesBackupArr[@]}" -gt "1" ]; then
                    for i in "${filesBackupArr[@]}"; do
                        [ -n "$(echo "$i" | grep "$chsRecoverDate")" ] && recoverPathFrom="$i"
                    done
                else
                    recoverPathFrom="${filesBackupArr[0]}"
                fi
            else
                [ -z "$pathOtherBackup" ] && return 1
            fi
        fi
    fi

    [ -n "$pathOtherBackup" ] && recoverPathFrom="$pathOtherBackup"

    if [ -z "$(mount | grep "$pathToRecover" )" ]; then
        mkdir -p "$pathMount"
        mount "$pathToRecover" "$pathMount"
        if [ "${config["addition.secureRecover"]}" == "true" ]; then
            if [ -d "${pathMount}/${ublinux_data_path_root:1}/recovery" ]; then
                recoverPathTo="$pathMount/${ublinux_data_path_root:1}/recovery"
            else
                mkdir -p "$pathMount/${ublinux_data_path_root:1}/recovery"
                recoverPathTo="$pathMount/${ublinux_data_path_root:1}/recovery"
            fi
        else
            recoverPathTo="$pathMount/"
        fi
        checkNewMountTo="true"
    else
        if [ "${config["addition.secureRecover"]}" == "true" ]; then
            if [ -d "$ublinux_data_path_root/recovery" ]; then
                recoverPathTo="$ublinux_data_path_root/recovery"
            else
                mkdir -p "$ublinux_data_path_root/recovery"
                recoverPathTo="$ublinux_data_path_root/recovery"
            fi
        else
            recoverPathTo="/"
        fi
    fi

    [ "${config["distribSett.bootloader"]}" == "true" ] && dirsPaths+="${path_root:1}/boot "
    [ "${config["distribSett.sysKernel"]}" == "true" ] && dirsPaths+="${ublinux_path_root:1}/vmlinuz-* ${ublinux_path_root:1}/ublinux-* "
    [ "${config["distribSett.basicModule"]}" == "true" ] && dirsPaths+="${ublinux_path_root:1}/base "
    [ "${config["distribSett.additSysModule"]}" == "true" ] && dirsPaths+="${ublinux_path_root:1}/modules ${ublinux_path_root:1}/machines ${ublinux_path_root:1}/optional"

    [ "${config["userDataSett.sysChanges"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/changes "
    [ "${config["userDataSett.userProf"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/homes "
    [ "${config["userDataSett.customModAndUpd"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/modules "
    [ "${config["userDataSett.tmpSysChange"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/rootcopy "
    [ "${config["userDataSett.macSysChange"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/machines "
    [ "${config["userDataSett.cacheModule"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/cache "
    [ "${config["userDataSett.notLoadSysModule"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/optional "
    [ "${config["userDataSett.sysSettings"]}" == "true" ] && dirsPaths+="${ublinux_data_path_root:1}/ublinux.ini "

    if [ -n "$recoverPathFrom" ] && [ -n "$dirsPaths" ] && [ -n "$recoverPathTo" ]; then
        tmpPwd="$(pwd)"
        cd "$recoverPathTo"
        tar --wildcards -xvf "$recoverPathFrom" ${dirsPaths} &>/dev/null && exitCodeTar="0" ||  exitCodeTar="1"
        cd "${tmpPwd}"
        [ "${config["userDataSett.sysSettings"]}" == "true" ] && [ "${config["addition.runSettAfterDone"]}" == "true" ] && config["addition.pathToNewIniFile"]="$recoverPathTo/${ublinux_data_path_root:1}/ublinux.ini"
        write_conf_file "$argPathConfFile" "config"
        [ "$checkNewMountTo" == "true" ] && [ "${config["addition.runSettAfterDone"]}" != "true" ] && umount -l "$pathToRecover" && rm -rf "$pathMount"
        [ "$checkNewMountFrom" == "true" ] && umount -l "$pathFromRecover" && rm -rf "$pathFromMount"
        return $exitCodeTar
    else
        [ "$checkNewMountTo" == "true" ] && umount -l "$pathToRecover" && rm -rf "$pathMount"
        [ "$checkNewMountFrom" == "true" ] && umount -l "$pathFromRecover" && rm -rf "$pathFromMount"
        return 1
    fi
}
export -f start_restore_backup

check_have_recover_file(){
    local pathFromMount="/mnt/ubrecover"

    if [ -z "$(mount | grep "$argPathBlock" )" ]; then
        mkdir -p "$pathFromMount"
        mount "$argPathBlock" "$pathFromMount"

        filesBackupArr=($(find "$pathFromMount/ubbackup" \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
        if [[ -n "${filesBackupArr[@]}" ]]; then
            umount -l "$argPathBlock" "$pathFromMount"  && rm -rf "$pathFromMount" 
            [ "${#filesBackupArr[@]}" -gt "1" ] && echo "${filesBackupArr[@]}" && return 2 || return 0
        else
            filesBackupArr=($(find "$pathFromMount/ubbackup" \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
            if [[ -n "${filesBackupArr[@]}" ]]; then
                umount -l "$argPathBlock" "$pathFromMount"  && rm -rf "$pathFromMount" 
                [ "${#filesBackupArr[@]}" -gt "1" ] && echo "${filesBackupArr[@]}" && return 2 || return 0
            else
                umount -l "$argPathBlock" "$pathFromMount"  && rm -rf "$pathFromMount" 
                return 1
            fi
        fi

        checkNewMountFrom="true"
    else
        filesBackupArr=($(find /ubbackup \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
        if [[ -n "${filesBackupArr[@]}" ]]; then
            [ "${#filesBackupArr[@]}" -gt "1" ] && echo "${filesBackupArr[@]}" && return 2 || return 0
        else
            filesBackupArr=($(find /home \( ! -regex '.*/\..*' \) -iname 'ubbackup-*.tar.zst' | sort -r 2>/dev/null))
            if [[ -n "${filesBackupArr[@]}" ]]; then
                [ "${#filesBackupArr[@]}" -gt "1" ] && echo "${filesBackupArr[@]}" && return 2 || return 0
            else
                return 1
            fi
        fi
    fi

    return 1
}
export -f check_have_recover_file

while [[ -n $1 ]]; do
    case $1 in
        -glp)
            export argGetListPart="-glp"
        ;;
        -gpc)
            export argGetPathConf="-gpc"
        ;;
        -cb)
            export argCreateBackup="-cb"
        ;;
        -rb)
            export argRestoreBackup="-rb"
        ;;
        -check)
            export argCheckRecoverFile="-check"
        ;;
        *)
            [ -f "$1" ] && export argPathConfFile="$1"
            [ -b "$1" ] && export argPathBlock="$1"
        ;;
    esac
	shift
done

[ -n "${argGetListPart}" ] && get_list_part && exit 0
[ -n "${argGetPathConf}" ] && create_conf && exit 0
[ -n "${argCreateBackup}" ] && start_create_backup && exit 0
[ -n "${argRestoreBackup}" ] && start_restore_backup && exit 0
if [ -n "${argCheckRecoverFile}" ]; then
    listArr="$(check_have_recover_file)"
    retCode="$?"
    [ "$retCode" == "2" ] && echo "$listArr" && exit 2
    [ "$retCode" == "0" ] && exit 0
fi

exit 1
