#!/usr/bin/env bash
# arch-autoinstall.sh — automated Arch Linux base installer
#
# If /answerfile.json exists (e.g. embedded in the ISO via build.sh --preconf),
# all prompts are answered from it. Missing fields fall back to interactive prompts.
#
# Answerfile fields: drive, kernel, keymap, hostname, username, encrypt, fido2_root,
#                    fido2_user, run_tui  (password always prompted interactively)

set -Eeuo pipefail

############################################
# LOGGING
############################################
LOGFILE="$HOME/arch-autoinstall.log"
{
    echo
    echo "############################################"
    echo " Arch Auto-Install Log - Started $(date)"
    echo "############################################"
    echo
} >> "$LOGFILE"
exec > >(tee -a "$LOGFILE") 2>&1

############################################
# Error handler — TUI prompt to send log via croc
############################################
error_handler() {
    local exit_code=$? line_num="${1:-?}"
    echo "" >> "$LOGFILE"
    echo "ERROR: installer failed at line $line_num (exit code $exit_code)" >> "$LOGFILE"

    if command -v dialog &>/dev/null; then
        if dialog --clear --title " Installer Error " \
                  --yesno \
                  "Installation failed at line $line_num (exit code: $exit_code).\n\nSend the log to another system via croc for analysis?" \
                  9 62; then
            clear
            if ! command -v croc &>/dev/null; then
                echo "Installing croc..."
                pacman -Sy --noconfirm croc 2>/dev/null || true
            fi
            if command -v croc &>/dev/null; then
                croc send "$LOGFILE"
            else
                echo "croc unavailable — log is at: $LOGFILE"
            fi
        else
            clear
            echo "Log saved to: $LOGFILE"
        fi
    else
        echo ""
        echo "Installation failed at line $line_num (exit code $exit_code)."
        read -rp "Send log via croc? [y/N]: " _croc_ans
        if [[ "${_croc_ans,,}" == "y" ]]; then
            command -v croc &>/dev/null || pacman -Sy --noconfirm croc 2>/dev/null || true
            croc send "$LOGFILE" || true
        else
            echo "Log saved to: $LOGFILE"
        fi
    fi
    exit "$exit_code"
}
trap 'error_handler $LINENO' ERR

############################################
# ANSWERFILE
############################################
ANSWERFILE="${ANSWERFILE:-/answerfile.json}"
AF_MODE=false
[[ -f "$ANSWERFILE" ]] && AF_MODE=true

af_get() {
    # af_get <jq-filter> [default]
    local val
    val=$(jq -r "${1} // empty" "$ANSWERFILE" 2>/dev/null || true)
    if [[ -z "$val" ]]; then printf '%s' "${2:-}"; else printf '%s' "$val"; fi
}

af_bool() {
    # Returns YES or NO from a JSON boolean field
    local val; val=$(jq -r "${1} // false" "$ANSWERFILE" 2>/dev/null || true)
    [[ "$val" == "true" ]] && echo "YES" || echo "NO"
}

get_mac_suffix() {
    local mac
    mac=$(ip link show 2>/dev/null \
        | awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}')
    printf '%s' "${mac//:/}"
}

part() { [[ "$1" == *nvme* || "$1" == *mmcblk* ]] && echo "${1}p${2}" || echo "${1}${2}"; }

if $AF_MODE; then
    echo "Answerfile detected: $ANSWERFILE"
    # Ensure jq is available
    command -v jq &>/dev/null || pacman -Sy --noconfirm jq
fi

############################################
# NETWORK CHECK
############################################
if ! ping -c1 -W3 1.1.1.1 &>/dev/null; then
    if $AF_MODE; then
        echo "Warning: no internet connection detected — continuing in answerfile mode."
    else
        echo "No internet connection detected. Launching nmtui..."
        nmtui
        if ! ping -c1 -W3 1.1.1.1 &>/dev/null; then
            echo "Warning: still offline. Packages cannot be downloaded without network."
            read -rp "Continue anyway? [y/N]: " _net_ans
            [[ "${_net_ans,,}" == "y" ]] || { echo "Aborted — no network."; exit 1; }
        fi
    fi
