#!/usr/bin/env bash
# shellcheck disable=SC1090,SC2153,SC2155,SC2034
#SC1090: Can't follow non-constant source. Use a directive to specify location.
#SC2153: Possible Misspelling: MYVARIABLE may not be assigned. Did you mean MY_VARIABLE?
#SC2155 Declare and assign separately to avoid masking return values
#SC2034: foo appears unused. Verify it or export it.
set -eu

# Arch Linux Install Script Recovery (alis-recovery) start a recovery for an
# failed installation or broken system.
# Copyright (C) 2022 picodotdev

# 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 3 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/>.

# This script is hosted at https://github.com/picodotdev/alis. For new features,
# improvements and bugs fill an issue in GitHub or make a pull request.
# Pull Request are welcome!
#
# If you test it in real hardware please send me an email to pico.dev@gmail.com with
# the machine description and tell me if somethig goes wrong or all works fine.
#
# Please, don't ask for support for this script in Arch Linux forums, first read
# the Arch Linux wiki [1], the Installation Guide [2] and the General
# Recomendations [3], later compare the commands with those of this script.
#
# [1] https://wiki.archlinux.org
# [2] https://wiki.archlinux.org/index.php/Installation_guide
# [3] https://wiki.archlinux.org/index.php/General_recommendations

# Starts the reccovery of a Arch Linux system installed with alis.
#
# Usage:
# # loadkeys es
# # curl -sL https://raw.githubusercontent.com/picodotdev/alis/main/download.sh | bash
# # vim alis-recovery.conf
# # ./alis-recovery.sh

function init_config() {
    local COMMONS_FILE="alis-commons.sh"

    source "$COMMONS_FILE"
    source "$COMMONS_CONF_FILE"
    source "$RECOVERY_CONF_FILE"
}

function sanitize_variables() {
    DEVICE=$(sanitize_variable "$DEVICE")
    FILE_SYSTEM_TYPE=$(sanitize_variable "$FILE_SYSTEM_TYPE")
    PARTITION_MODE=$(sanitize_variable "$PARTITION_MODE")
    SWAP_SIZE=$(sanitize_variable "$SWAP_SIZE")
    PARTITION_CUSTOMMANUAL_BOOT=$(sanitize_variable "$PARTITION_CUSTOMMANUAL_BOOT")
    PARTITION_CUSTOMMANUAL_ROOT=$(sanitize_variable "$PARTITION_CUSTOMMANUAL_ROOT")

    for I in "${BTRFS_SUBVOLUMES_MOUNTPOINTS[@]}"; do
        IFS=',' read -ra SUBVOLUME <<< "$I"
        if [ "${SUBVOLUME[0]}" == "root" ]; then
            BTRFS_SUBVOLUME_ROOT=("${SUBVOLUME[@]}")
        elif [ "${SUBVOLUME[0]}" == "swap" ]; then
            BTRFS_SUBVOLUME_SWAP=("${SUBVOLUME[@]}")
        fi
    done

    for I in "${PARTITION_MOUNT_POINTS[@]}"; do
        IFS='=' read -ra PARTITION_MOUNT_POINT <<< "$I"
        if [ "${PARTITION_MOUNT_POINT[1]}" == "/boot" ]; then
            PARTITION_BOOT_NUMBER="${PARTITION_MOUNT_POINT[0]}"
        elif [ "${PARTITION_MOUNT_POINT[1]}" == "/" ]; then
            PARTITION_ROOT_NUMBER="${PARTITION_MOUNT_POINT[0]}"
        fi
    done
}

