#!/bin/bash
set -u

readonly ONIX_MODULE_DIR=/usr/lib/onix-configure
readonly ONIX_LOCK=/run/lock/onix-configure.lock
ONIX_MODE="${1:---repair}"
readonly ONIX_SYSTEM_CONFIG=/etc/onix/system-fix.conf
ONIX_USER_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/onix/configure.conf"

# mkarchiso owns ISO rootfs assembly; skip normal repair hooks only while
# those package transactions are running inside an ISO build.
if [[ "${ONIX_ISO_BUILD:-0}" == 1 &&
      ( "$ONIX_MODE" == --first-install || "$ONIX_MODE" == --transaction ) ]]; then
    exit 0
fi

[[ -r "$ONIX_SYSTEM_CONFIG" ]] && . "$ONIX_SYSTEM_CONFIG"
[[ -r "$ONIX_USER_CONFIG" ]] && . "$ONIX_USER_CONFIG"
: "${ONIX_SAFE_OPTIMIZATION:=1}"
: "${ONIX_ENABLE_FSTRIM:=1}"
: "${ONIX_REPAIR_BOOT:=1}"
: "${ONIX_POWER_BACKEND:=auto}"
: "${ONIX_DESKTOP_OVERRIDE:=auto}"
: "${ONIX_FORM_FACTOR_OVERRIDE:=auto}"

for module in detect hardware display repair-pacman repair-boot repair-power repair-graphics repair-audio repair-network desktop-common \
    desktop-gnome desktop-kde desktop-xfce desktop-mate desktop-cinnamon desktop-lxqt desktop-budgie; do
    # shellcheck disable=SC1090
    [[ -r "$ONIX_MODULE_DIR/$module.sh" ]] && . "$ONIX_MODULE_DIR/$module.sh"
done

onix_require_root() {
    (( EUID == 0 )) || { echo "onix-configure: root privileges are required" >&2; exit 1; }
}

onix_lock() {
    install -d -m 0755 /run/lock
    exec 9>"$ONIX_LOCK"
    flock -n 9 || { onix_log "another configuration run is active"; exit 0; }
}

onix_log() { printf 'onix-configure: %s\n' "$*"; }
onix_warn() { printf 'onix-configure: %s\n' "$*" >&2; }

onix_config_path() {
    (( EUID == 0 )) && printf '%s\n' "$ONIX_SYSTEM_CONFIG" || printf '%s\n' "$ONIX_USER_CONFIG"
}

onix_set_option() {
    local assignment="$1" key value config
    [[ "$assignment" == *=* ]] || { onix_warn 'option must be key=value'; return 2; }
    key=${assignment%%=*}; value=${assignment#*=}
    case "$key" in
        ONIX_SAFE_OPTIMIZATION|ONIX_ENABLE_FSTRIM|ONIX_REPAIR_BOOT)
            [[ "$value" == 0 || "$value" == 1 ]] || { onix_warn "$key accepts 0 or 1"; return 2; } ;;
        ONIX_POWER_BACKEND) [[ "$value" =~ ^(auto|ppd|tlp|none)$ ]] || { onix_warn 'power backend must be auto, ppd, tlp or none'; return 2; } ;;
        ONIX_DESKTOP_OVERRIDE) [[ "$value" =~ ^(auto|gnome|kde|xfce|mate|cinnamon|lxqt|budgie)$ ]] || { onix_warn 'unsupported desktop profile'; return 2; } ;;
        ONIX_FORM_FACTOR_OVERRIDE) [[ "$value" =~ ^(auto|laptop|desktop)$ ]] || { onix_warn 'form factor must be auto, laptop or desktop'; return 2; } ;;
        *) onix_warn "unknown option: $key"; return 2 ;;
    esac
    config=$(onix_config_path)
    install -d -m 0755 "$(dirname "$config")"
    touch "$config"
    if grep -qE "^${key}=" "$config"; then
        sed -i "s|^${key}=.*|${key}=${value}|" "$config"
    else
        printf '%s=%s\n' "$key" "$value" >> "$config"
    fi
    onix_log "$key updated in $config"
}

