#!/bin/bash
# Script:      chroot-install
# Title:       Astra Linux Chroot Installer
# Description: This script runs within the chroot environment during the live image build
#              process. It configures the system, installs packages, sets up users, and
#              performs other essential tasks within the chroot.

set -e          # exit on error
set -o pipefail # exit on pipeline error
set -u          # treat unset variable as error

# Function to create a group if it does not exist.
# Usage: create_group <GROUP_NAME> [group_id]
create_group() {
    local GROUP_NAME="$1"
    local GROUP_ID="${2:-}"
    if ! getent group "${GROUP_NAME}" >/dev/null; then
        if [ -n "${GROUP_ID}" ] && ! getent group "${GROUP_ID}" >/dev/null; then
            groupadd --gid "${GROUP_ID}" "${GROUP_NAME}"
        else
            groupadd --system "${GROUP_NAME}"
        fi
    fi
}

run_with_spinner() {
    local MSG="$1"
    shift
    if [ "${VERBOSITY_LEVEL}" -ge 1 ]; then
        "$@"
    else
        # Create temporary log file for interactive output
        local COMBINED_LOG="/tmp/chroot_build_log_$$"

        # Run command with stdout and stderr redirected to log file
        "$@" >"${COMBINED_LOG}" 2>&1 &
        local CMD_PID="$!"

        local DELAY=0.1
        local SPINSTR='|/-\\'
        # Use environment variable for show output toggle (like VERBOSITY_LEVEL)
        # Initialize to false only if not already set
        : ${SHOW_SPINNER_OUTPUT:="false"}
        local TAIL_PID=""
        local LAST_LINE_COUNT=0
        local OUTPUT_LINES=0
        local OUTPUT_ANIM_COUNTER=0
        local LAST_REFRESH_TIME=$(date +%s)
        console_colors

        while kill -0 "${CMD_PID}" 2>/dev/null; do
            if [ "$SHOW_SPINNER_OUTPUT" = "false" ]; then
                for ((i = 0; i < ${#SPINSTR}; i++)); do
                    # Check if process is still running before showing spinner
                    if ! kill -0 "${CMD_PID}" 2>/dev/null; then
                        break 2
                    fi

                    printf "\r${BOLD}${CYAN}I:${ENDCOLOR} ${MSG} [${CYAN}${SPINSTR:$i:1}${ENDCOLOR}] ${DIM}(press ${ENDCOLOR}${BOLD}R${ENDCOLOR}${DIM} to toggle output)${ENDCOLOR}" >&2

                    # Check for 'r' key with multiple attempts for better responsiveness
                    local KEY_PRESSED=false
                    for attempt in {1..3}; do
                        if read -t 0.05 -n 1 -s KEY 2>/dev/null && [[ "$KEY" == "r" || "$KEY" == "R" || "$KEY" == "к" || "$KEY" == "К" ]]; then
                            KEY_PRESSED=true
                            break
                        fi
                    done
                    if [ "$KEY_PRESSED" = "true" ]; then
                        SHOW_SPINNER_OUTPUT="true"
                        # Clear input buffer to prevent multiple rapid keypresses
                        while read -t 0.01 -n 1 -s 2>/dev/null; do :; done
                        printf "\r${BOLD}${CYAN}I:${ENDCOLOR} ${MSG} ${DIM}(press ${ENDCOLOR}${BOLD}R${ENDCOLOR}${DIM} to toggle output)${ENDCOLOR}$(tput el)\n"
                        # Show separator line
                        printf "${DIM}────────────────────────────────────────────────────────────────────────────────${ENDCOLOR}\n"
                        # Always show exactly 10 lines, padding with empty lines if needed
                        local AVAILABLE_LINES=$(wc -l <"${COMBINED_LOG}" 2>/dev/null || echo 0)
                        local LINES_TO_SHOW=10
                        if [ "$AVAILABLE_LINES" -gt 0 ]; then
                            tail -n ${LINES_TO_SHOW} "${COMBINED_LOG}" 2>/dev/null
                            local ACTUAL_LINES=$(tail -n ${LINES_TO_SHOW} "${COMBINED_LOG}" 2>/dev/null | wc -l)
                            # Pad with empty lines if we have fewer than 10 lines
                            for ((i = ACTUAL_LINES; i < LINES_TO_SHOW; i++)); do
                                echo ""
                            done
                        else
                            # No content yet, show 10 empty lines
                            for ((i = 0; i < LINES_TO_SHOW; i++)); do
                                echo ""
                            done
                        fi
                        # Show bottom separator line
                        printf "${DIM}────────────────────────────────────────────────────────────────────────────────${ENDCOLOR}\n"
                        # Set current line count so we only show new lines from this point
                        LAST_LINE_COUNT=$(wc -l <"${COMBINED_LOG}" 2>/dev/null || echo 0)
                        OUTPUT_LINES=$((1 + 1 + 10 + 1)) # Count header + top separator + exactly 10 lines + bottom separator
                        break
                    fi
                done
            else
                # In output mode - maintain sliding window of exactly 10 lines
                local CURRENT_LINE_COUNT=$(wc -l <"${COMBINED_LOG}" 2>/dev/null || echo 0)
                local CURRENT_TIME=$(date +%s)
                local TIME_SINCE_REFRESH=$((CURRENT_TIME - LAST_REFRESH_TIME))

                # Update if new lines appeared OR if 10 seconds passed (for progress updates like mksquashfs)
                if [ "$CURRENT_LINE_COUNT" -gt "$LAST_LINE_COUNT" ] || [ "$TIME_SINCE_REFRESH" -ge 10 ]; then
                    # Always show exactly 10 lines, replacing the entire output area
                    local MAX_LINES=10

                    # Move to start of output area (after header)
                    if [ "$OUTPUT_LINES" -gt 1 ]; then
                        printf "\033[$((OUTPUT_LINES - 1))A"
                    fi

                    # Clear the output area and show latest 10 lines with separators
                    printf "$(tput ed)"
                    printf "${DIM}────────────────────────────────────────────────────────────────────────────────${ENDCOLOR}\n"
                    # Always show exactly 10 lines, padding with empty lines if needed
                    local AVAILABLE_LINES=$(wc -l <"${COMBINED_LOG}" 2>/dev/null || echo 0)
                    if [ "$AVAILABLE_LINES" -gt 0 ]; then
                        tail -n ${MAX_LINES} "${COMBINED_LOG}" 2>/dev/null
                        local ACTUAL_LINES=$(tail -n ${MAX_LINES} "${COMBINED_LOG}" 2>/dev/null | wc -l)
                        # Pad with empty lines if we have fewer than 10 lines
                        for ((i = ACTUAL_LINES; i < MAX_LINES; i++)); do
                            echo ""
                        done
                    else
                        # No content yet, show 10 empty lines
                        for ((i = 0; i < MAX_LINES; i++)); do
                            echo ""
                        done
                    fi
                    printf "${DIM}────────────────────────────────────────────────────────────────────────────────${ENDCOLOR}\n"

                    LAST_LINE_COUNT="$CURRENT_LINE_COUNT"
                    LAST_REFRESH_TIME="$CURRENT_TIME"
                    OUTPUT_LINES=$((1 + 1 + 10 + 1)) # Header + top separator + exactly 10 lines + bottom separator
                fi

                # Animate the header line with spinner (same as normal mode but different color)
                OUTPUT_ANIM_COUNTER=$((OUTPUT_ANIM_COUNTER + 1))
                local SPINNER_CHAR=${SPINSTR:$((OUTPUT_ANIM_COUNTER % ${#SPINSTR})):1}

                # Update the first line with spinner animation
                if [ "$OUTPUT_LINES" -gt 0 ]; then
                    printf "\033[${OUTPUT_LINES}A\r${BOLD}${CYAN}I:${ENDCOLOR} ${MSG} [${LIGHTGREEN}${SPINNER_CHAR}${ENDCOLOR}] ${DIM}(press ${ENDCOLOR}${BOLD}R${ENDCOLOR}${DIM} to toggle output)${ENDCOLOR}$(tput el)\033[${OUTPUT_LINES}B\r"
                fi

                # Check for 'r' to return to spinner with multiple attempts
                local KEY_PRESSED=false
                for attempt in {1..3}; do
                    if read -t 0.05 -n 1 -s KEY 2>/dev/null && [[ "$KEY" == "r" || "$KEY" == "R" || "$KEY" == "к" || "$KEY" == "К" ]]; then
                        KEY_PRESSED=true
                        break
                    fi
                done
                if [ "$KEY_PRESSED" = "true" ]; then
                    SHOW_SPINNER_OUTPUT="false"
                    # Clear input buffer to prevent multiple rapid keypresses
                    while read -t 0.01 -n 1 -s 2>/dev/null; do :; done
                    # Move cursor up to the first "Output mode" line and replace it
                    printf "\033[${OUTPUT_LINES}A\r${BOLD}${CYAN}I:${ENDCOLOR} ${MSG} [${CYAN}${SPINSTR:0:1}${ENDCOLOR}] ${DIM}(press ${ENDCOLOR}${BOLD}R${ENDCOLOR}${DIM} to toggle output)${ENDCOLOR}$(tput el)$(tput ed)"
                    OUTPUT_LINES=0
                else
                    # Short sleep to prevent high CPU usage in output mode
                    sleep 0.1
                fi
            fi
        done

        # Clean up
        if [ -n "$TAIL_PID" ]; then
            kill "$TAIL_PID" 2>/dev/null
        fi

        wait "${CMD_PID}"
        local EXIT_CODE=$?

        rm -f "${COMBINED_LOG}"

        # Move to the header line and show [done], clearing everything below
        if [ "$SHOW_SPINNER_OUTPUT" = "true" ] && [ "$OUTPUT_LINES" -gt 0 ]; then
            printf "\033[${OUTPUT_LINES}A\r${BOLD}${CYAN}I:${ENDCOLOR} ${MSG} [${GREEN}done${ENDCOLOR}]$(tput el)$(tput ed)\n" >&2
        else
            printf "\r${BOLD}${CYAN}I:${ENDCOLOR} ${MSG} [${GREEN}done${ENDCOLOR}]$(tput el)\n" >&2
        fi

        return $EXIT_CODE
    fi
}

console_colors() {
    RED=$'\e[31m'
    GREEN=$'\e[32m'
    YELLOW=$'\e[33m'
    CYAN=$'\e[36m'
    LIGHTGREEN=$'\e[92m'
    LIGHTYELLOW=$'\e[93m'
    BOLD=$'\e[1m'
    DIM=$'\e[2m'
    ENDCOLOR=$'\e[0m'
}

console_colors

# Check if configuration file exists and source it
CONFIG_FILE="/astra-live/config"
if [[ -f "${CONFIG_FILE}" ]]; then
    . "${CONFIG_FILE}"
else
    echo "Configuration file '${CONFIG_FILE}' not found. Exiting."
    exit 1
fi

# Set the hostname
echo "${HOST_NAME}" >/etc/hostname

# Define debconf settings
DEBCONF_SETTINGS=(
    "keyboard-configuration keyboard-configuration/layoutcode string us,ru"
    "keyboard-configuration keyboard-configuration/variant select Russian"
    "keyboard-configuration keyboard-configuration/toggle select Alt+Shift"
    "tzdata tzdata/Zones/Europe select Moscow"
    "locales locales/default_environment_locale select ru_RU.UTF-8"
    "locales locales/locales_to_be_generated multiselect ru_RU.UTF-8 UTF-8"
    "openssh-server openssh-server/password-authentication boolean false"
    "openssh-server openssh-server/permit-root-login boolean false"
    "console-setup console-setup/codeset47 select Guess optimal character set"
    "console-setup console-setup/store_defaults_in_debconf_db boolean true"
    "console-setup console-setup/codesetcode string guess"
    "console-setup console-setup/fontsize string 8x16"
    "console-setup console-setup/fontsize-fb47 select 8x16"
    "console-setup console-setup/fontsize-text47 select 8x16"
    "console-setup console-setup/charmap47 select UTF-8"
    "console-setup console-setup/fontface47 select Fixed"
)

# Apply debconf settings
run_with_spinner "Applying debconf settings" bash -c '
    for SETTING in "${DEBCONF_SETTINGS[@]}"; do
        echo "${SETTING}" | debconf-set-selections -v
    done
'

# Update package lists
run_with_spinner "Updating package lists" env DEBIAN_FRONTEND=noninteractive apt-get update

# Upgrade packages
run_with_spinner "Upgrading installed packages" env DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y

# Install packages based on provided list and distribution parameters
/astra-live/scripts/condinapt -l /astra-live/packages/packages.list -P /astra-live/packages/priority.list -c /astra-live/config -m /astra-live/scripts/filter.map

# Default password hash (existing hash)
DEFAULT_PASSWORD_HASH='U6aMy0wojraho'

# Check if a custom password hash is provided; otherwise use default
PASSWORD_HASH="${USER_PASSWORD_HASH:-${DEFAULT_PASSWORD_HASH}}"

# Additional setup for specific image types
if [[ " ${GENERATE_IMAGES[@]} " =~ " iso " ]]; then
    # Write live configuration for ISO images
    cat <<EOF >>/etc/live/config.conf
LIVE_CONFIG_CMDLINE="boot=live components"
LIVE_HOSTNAME="astra"
LIVE_USERNAME="${USER_NAME}"
LIVE_USER_FULLNAME="Astra Live System User"
LIVE_USER_DEFAULT_GROUPS="dialout,cdrom,floppy,audio,video,plugdev,users,fuse,weston-launch,kvm,libvirt,libvirt-qemu,vboxusers,astra-admin,astra-console,lpadmin"
LIVE_LOCALES="ru_RU.UTF-8,en_US.UTF-8"
LIVE_TIMEZONE="Europe/Moscow"
LIVE_KEYBOARD_MODEL="pc105"
LIVE_KEYBOARD_LAYOUTS="us,ru"
LIVE_KEYBOARD_VARIANTS=","
LIVE_CONFIG_DEBUG="true"
EOF

    # Create required groups
    create_group "astra-admin"
    create_group "astra-console" 333

    # Update password for live image configuration
    if [[ "${USER_NAME}" == "root" ]]; then
        usermod -p "${PASSWORD_HASH}" root
    else
        sed -i "s|^\(\s*\)_PASSWORD=.*|\1_PASSWORD='${PASSWORD_HASH}'|" /lib/live/config/0030-live-debconfig_passwd
        sed -i "s|^\(\s*\)_PASSWORD=.*|\1_PASSWORD='${PASSWORD_HASH}'|" /lib/live/config/0030-user-setup
    fi

elif [[ " ${GENERATE_IMAGES[@]} " =~ " raw " ]] || [[ " ${GENERATE_IMAGES[@]} " =~ " qcow2 " ]] || [[ " ${GENERATE_IMAGES[@]} " =~ " vmdk " ]]; then
    # Update adduser configuration if available
    if [[ -f /etc/adduser.conf ]]; then
        sed -i 's/^#ADD_EXTRA_GROUPS=.*/ADD_EXTRA_GROUPS=1/' /etc/adduser.conf
        sed -i 's/^#EXTRA_GROUPS=.*/EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users fuse weston-launch kvm libvirt libvirt-qemu vboxusers"/' /etc/adduser.conf
        sed -i 's/^ADD_EXTRA_GROUPS=.*/ADD_EXTRA_GROUPS=1/' /etc/adduser.conf
        sed -i 's/^EXTRA_GROUPS=.*/EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users fuse weston-launch kvm libvirt libvirt-qemu vboxusers"/' /etc/adduser.conf
    fi

    # Create or update user
    if [[ "${USER_NAME}" == "root" ]]; then
        usermod -p "${PASSWORD_HASH}" root
    else
        if id "${USER_NAME}" &>/dev/null; then
            usermod -p "${PASSWORD_HASH}" "${USER_NAME}"
        else
            useradd -p "${PASSWORD_HASH}" -m -s /bin/bash "${USER_NAME}"
        fi
    fi

    # Create required groups
    create_group "astra-admin"
    create_group "astra-console" 333

    # Add user to desired groups if user is not root
    if [[ "${USER_NAME}" != "root" ]]; then
        DESIRED_GROUPS=(dialout cdrom floppy audio video plugdev users fuse weston-launch kvm libvirt libvirt-qemu vboxusers astra-admin astra-console lpadmin)
        EXISTING_GROUPS=()
        for GROUP in "${DESIRED_GROUPS[@]}"; do
            if getent group "${GROUP}" >/dev/null; then
                EXISTING_GROUPS+=("${GROUP}")
            fi
        done
        if [ ${#EXISTING_GROUPS[@]} -gt 0 ]; then
            GROUPS_CSV=$(printf "%s," "${EXISTING_GROUPS[@]}")
            GROUPS_CSV=${GROUPS_CSV%,}
            usermod -aG "${GROUPS_CSV}" "${USER_NAME}"
        fi
    fi
fi

# Ensure root password is updated
usermod -p "${PASSWORD_HASH}" root

# Enable autologin if specified
if [[ "${AUTOLOGIN}" == "true" ]] && ([[ " ${GENERATE_IMAGES[@]} " =~ " raw " ]] || [[ " ${GENERATE_IMAGES[@]} " =~ " qcow2 " ]] || [[ " ${GENERATE_IMAGES[@]} " =~ " vmdk " ]]); then
    if [ -f /etc/X11/fly-dm/fly-dmrc ]; then
        sed -i 's/#\?AutoLoginEnable=.*/AutoLoginEnable=true/' /etc/X11/fly-dm/fly-dmrc
        sed -i "s/#\?AutoLoginUser=.*/AutoLoginUser=${USER_NAME}/" /etc/X11/fly-dm/fly-dmrc
        sed -i 's/#\?AutoLoginDelay=.*/AutoLoginDelay=0/' /etc/X11/fly-dm/fly-dmrc
    fi

    if [ -f /usr/share/fly-wm/theme/default.themerc ]; then
        sed -i 's/ScreenSaverDelay=.*/ScreenSaverDelay=0/' /usr/share/fly-wm/theme/default.themerc
    fi

    if [ -f /usr/bin/fly-first-start.sh ]; then
        sed -i '3i exit 0' /usr/bin/fly-first-start.sh
    fi

    if [ -f /usr/share/fly-wm/sessrc ]; then
        sed -i 's/UseConfirmDialog=.*/UseConfirmDialog=false/' /usr/share/fly-wm/sessrc
    fi
fi

# Enable composte by default
if [ -f /usr/share/fly-wm/theme/default.themerc ]; then
    sed -i 's/UseComposite=.*/UseComposite=true/' /usr/share/fly-wm/theme/default.themerc
fi