function check_variables() {
    check_variables_value "KEYS" "$KEYS"
    check_variables_boolean "LOG_TRACE" "$LOG_TRACE"
    check_variables_boolean "LOG_FILE" "$LOG_FILE"
    check_variables_value "DEVICE" "$DEVICE"
    if [ "$DEVICE" == "auto" ]; then
        local DEVICE_BOOT=$(lsblk -oMOUNTPOINT,PKNAME -P -M | grep 'MOUNTPOINT="/run/archiso/bootmnt"' | sed 's/.*PKNAME="\(.*\)".*/\1/')
        if [ -n "$DEVICE_BOOT" ]; then
            local DEVICE_BOOT="/dev/$DEVICE_BOOT"
        fi
        local DEVICE_DETECTED="false"
        if [ -e "/dev/sda" ] && [ "$DEVICE_BOOT" != "/dev/sda" ]; then
            if [ "$DEVICE_DETECTED" == "true" ]; then
                echo "Auto device is ambiguous, detected $DEVICE and /dev/sda."
                exit 1
            fi
            DEVICE_DETECTED="true"
            DEVICE_SDA="true"
            DEVICE="/dev/sda"
        fi
        if [ -e "/dev/nvme0n1" ] && [ "$DEVICE_BOOT" != "/dev/nvme0n1" ]; then
            if [ "$DEVICE_DETECTED" == "true" ]; then
                echo "Auto device is ambiguous, detected $DEVICE and /dev/nvme0n1."
                exit 1
            fi
            DEVICE_DETECTED="true"
            DEVICE_NVME="true"
            DEVICE="/dev/nvme0n1"
        fi
        if [ -e "/dev/vda" ] && [ "$DEVICE_BOOT" != "/dev/vda" ]; then
            if [ "$DEVICE_DETECTED" == "true" ]; then
                echo "Auto device is ambiguous, detected $DEVICE and /dev/vda."
                exit 1
            fi
            DEVICE_DETECTED="true"
            DEVICE_VDA="true"
            DEVICE="/dev/vda"
        fi
        if [ -e "/dev/mmcblk0" ] && [ "$DEVICE_BOOT" != "/dev/mmcblk0" ]; then
            if [ "$DEVICE_DETECTED" == "true" ]; then
                echo "Auto device is ambiguous, detected $DEVICE and /dev/mmcblk0."
                exit 1
            fi
            DEVICE_DETECTED="true"
            DEVICE_MMC="true"
            DEVICE="/dev/mmcblk0"
        fi
    fi
    check_variables_boolean "DEVICE_TRIM" "$DEVICE_TRIM"
    check_variables_boolean "LVM" "$LVM"
    check_variables_equals "LUKS_PASSWORD" "LUKS_PASSWORD_RETYPE" "$LUKS_PASSWORD" "$LUKS_PASSWORD_RETYPE"
    check_variables_list "FILE_SYSTEM_TYPE" "$FILE_SYSTEM_TYPE" "ext4 btrfs xfs f2fs reiserfs" "true" "true"
    check_variables_size "BTRFS_SUBVOLUME_ROOT" ${#BTRFS_SUBVOLUME_ROOT[@]} 3
    check_variables_list "BTRFS_SUBVOLUME_ROOT" "${BTRFS_SUBVOLUME_ROOT[2]}" "/" "true" "true"
    if [ -n "$SWAP_SIZE" ]; then
        check_variables_size "BTRFS_SUBVOLUME_SWAP" ${#BTRFS_SUBVOLUME_SWAP[@]} 3
    fi
    for I in "${BTRFS_SUBVOLUMES_MOUNTPOINTS[@]}"; do
        IFS=',' read -ra SUBVOLUME <<< "$I"
        check_variables_size "SUBVOLUME" ${#SUBVOLUME[@]} 3
    done
    check_variables_list "PARTITION_MODE" "$PARTITION_MODE" "auto custom manual" "true" "true"
    check_variables_value "PARTITION_BOOT_NUMBER" "$PARTITION_BOOT_NUMBER"
    check_variables_value "PARTITION_ROOT_NUMBER" "$PARTITION_ROOT_NUMBER"
    check_variables_boolean "CHROOT" "$CHROOT"
}

function warning() {
    echo -e "${BLUE}Welcome to Arch Linux Install Script Recovery${NC}"
    echo ""
    read -r -p "Do you want to continue? [y/N] " yn
    case $yn in
        [Yy]* )
            ;;
        [Nn]* )
            exit
            ;;
        * )
            exit
            ;;
    esac
}