fi

############################################
# KEYMAP
# To add more layouts: append "code|Display Name" to KEYMAPS
############################################
KEYMAPS=(
    "us|English US"
    "de|German"
)

if $AF_MODE; then
    LIVE_KEYMAP=$(af_get '.keymap' 'us')
else
    echo "Select keyboard layout:"
    for i in "${!KEYMAPS[@]}"; do
        _km_code="${KEYMAPS[$i]%%|*}"
        _km_name="${KEYMAPS[$i]##*|}"
        printf "  %d) %-14s (%s)\n" $((i+1)) "$_km_name" "$_km_code"
    done
    read -rp "Choice [1]: " _KM_IDX
    _KM_IDX=$(( ${_KM_IDX:-1} - 1 ))
    if (( _KM_IDX >= 0 && _KM_IDX < ${#KEYMAPS[@]} )); then
        LIVE_KEYMAP="${KEYMAPS[$_KM_IDX]%%|*}"
    else
        LIVE_KEYMAP="${KEYMAPS[0]%%|*}"
    fi
fi
loadkeys "$LIVE_KEYMAP"
KEYMAP="$LIVE_KEYMAP"

############################################
# SAFETY WARNING
############################################
if $AF_MODE; then
    echo "WARNING: Automated install — all data on $(af_get '.drive' '/dev/?') will be ERASED."
    echo "Proceeding in 5 seconds... (Ctrl-C to abort)"
    sleep 5
else
    echo "WARNING: This will ERASE ALL DATA on the selected drive!"
    read -rp "Type 'YES' to continue: " confirm
    [[ "$confirm" == "YES" ]] || { echo "Aborted."; exit 1; }
fi

############################################
# REQUIRED PACKAGES FOR INSTALL ENVIRONMENT
############################################
pacman -Sy --noconfirm parted cryptsetup libfido2 pam-u2f

############################################
# DRIVE SELECTION
############################################
lsblk
if $AF_MODE && [[ -n "$(af_get '.drive')" ]]; then
    DRIVE=$(af_get '.drive')
    echo "Drive (from answerfile): $DRIVE"
else
    read -rp "Enter target drive (e.g., /dev/sda): " DRIVE
fi

############################################
# USER INPUT
############################################
if $AF_MODE; then
    KERNEL=$(af_get '.kernel' 'linux')
    RAW_HOSTNAME=$(af_get '.hostname' '')
    if [[ -n "$RAW_HOSTNAME" ]]; then
        HOSTNAME="${RAW_HOSTNAME}-$(get_mac_suffix)"
    else
        HOSTNAME="arch"
    fi
    USERNAME=$(af_get '.username' '')
    ENCRYPT_DISK=$(af_bool '.encrypt')
    FIDO_ROOT=$(af_bool '.fido2_root')
    FIDO_USER=$(af_bool '.fido2_user')
    RUN_TUI=$(af_bool '.run_tui')
    echo "Kernel:   $KERNEL"
    echo "Hostname: $HOSTNAME"
    echo "Username: $USERNAME"
    echo "Encrypt:  $ENCRYPT_DISK  / FIDO2 root: $FIDO_ROOT  / FIDO2 user: $FIDO_USER"
else
    read -rp "Enter kernel package (e.g., linux, linux-lts): " KERNEL
    read -rp "Enter hostname: " HOSTNAME
    read -rp "Enter username: " USERNAME
    read -rp "Enable disk encryption? (YES/NO): " ENCRYPT_DISK
    FIDO_ROOT="NO"
    if [[ "$ENCRYPT_DISK" == "YES" ]]; then
        read -rp "Enable FIDO2 unlocking for root partition? (YES/NO): " FIDO_ROOT
    fi
    read -rp "Enable FIDO2 authentication for user login? (YES/NO): " FIDO_USER
fi

# Password always prompted — never stored in answerfile
read -rp "Enter password for $USERNAME: " USERPASS
[[ -z "$USERPASS" ]] && { echo "Error: password cannot be empty."; exit 1; }

if ! $AF_MODE; then
    read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI_IN
    _RUN_TUI_IN="${_RUN_TUI_IN:-YES}"
    [[ "${_RUN_TUI_IN^^}" == "YES" ]] && RUN_TUI="YES" || RUN_TUI="NO"
fi

############################################
# RAM / PARTITION SIZING
############################################
RAM_GB=$(free --giga | awk '/^Mem:/ {print $2}')
BOOT_SIZE=15GiB
SWAP_SIZE="${RAM_GB}GiB"

DISK_SIZE=$(lsblk -b -dn -o SIZE "$DRIVE")
DISK_GIB=$((DISK_SIZE / 1024 / 1024 / 1024))
ROOT_GIB=$((DISK_GIB - RAM_GB - 15))
if (( ROOT_GIB < 8 )); then
    echo "ERROR: Not enough disk space. Root would be only ${ROOT_GIB}GiB (need ≥8GiB)."
    exit 1
fi

echo "Partition plan:"
echo "  Boot: ${BOOT_SIZE}"
echo "  Root: ${ROOT_GIB}GiB"
echo "  Swap: ${SWAP_SIZE}"

############################################
# PARTITION DISK
############################################
parted "$DRIVE" --script mklabel gpt \
    mkpart ESP fat32 1MiB 15GiB \
    set 1 boot on \
    mkpart ROOT 15GiB "$((15 + ROOT_GIB))"GiB \
    mkpart SWAP "$((15 + ROOT_GIB))"GiB 100%

BOOT_PART=$(part "$DRIVE" 1)
ROOT_PART=$(part "$DRIVE" 2)
SWAP_PART=$(part "$DRIVE" 3)

############################################
# FORMAT BOOT + SWAP
############################################
mkfs.fat -F32 "$BOOT_PART"
mkswap "$SWAP_PART"
swapon "$SWAP_PART"

############################################
# ENCRYPTION (OPTIONAL)
############################################
LUKS_BACKUP_KEY=""   # path to key file, set only when encryption is active

if [[ "$ENCRYPT_DISK" == "YES" ]]; then
    echo "Encrypting root partition..."
    cryptsetup -v luksFormat "$ROOT_PART"
    cryptsetup open "$ROOT_PART" cryptroot

    # ── Auto-generate backup LUKS key ──────────────────────────────────────────
    # A random key is enrolled as a second LUKS slot so recovery is possible
    # without the primary passphrase. It is written to /_LUKS_BACKUP_KEY in the
    # new system (inside the encrypted container) where only root can read it.
    LUKS_BACKUP_KEY=$(mktemp /tmp/luks-backup-key.XXXXXX)
    dd if=/dev/urandom bs=64 count=1 2>/dev/null | base64 -w0 > "$LUKS_BACKUP_KEY"
    echo "Enrolling auto-generated backup LUKS key..."
    cryptsetup luksAddKey "$ROOT_PART" "$LUKS_BACKUP_KEY"

    # ── Optional FIDO2 enrollment ─────────────────────────────────────────────
    if [[ "$FIDO_ROOT" == "YES" ]]; then
        echo "Insert FIDO2 key for LUKS and touch when prompted..."
        systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no
    fi

    ############################################
    # BTRFS ON ENCRYPTED ROOT
    ############################################
    mkfs.btrfs /dev/mapper/cryptroot
    mount /dev/mapper/cryptroot /mnt
    btrfs subvolume create /mnt/@
    btrfs subvolume create /mnt/@home
    umount /mnt

    mount -o subvol=@ /dev/mapper/cryptroot /mnt
    mkdir -p /mnt/home
    mount -o subvol=@home /dev/mapper/cryptroot /mnt/home

else
    echo "Skipping encryption — formatting root directly."

    ############################################
    # BTRFS ON UNENCRYPTED ROOT
    ############################################
    mkfs.btrfs "$ROOT_PART"
    mount "$ROOT_PART" /mnt
    btrfs subvolume create /mnt/@
    btrfs subvolume create /mnt/@home
    umount /mnt

    mount -o subvol=@ "$ROOT_PART" /mnt
    mkdir -p /mnt/home
    mount -o subvol=@home "$ROOT_PART" /mnt/home
fi

mkdir -p /mnt/boot
mount "$BOOT_PART" /mnt/boot

# Place backup key inside the new system (only accessible when disk is unlocked)
if [[ -n "$LUKS_BACKUP_KEY" ]]; then
    install -m 400 "$LUKS_BACKUP_KEY" /mnt/_LUKS_BACKUP_KEY
    rm -f "$LUKS_BACKUP_KEY"
    echo "Backup LUKS key written to /_LUKS_BACKUP_KEY in new system."
fi

############################################
# GPU DETECTION
############################################
GPU_INFO=$(lspci | grep -E "VGA|3D" || true)
GPU_PKGS=""
if echo "$GPU_INFO" | grep -qi "NVIDIA"; then
    GPU_PKGS="nvidia nvidia-utils"
elif echo "$GPU_INFO" | grep -qi "AMD"; then
    GPU_PKGS="xf86-video-amdgpu"
elif echo "$GPU_INFO" | grep -qi "Intel"; then
    GPU_PKGS="xf86-video-intel"
fi
echo "Detected GPU: ${GPU_INFO:-none}"

############################################
# BASE INSTALL
############################################
# shellcheck disable=SC2086
pacstrap -K /mnt base base-devel "$KERNEL" linux-firmware vim bash zsh git less btop fastfetch \
    networkmanager grub cryptsetup libfido2 pam-u2f efibootmgr sudo btrfs-progs lvm2 jq $GPU_PKGS

############################################
# FSTAB
############################################
genfstab -U /mnt >> /mnt/etc/fstab

############################################
# COPY ANSWERFILE INTO NEW SYSTEM
############################################
if $AF_MODE; then
    install -m 644 "$ANSWERFILE" /mnt/answerfile.json
fi

############################################
# PASS VARIABLES INTO CHROOT
############################################
export HOSTNAME USERNAME USERPASS ROOT_PART KERNEL FIDO_ROOT FIDO_USER ENCRYPT_DISK KEYMAP

############################################
# CHROOT CONFIGURATION
############################################
arch-chroot /mnt /bin/bash <<'CHROOT_EOF'
set -euo pipefail

# Locale
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf
echo "KEYMAP=${KEYMAP}" > /etc/vconsole.conf

# Time / hostname
ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
hwclock --systohc
echo "$HOSTNAME" > /etc/hostname

# NetworkManager
systemctl enable NetworkManager

# User
useradd -m -G wheel -s /bin/zsh "$USERNAME"
echo "$USERNAME:$USERPASS" | chpasswd
echo "%wheel ALL=(ALL:ALL) ALL" >> /etc/sudoers

###################################################
# INITRAMFS CONFIG
###################################################
if [[ "$ENCRYPT_DISK" == "YES" && "$FIDO_ROOT" == "YES" ]]; then
    sed -i 's/^HOOKS=.*/HOOKS=(base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
elif [[ "$ENCRYPT_DISK" == "YES" ]]; then
    sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
else
    sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block btrfs filesystems keyboard fsck)/' /etc/mkinitcpio.conf
fi

mkinitcpio -p "$KERNEL"

###################################################
# GRUB CONFIG
###################################################
UUID=$(blkid -s UUID -o value "$ROOT_PART")
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
    if [[ "$FIDO_ROOT" == "YES" ]]; then
        KERNEL_CMD="rd.luks.name=${UUID}=cryptroot root=/dev/mapper/cryptroot"
    else
        KERNEL_CMD="cryptdevice=UUID=${UUID}:cryptroot root=/dev/mapper/cryptroot"
    fi
else
    KERNEL_CMD="root=UUID=${UUID} rootflags=subvol=@"
fi

sed -i "s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"$KERNEL_CMD\"|" /etc/default/grub

grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=M-Archy-GRUB
grub-mkconfig -o /boot/grub/grub.cfg

###################################################
# USER FIDO2 LOGIN
###################################################
if [[ "$FIDO_USER" == "YES" ]]; then
    mkdir -p "/home/$USERNAME/.config/Yubico"
    echo "Insert FIDO2 key for user login and touch when prompted..."
    sudo -u "$USERNAME" pamu2fcfg -u "$USERNAME" > "/home/$USERNAME/.config/Yubico/u2f_keys"
    chown "$USERNAME":"$USERNAME" "/home/$USERNAME/.config/Yubico/u2f_keys"
    echo "auth required pam_u2f.so" >> /etc/pam.d/system-local-login
fi

###################################################
# CLONE DOTFILES
###################################################
echo "Cloning dotfiles..."
git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git "/home/$USERNAME/Dotfiles" \
    && chown -R "$USERNAME":"$USERNAME" "/home/$USERNAME/Dotfiles" \
    || echo "Warning: dotfiles clone failed — clone manually after first boot."

CHROOT_EOF

############################################
# DOTFILES TUI SETUP (in-chroot, optional)
############################################
if [[ "${RUN_TUI^^}" == "YES" ]]; then
    echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" \
        | arch-chroot /mnt tee /etc/sudoers.d/99-setup-nopasswd > /dev/null

    echo "Running tui-install.sh as ${USERNAME} inside chroot..."
    arch-chroot /mnt runuser -u "${USERNAME}" -- \
        bash "/home/${USERNAME}/Dotfiles/setup/tui-install.sh" \
        || echo "Warning: tui-install exited with errors — check ~/dotfiles-install.log in the new system."

    arch-chroot /mnt rm -f /etc/sudoers.d/99-setup-nopasswd
fi

# Remove answerfile from new system after setup is complete (contains sensitive paths/config)
if $AF_MODE && [[ -f /mnt/answerfile.json ]]; then
    rm -f /mnt/answerfile.json
fi

############################################
# SUMMARY
############################################
echo
echo "############################################"
echo " INSTALL SUMMARY"
echo "############################################"
echo "Drive:             $DRIVE"
echo "Boot partition:    $BOOT_PART"
echo "Root partition:    $ROOT_PART"
echo "Swap partition:    $SWAP_PART"
echo
echo "Hostname:          $HOSTNAME"
echo "Username:          $USERNAME"
echo "Kernel:            $KERNEL"
echo "GPU detected:      ${GPU_INFO:-none}"
echo
echo "Disk encryption:   $ENCRYPT_DISK"
echo "FIDO2 root unlock: $FIDO_ROOT"
echo "FIDO2 user login:  $FIDO_USER"
[[ "$ENCRYPT_DISK" == "YES" ]] && echo "LUKS backup key:   /_LUKS_BACKUP_KEY (in new system)"
echo
echo "Boot size:         $BOOT_SIZE"
echo "Root size:         ${ROOT_GIB}GiB"
echo "Swap size:         $SWAP_SIZE"
echo
echo "Log file:          $LOGFILE"
echo "############################################"
echo

cp "$LOGFILE" /mnt/boot/ 2>/dev/null || true

echo "Installation complete! Unmount and reboot:"
echo "  umount -R /mnt && reboot"
if [[ "${RUN_TUI^^}" != "YES" ]]; then
    echo
    echo "After first boot, login as $USERNAME and run:"
    echo "  ~/Dotfiles/setup/tui-install.sh"
fi
