#!/usr/bin/env bash
#
# Name: System Installer
# Author: kleb
# Created: 2024/11/12
# Last modified: 2025/12/05
#
# Version: 1.9
#

set -euo pipefail

currentName="System Installer"
currentVersion="1.9"
currentMe=$(basename "$0")

locationUtils="${BASE_URL:-https://kleb.sh}/installer/utils.sh"
locationVars="${BASE_URL:-https://kleb.sh}/installer/system/system.vars"

requiredPackages=(
    bc
    curl
    ethtool
    fish
    gnupg
    btop
    inotify-tools
    lsb-release
    moreutils
    net-tools
    nfs-common
    sudo
    unzip
    wget
    zip
    cron
    wtmpdb
    bat
    jq
)

parentModule="${1:-}"

start() {
    source "utils.sh"

    trap clean ERR

    initProgress "System" "$parentModule"
    logInfo "$currentName v$currentVersion"

    prepare
    execute
    clean
}

prepare() {
    updateProgress "Preparing System module" 5

    if ! download "$locationVars" "system.vars" "${skipChecksum:-}"; then
        logError "Failed to download system.vars"
        return 1
    fi

    source "system.vars"

    updateProgress "Installing required packages" 15
    installPackage "${requiredPackages[@]}" "$FALSE"
}