function init() {
    print_step "init()"

    init_log_trace "$LOG_TRACE"
    init_log_file "$LOG_FILE" "$RECOVERY_LOG_FILE"
    loadkeys "$KEYS"
}

function facts() {
    print_step "facts()"

    facts_commons

    if echo "$DEVICE" | grep -q "^/dev/sd[a-z]"; then
        DEVICE_SDA="true"
    elif echo "$DEVICE" | grep -q "^/dev/nvme"; then
        DEVICE_NVME="true"
    elif echo "$DEVICE" | grep -q "^/dev/vd[a-z]"; then
        DEVICE_VDA="true"
    elif echo "$DEVICE" | grep -q "^/dev/mmc"; then
        DEVICE_MMC="true"
    fi
}

function prepare() {
    print_step "prepare()"

    prepare_partition
    configure_network
    ask_passwords
}

function prepare_partition() {
    if [ -d "${MNT_DIR}"/boot ]; then
        umount "${MNT_DIR}"/boot
        umount "${MNT_DIR}"
    fi
    if [ -e "/dev/mapper/$LVM_VOLUME_GROUP-$LVM_VOLUME_LOGICAL" ]; then
        umount "/dev/mapper/$LVM_VOLUME_GROUP-$LVM_VOLUME_LOGICAL"
    fi
    if [ -e "/dev/mapper/$LUKS_DEVICE_NAME" ]; then
        cryptsetup close "$LUKS_DEVICE_NAME"
    fi
    partprobe "$DEVICE"
}

function ask_passwords() {
    if [ "$LUKS_PASSWORD" == "ask" ]; then
        PASSWORD_TYPED="false"
        while [ "$PASSWORD_TYPED" != "true" ]; do
            read -r -sp 'Type LUKS password: ' LUKS_PASSWORD
            echo ""
            read -r -sp 'Retype LUKS password: ' LUKS_PASSWORD_RETYPE
            echo ""
            if [ "$LUKS_PASSWORD" == "$LUKS_PASSWORD_RETYPE" ]; then
                PASSWORD_TYPED="true"
            else
                echo "LUKS password don't match. Please, type again."
            fi
        done
    fi
}

function partition() {
    print_step "partition()"

    # setup
    partition_setup

    # luks and lvm
    if [ -n "$LUKS_PASSWORD" ]; then
        echo -n "$LUKS_PASSWORD" | cryptsetup --key-file=- open "$PARTITION_ROOT" "$LUKS_DEVICE_NAME"
        sleep 5
    fi

    if [ -n "$LUKS_PASSWORD" ]; then
        DEVICE_ROOT="/dev/mapper/$LUKS_DEVICE_NAME"
    fi
    if [ "$LVM" == "true" ]; then
        DEVICE_ROOT="/dev/mapper/$LVM_VOLUME_GROUP-$LVM_VOLUME_LOGICAL"
    fi

    # options
    partition_options

    # mount
    partition_mount
}

function recovery() {
    arch-chroot "${MNT_DIR}"
}

function end() {
    echo ""
    if [ "$CHROOT" == "false" ]; then
        echo -e "${GREEN}Recovery started.${NC}"
    else
        echo -e "${GREEN}Recovery ended.${NC}"
    fi
    echo "You must do an explicit reboot after finalize recovery (exit if in arch-chroot, ./alis-reboot.sh)."
    echo ""
}

function main() {
    init_config
    sanitize_variables
    check_variables
    warning
    init
    facts
    prepare
    partition
    if [ "$CHROOT" == "true" ]; then
        recovery
    fi
    end
}

main
