#!/usr/bin/env bash
# archbaseos-guided-install.sh — guided (dialog-based) Arch Linux base installer
#
# If /answerfile.json exists (e.g. embedded via build.sh --preconf), all prompts
# are answered from it. Missing fields fall back to interactive prompts.

set -Eeuo pipefail

############################################
# LOGGING
############################################
LOGFILE="$HOME/archbaseos-guided-install.log"
{
    echo
    echo "############################################"
    echo " Arch Guided 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"

    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
    exit "$exit_code"
}
trap 'error_handler $LINENO' ERR

############################################
# Helper Functions
############################################

confirm() {
    echo "WARNING: This will ERASE ALL DATA on $1"
    read -rp "Type YES to continue: " ans
    [[ $ans == "YES" ]]
}

ask() {
    local prompt=$1
    local var
    read -rp "$prompt: " var
    echo "$var"
}

pause() {
    read -rp "Press ENTER to continue..."
}

# Returns the correct partition device for a given drive and partition number.
# NVMe and eMMC use a 'p' separator (e.g. /dev/nvme0n1p1), others don't.
part() { [[ "$1" == *nvme* || "$1" == *mmcblk* ]] && echo "${1}p${2}" || echo "${1}${2}"; }

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

af_get() {
    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() {
    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//:/}"
}

if $AF_MODE; then
    echo "== Arch Linux Guided Installer (answerfile mode) =="
    command -v jq &>/dev/null || pacman -Sy --noconfirm jq
else
    echo "== Arch Linux FIDO2-Ready Installer =="
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

############################################
# Begin
############################################

KEYMAPS=(
    "us|English US"
    "de|German"
)

############################################
# 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')
    ENABLE_FIDO_ROOT=$(af_bool '.fido2_root')
    ENABLE_FIDO_USER=$(af_bool '.fido2_user')
    RUN_TUI=$(af_bool '.run_tui')
    KEYMAP=$(af_get '.keymap' 'us')
    echo "Kernel:   $KERNEL / Hostname: $HOSTNAME / Username: $USERNAME"
    echo "Encrypt:  $ENCRYPT_DISK / FIDO2 root: $ENABLE_FIDO_ROOT / FIDO2 user: $ENABLE_FIDO_USER"
    echo "Keymap:   $KEYMAP"
else
    KERNEL=$(ask "Kernel (linux, linux-lts, linux-zen)")
    HOSTNAME=$(ask "Hostname")
    USERNAME=$(ask "Username")
    read -rp "Enable disk encryption? (YES/NO): " ENCRYPT_DISK
    ENABLE_FIDO_ROOT="NO"
    if [[ "$ENCRYPT_DISK" == "YES" ]]; then
        read -rp "Enable FIDO2 for unlocking root? (YES/NO): " ENABLE_FIDO_ROOT
    fi
    read -rp "Enable FIDO2 for user login? (YES/NO): " ENABLE_FIDO_USER

    echo ""
    echo "Select keyboard layout for installed system:"
    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_CHOICE
    _KM_CHOICE=$(( ${_KM_CHOICE:-1} - 1 ))
    if (( _KM_CHOICE >= 0 && _KM_CHOICE < ${#KEYMAPS[@]} )); then
        KEYMAP="${KEYMAPS[$_KM_CHOICE]%%|*}"
    else
        KEYMAP="${KEYMAPS[0]%%|*}"
    fi
fi

read -rp "Password for $USERNAME: " USERPASS
[[ -z "$USERPASS" ]] && { echo "Error: password cannot be empty."; exit 1; }

lsblk

if $AF_MODE && [[ -n "$(af_get '.drive')" ]]; then
    DRIVE=$(af_get '.drive')
    echo "Drive (from answerfile): $DRIVE"
    echo "WARNING: All data on $DRIVE will be erased. Proceeding in 5 seconds..."
    sleep 5
else
    DRIVE=$(ask "Enter install drive (e.g., /dev/sda)")
    confirm "$DRIVE" || exit 1
fi

# Required packages
pacman -Syd --noconfirm parted cryptsetup libfido2 pam-u2f systemd-ukify jq

############################################
# Partitioning
############################################

RAM_GB=$(free --giga | awk '/Mem/ {print $2}')
DISK_GB=$(lsblk -dn -o SIZE -b "$DRIVE" | awk '{print int($1/1024/1024/1024)}')

EFI_SIZE=5
SWAP_SIZE=$RAM_GB
ROOT_SIZE=$((DISK_GB - SWAP_SIZE - EFI_SIZE - 1))

if (( ROOT_SIZE < 8 )); then
    echo "ERROR: Disk too small for layout."
    exit 1
fi

echo "EFI=${EFI_SIZE}G, Root=${ROOT_SIZE}G, Swap=${SWAP_SIZE}G"

parted -s "$DRIVE" mklabel gpt \
    mkpart EFI fat32 1MiB "${EFI_SIZE}GiB" \
    set 1 esp on \
    mkpart ROOT "${EFI_SIZE}GiB" "$((EFI_SIZE + ROOT_SIZE))GiB" \
    mkpart SWAP "$((EFI_SIZE + ROOT_SIZE))GiB" 100%

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

mkfs.fat -F32 "$EFI_PART"
mkswap "$SWAP_PART"
swapon "$SWAP_PART"

############################################
# Encryption (optional)
############################################
LUKS_BACKUP_KEY=""

if [[ "$ENCRYPT_DISK" == "YES" ]]; then
    echo "Formatting LUKS2 root..."
    cryptsetup luksFormat "$ROOT_PART" --type luks2
    cryptsetup open "$ROOT_PART" cryptroot

    # ── Auto-generate backup LUKS key ─────────────────────────────────────────
    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"

    if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then
        echo "Enroll FIDO2 key for LUKS2"
        pause
        systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no
    fi

    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."

    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 "$EFI_PART" /mnt/boot

# Place backup key inside the new system (readable only by root, inside LUKS container)
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

############################################
# Base System Install
############################################
GPU_INFO=$(lspci | grep -E "VGA|3D" || true)
GPU_PKGS=""

if echo "$GPU_INFO" | grep -qi nvidia; then
    GPU_PKGS="nvidia-open"
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

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

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

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

############################################
# CHROOT Configuration
############################################
ROOT_UUID=$(blkid -s UUID -o value "$ROOT_PART")

arch-chroot /mnt /usr/bin/env \
  HOSTNAME="$HOSTNAME" \
  USERNAME="$USERNAME" \
  USERPASS="$USERPASS" \
  ENCRYPT_DISK="$ENCRYPT_DISK" \
  ENABLE_FIDO_ROOT="$ENABLE_FIDO_ROOT" \
  ENABLE_FIDO_USER="$ENABLE_FIDO_USER" \
  ROOT_UUID="$ROOT_UUID" \
  ROOT_PART="$ROOT_PART" \
  KEYMAP="$KEYMAP" \
  /bin/bash <<'CHROOT_EOF'

set -euo pipefail

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

ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
hwclock --systohc

echo "$HOSTNAME" > /etc/hostname
systemctl enable NetworkManager

# Populate /etc/skel before user creation so useradd -m copies everything
echo "Cloning dotfiles into /etc/skel..."
git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git /etc/skel/Dotfiles \
    || echo "Warning: dotfiles clone failed — clone manually after first boot."

mkdir -p /etc/skel/{Desktop,Documents,Downloads,Music,Pictures,Public,Templates,Videos}

useradd -m -G wheel -s /bin/zsh "$USERNAME"
echo "$USERNAME:$USERPASS" | chpasswd
chown -R "$USERNAME:$USERNAME" "/home/$USERNAME"

echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers

# Initramfs
if [[ "$ENCRYPT_DISK" == "YES" && "$ENABLE_FIDO_ROOT" == "YES" ]]; then
    sed -i 's/^HOOKS=.*/HOOKS=(base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt 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 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

# GRUB
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
    if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then
        GRUB_CMDLINE="rd.luks.name=$ROOT_UUID=cryptroot rd.luks.options=fido2-device=auto root=/dev/mapper/cryptroot"
    else
        GRUB_CMDLINE="cryptdevice=UUID=$ROOT_UUID:cryptroot root=/dev/mapper/cryptroot"
    fi
else
    GRUB_CMDLINE="root=UUID=${ROOT_UUID} rootflags=subvol=@"
fi

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

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

# User login FIDO2 — directory + PAM only; key enrollment happens outside chroot
if [[ "$ENABLE_FIDO_USER" == "YES" ]]; then
    mkdir -p "/home/$USERNAME/.config/Yubico"
    chown "$USERNAME:$USERNAME" "/home/$USERNAME/.config/Yubico"
    echo "auth required pam_u2f.so" >> /etc/pam.d/system-auth
fi

CHROOT_EOF

# pamu2fcfg must run outside arch-chroot: inside the chroot the host's udev manages
# /dev/hidraw* permissions and the new user has no access to the device.
if [[ "$ENABLE_FIDO_USER" == "YES" ]]; then
    echo "Enrolling FIDO2 key for user login (outside chroot)..."
    U2F_KEYFILE="/mnt/home/${USERNAME}/.config/Yubico/u2f_keys"
    mkdir -p "/mnt/home/${USERNAME}/.config/Yubico"
    pamu2fcfg -u "$USERNAME" > "$U2F_KEYFILE"
    _NEWUID=$(arch-chroot /mnt id -u "$USERNAME" 2>/dev/null || echo "1000")
    _NEWGID=$(arch-chroot /mnt id -g "$USERNAME" 2>/dev/null || echo "1000")
    chown -R "$_NEWUID:$_NEWGID" "/mnt/home/${USERNAME}/.config/Yubico"
    chmod 600 "$U2F_KEYFILE"
    echo "FIDO2 key enrolled for $USERNAME."
fi

############################################
# DOTFILES SETUP (in-chroot, optional)
############################################
if $AF_MODE; then
    _DO_TUI="${RUN_TUI}"
else
    read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _TUI_IN
    _TUI_IN="${_TUI_IN:-YES}"
    [[ "${_TUI_IN^^}" == "YES" ]] && _DO_TUI="YES" || _DO_TUI="NO"
fi

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

    echo "Running simple-install.sh as ${USERNAME} inside chroot..."
    arch-chroot /mnt runuser -u "${USERNAME}" -- \
        bash "/home/${USERNAME}/Dotfiles/setup/simple-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 completes
if $AF_MODE && [[ -f /mnt/answerfile.json ]]; then
    rm -f /mnt/answerfile.json
fi

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

echo "Installation complete! Log saved to /mnt/boot/$(basename "$LOGFILE")"
echo "  umount -R /mnt && reboot"
if [[ "${_DO_TUI^^}" != "YES" ]]; then
    echo
    echo "After first boot, login as ${USERNAME} and run:"
    echo "  ~/Dotfiles/setup/simple-install.sh"
fi
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
    echo
    echo "LUKS backup key stored at /_LUKS_BACKUP_KEY in the new system."
    echo "Keep this file secure — it can unlock the root partition."
fi