onix_install_desktop() {
    local desktop="$1"; shift
    local packages=()
    case "$desktop" in
        gnome) packages=(gnome gdm) ;;
        kde) packages=(plasma sddm) ;;
        xfce) packages=(xfce4 xfce4-goodies lightdm lightdm-gtk-greeter) ;;
        mate) packages=(mate mate-extra lightdm lightdm-gtk-greeter) ;;
        cinnamon) packages=(cinnamon lightdm lightdm-gtk-greeter) ;;
        lxqt) packages=(lxqt sddm) ;;
        budgie) packages=(budgie-desktop gdm) ;;
        *) onix_warn 'automatic package installation is unavailable for this desktop'; return 2 ;;
    esac
    (( EUID == 0 )) || { onix_warn 'desktop package installation requires root'; return 1; }
    /usr/bin/pacman -Syu --needed --noconfirm "${packages[@]}"
}

onix_interactive() {
    local answer profile desktop power
    echo 'Onix automatic configuration'
    select profile in auto laptop desktop; do [[ -n "${profile:-}" ]] && break; done
    onix_set_option "ONIX_FORM_FACTOR_OVERRIDE=$profile"
    select desktop in auto gnome kde xfce mate cinnamon lxqt budgie; do [[ -n "${desktop:-}" ]] && break; done
    onix_set_option "ONIX_DESKTOP_OVERRIDE=$desktop"
    select power in auto ppd tlp none; do [[ -n "${power:-}" ]] && break; done
    onix_set_option "ONIX_POWER_BACKEND=$power"
    read -r -p 'Safe performance defaults? [Y/n] ' answer
    [[ "${answer,,}" != n ]] && onix_set_option 'ONIX_SAFE_OPTIMIZATION=1' || onix_set_option 'ONIX_SAFE_OPTIMIZATION=0'
    read -r -p 'Enable periodic SSD trim? [Y/n] ' answer
    [[ "${answer,,}" != n ]] && onix_set_option 'ONIX_ENABLE_FSTRIM=1' || onix_set_option 'ONIX_ENABLE_FSTRIM=0'
    if [[ "$desktop" != auto && "$desktop" != none ]] && (( EUID == 0 )); then
        read -r -p "Install recommended $desktop packages? [y/N] " answer
        [[ "${answer,,}" == y ]] && onix_install_desktop "$desktop"
    fi
    onix_log 'configuration saved; applying it now'
    ONIX_MODE=--repair
}

onix_run_system() {
    onix_detect
    onix_log "detected $ONIX_FORM_FACTOR, $ONIX_DESKTOP, $ONIX_GPU, session=$ONIX_SESSION"
    onix_repair_pacman
    onix_repair_runtime
    onix_repair_power
    onix_repair_graphics
    onix_repair_audio
    onix_repair_network
    onix_repair_display
    onix_repair_boot
    onix_configure_desktop
}

onix_run_user() {
    onix_detect
    onix_configure_user_desktop
    onix_configure_user_display
}

case "$ONIX_MODE" in
    --configure|--interactive)
        onix_interactive
        if (( EUID == 0 )); then onix_lock; onix_run_system; else onix_run_user; fi
        ;;
    --set)
        [[ -n "${2:-}" ]] || { onix_warn 'usage: onix-configure --set KEY=VALUE'; exit 2; }
        onix_set_option "$2"
        ;;
    --show-config)
        config=$(onix_config_path)
        [[ -r "$config" ]] && cat "$config" || echo "No configuration saved at $config"
        ;;
    --first-install|--repair|--transaction)
        onix_require_root
        onix_lock
        onix_run_system
        ;;
    --user)
        onix_run_user
        ;;
    --diagnose)
        onix_detect
        printf 'form_factor=%s\ndesktop=%s\nsession=%s\ndisplay=%s\nxwayland=%s\nxrandr=%s\ngpu=%s\ncpu=%s\ncores=%s\nram_mb=%s\nfirmware=%s\nnetwork=%s\naudio=%s\nstorage=%s\n' \
            "$ONIX_FORM_FACTOR" "$ONIX_DESKTOP" "$ONIX_SESSION" "$ONIX_DISPLAY" "$ONIX_XWAYLAND" "$ONIX_XRANDR" "$ONIX_GPU" \
            "$ONIX_CPU" "$ONIX_CORES" "$ONIX_RAM_MB" "$ONIX_FIRMWARE" \
            "$ONIX_NETWORK" "$ONIX_AUDIO" "$ONIX_STORAGE"
        ;;
    --help|-h)
        echo "Usage: onix-configure [--configure|--set KEY=VALUE|--show-config|--repair|--user|--diagnose]"
        ;;
    *) onix_warn "unknown option: $ONIX_MODE"; exit 2 ;;
esac