execute() {
    updateProgress "Configuring system language" 20

    if [[ "${LANGUAGE:-}" ]]; then
        if ! grep -qxF "$LANGUAGE UTF-8" /etc/locale.gen; then
            appendToFile "$LANGUAGE UTF-8" "/etc/locale.gen"
        fi

        noOut locale-gen --purge "$LANGUAGE"
        noOut update-locale LANG="$LANGUAGE"
        noOut update-locale LC_ALL="$LANGUAGE"
        noOut update-locale LANGUAGE="$LANGUAGE"
        noOut dpkg-reconfigure --frontend=noninteractive locales
    fi

    updateProgress "Configuring SSH key" 35

    if [[ -v SSH_KEY_DCS ]] && [[ ${#SSH_KEY_DCS[@]} -gt 0 ]]; then
        ownIp=$(ifconfig 2>/dev/null | grep -Eo "inet (addr:)?([0-9]*\.){3}[0-9]*" | grep -Eo "([0-9]*\.){3}[0-9]*" | grep -v "127.0.0.1" | head -n 1)
        if [[ ! "$ownIp" ]]; then
            ownIp=$(ip -4 addr show 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | grep -v "127.0.0.1" | head -n 1)
        fi

        sshKeyUrl=""
        for dc in "${!SSH_KEY_DCS[@]}"; do
            if [[ ! -z "${SSH_IP_RANGES[$dc]:-}" && "$ownIp" == "${SSH_IP_RANGES[$dc]}"* ]]; then
                sshKeyUrl="${SSH_KEY_DCS[$dc]}"
                logInfo "Using SSH key for DC: $dc"
                break
            fi
        done

        if [[ ! "$sshKeyUrl" ]]; then
            sshKeyUrl="${SSH_KEY_DCS[0]:-}"
            if [[ "$sshKeyUrl" ]]; then
                logWarning "IP $ownIp not matched to any DC, using default (0)"
            fi
        fi

        if [[ "$sshKeyUrl" ]]; then
            mkdir -p "$HOME/.ssh"
            if ! download "$sshKeyUrl" "ssh_keys_temp" "${skipChecksum:-}"; then
                logError "Failed to download SSH key from $sshKeyUrl"
                return 1
            fi

            while IFS= read -r key || [[ -n "$key" ]]; do
                [[ -z "$key" ]] && continue
                appendToFile "$key" "$HOME/.ssh/authorized_keys"
            done < "ssh_keys_temp"

            remove "ssh_keys_temp"

            chmod 700 "$HOME/.ssh"
            chmod 600 "$HOME/.ssh/authorized_keys"

            if grep -q "^#PermitRootLogin" /etc/ssh/sshd_config; then
                replace "/etc/ssh/sshd_config" "^#PermitRootLogin" "PermitRootLogin" "$FALSE"
            fi
            if grep -q "^#AuthorizedKeysFile" /etc/ssh/sshd_config; then
                replace "/etc/ssh/sshd_config" "^#AuthorizedKeysFile" "AuthorizedKeysFile" "$FALSE"
            fi

            if [[ "$SSH_RESTART" ]]; then
                restartSSHService
            fi
        fi
    fi

    updateProgress "Setting timezone" 45

    if [[ "${TIMEZONE:-}" ]]; then
        ZONEINFO="/usr/share/zoneinfo/$TIMEZONE"

        if [[ -f "$ZONEINFO" ]]; then
            ln -sf "$ZONEINFO" /etc/localtime
            echo "$TIMEZONE" >/etc/timezone
            logInfo "Timezone set to $TIMEZONE"
        else
            logWarning "Timezone $TIMEZONE not found in /usr/share/zoneinfo"
        fi
    fi

    updateProgress "Detecting virtualization" 60

    if [[ "${CHECK_VIRTUALIZATION:-}" == "TRUE" ]]; then
        virtualization=$(systemd-detect-virt || true)

        case "$virtualization" in
        kvm)
            logInfo "Installing qemu-guest-agent for KVM/Proxmox..."

            if ! installPackage "qemu-guest-agent"; then
                logError "Failed to install qemu-guest-agent"
                return 1
            fi
            enableService "qemu-guest-agent"
            ;;
        vmware)
            logInfo "Installing open-vm-tools for VMware..."

            if ! installPackage "open-vm-tools"; then
                logError "Failed to install open-vm-tools"
                return 1
            fi

            enableService "open-vm-tools"
            ;;
        lxc)
            logInfo "LXC container detected, no guest agent required"
            ;;
        "" | none)
            logInfo "No virtualization detected"
            ;;
        *)
            logInfo "Unknown virtualization detected (${virtualization:-unknown}), skipping"
            ;;
        esac
    fi

    updateProgress "Configuring SSH MOTD" 75

    if [[ "${SSH_MOTD:-}" == "TRUE" ]]; then
        if grep -qE "^\s*PrintMotd" /etc/ssh/sshd_config; then
            replace "/etc/ssh/sshd_config" "^\s*PrintMotd.*" "PrintMotd yes" "$FALSE"
        else
            appendToFile "PrintMotd yes" "/etc/ssh/sshd_config"
        fi

        if grep -qE "^\s*PrintLastLog" /etc/ssh/sshd_config; then
            replace "/etc/ssh/sshd_config" "^\s*PrintLastLog.*" "PrintLastLog no" "$FALSE"
        else
            appendToFile "PrintLastLog no" "/etc/ssh/sshd_config"
        fi

        if grep -q "^[^#].*pam_mail\.so" /etc/pam.d/sshd; then
            replace "/etc/pam.d/sshd" "^\([^#].*pam_mail\.so\)" "# \1" "$FALSE"
        fi

        if [[ "${SSH_RESTART:-}" == "TRUE" ]]; then
            restartSSHService
        fi
    fi

    updateProgress "Installing MOTD" 85

    if [[ "${MOTD:-}" ]]; then
        rm -f /etc/update-motd.d/*
        : >/etc/motd

        if ! download "$MOTD" "motd.sh" "${skipChecksum:-}"; then
            logError "Failed to download MOTD script from $MOTD"
            return 1
        fi

        chmod 755 "motd.sh"
        if ! move "motd.sh" "/usr/local/bin/motd.sh"; then
            logError "Failed to move motd.sh to /usr/local/bin/"
            return 1
        fi

        if ! addCronjob "$MOTD_CRON"; then
            logError "Failed to add MOTD cron job"
            return 1
        fi

        writeToFile $'#!/usr/bin/env bash\ncat /var/cache/motd' "/etc/update-motd.d/00-motd"

        chmod 755 /etc/update-motd.d/00-motd

        logInfo "Generating motd for the first time..."
        /usr/local/bin/motd.sh >/var/cache/motd
    fi

    updateProgress "Configuring default shell" 90

    if [[ "$FISH_DEFAULT_SHELL" == "TRUE" ]]; then
        if command -v fish >/dev/null 2>&1; then
            fishPath=$(command -v fish)
            if [[ -n "$fishPath" ]]; then
                if ! grep -q "$fishPath" /etc/shells; then
                    appendToFile "$fishPath" "/etc/shells"
                fi

                if grep -q "^root:" /etc/passwd; then
                    rootLine=$(grep "^root:" /etc/passwd)
                    newRootLine=$(echo "$rootLine" | sed "s|:[^:]*$|:$fishPath|")
                    if ! replace "/etc/passwd" "^root:.*" "$newRootLine" "$TRUE"; then
                        logError "Failed to set fish as default shell for root user"
                        return 1
                    fi
                    logInfo "Set fish as default shell for root user"
                else
                    logError "Root user not found in /etc/passwd, cannot set default shell"
                    return 1
                fi

                if [[ "$FISH_INSTALL_FISHER" == "TRUE" ]]; then
                    if ! command -v fisher >/dev/null 2>&1; then
                        fish -c "curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher" >/dev/null 2>&1
                        logInfo "Fisher plugin manager installed"
                    else
                        logInfo "Fisher already installed, skipping"
                    fi

                    if [[ "$FISH_INSTALL_CATPPUCCIN" == "TRUE" ]]; then
                        fish -c "fisher install catppuccin/fish" >/dev/null 2>&1
                        mkdir -p "$HOME/.config/fish"
                        executeWithPrompt "y" "fish -c \"fish_config theme save 'Catppuccin Mocha'\""
                        logInfo "Catppuccin Mocha theme installed and set"

                        if ! grep -Fxq "set fish_greeting" "$HOME/.config/fish/config.fish" 2>/dev/null; then
                            appendToFile "set fish_greeting" "$HOME/.config/fish/config.fish"
                        else
                            logInfo "Fish greeting already disabled, skipping"
                        fi
                    fi

                    if [[ "$FISH_ALIAS_BAT" == "TRUE" ]] && command -v batcat >/dev/null 2>&1; then
                        addFishAlias "bat" "batcat"
                    fi

                    if [[ "$FISH_ALIAS_PC" == "TRUE" ]]; then
                        addFishAlias "pc" "netstat -tupln"
                    fi
                fi
            else
                logError "Fish shell not found in PATH"
                return 1
            fi
        else
            logError "Fish shell not installed"
            return 1
        fi
    fi
}

clean() {
    remove "system.vars"
    remove "$currentMe"

    updateProgress "System module configuration complete" 100
}

start
