#!/usr/bin/env bash
set -euo pipefail

APP_ID="com.trustkernel.plugos.client"
RUNTIME="org.gnome.Platform//49"
RULES_FILE="/etc/udev/rules.d/70-plugos-usb.rules"
FLATPAK_URL="https://plugos.net/assets/linux-updates/PlugOS.flatpak"

log() {
    printf '[PlugOS] %s\n' "$*" >&2
}

die() {
    printf '[PlugOS] Error: %s\n' "$*" >&2
    exit 1
}

die_with_help() {
    local message="$1"
    shift

    printf '[PlugOS] Error: %s\n' "$message" >&2
    if [[ $# -gt 0 ]]; then
        printf '[PlugOS] What to do:\n' >&2
        for item in "$@"; do
            printf '[PlugOS]   - %s\n' "$item" >&2
        done
    fi
    exit 1
}

need_cmd() {
    command -v "$1" >/dev/null 2>&1 || die "missing command: $1"
}

join_by() {
    local delimiter="$1"
    shift

    local first=1
    for item in "$@"; do
        if [[ $first -eq 1 ]]; then
            printf '%s' "$item"
            first=0
        else
            printf '%s%s' "$delimiter" "$item"
        fi
    done
}

if [[ ${EUID:-$(id -u)} -eq 0 ]]; then
    die "please run this script as a normal user, not root"
fi

need_cmd sudo

download_flatpak_file() {
    local download_dir
    local output_file

    [[ -n "$FLATPAK_URL" ]] || die "flatpak download url is empty"

    download_dir="$(mktemp -d)"
    output_file="${download_dir}/$(basename "$FLATPAK_URL")"

    log "downloading app package from: $FLATPAK_URL"
    curl -fL --retry 3 --connect-timeout 15 -o "$output_file" "$FLATPAK_URL" || die_with_help \
        "failed to download flatpak package" \
        "Please check the network connection and download URL, then try again."

    [[ -s "$output_file" ]] || die "downloaded flatpak file is empty: $output_file"
    printf '%s\n' "$output_file"
}

install_base_packages() {
    if command -v apt >/dev/null 2>&1; then
        local packages=(curl flatpak xdg-desktop-portal xdg-desktop-portal-gtk gnome-keyring)
        local missing_packages=()
        local pkg

        log "installing base packages with apt"
        sudo apt update || die_with_help \
            "apt update failed" \
            "Check whether this machine can access the configured apt mirrors." \
            "Run manually: sudo apt update" \
            "If it still fails, switch to a working software source mirror and try again."

        for pkg in "${packages[@]}"; do
            if ! apt-cache show "$pkg" >/dev/null 2>&1; then
                missing_packages+=("$pkg")
            fi
        done

        if [[ ${#missing_packages[@]} -gt 0 ]]; then
            die_with_help \
                "apt software sources do not provide: $(join_by ', ' "${missing_packages[@]}")" \
                "This is usually a system software source issue, not a PlugOS package issue." \
                "Run manually: apt-cache policy $(join_by ' ' "${missing_packages[@]}")" \
                "Enable standard Debian or Ubuntu repositories, or switch to a software source mirror that provides these packages." \
                "After fixing the software source, run: sudo apt update"
        fi

        for pkg in "${packages[@]}"; do
            if dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -qx 'install ok installed'; then
                log "apt package already installed: $pkg"
                continue
            fi

            log "installing apt package: $pkg"
            sudo apt-get install -y --no-remove "$pkg" || die_with_help \
                "failed to install apt package: $pkg" \
                "Run manually: sudo apt install $pkg" \
                "If apt says the package cannot be located, fix the software sources first." \
                "If apt says there are broken dependencies, run: sudo apt --fix-broken install"
        done
        return
    fi

    if command -v dnf >/dev/null 2>&1; then
        local packages=(curl flatpak xdg-desktop-portal xdg-desktop-portal-gtk gnome-keyring)
        local pkg

        log "installing base packages with dnf"
        for pkg in "${packages[@]}"; do
            log "installing dnf package: $pkg"
            sudo dnf install -y "$pkg" || die_with_help \
                "failed to install dnf package: $pkg" \
                "Run manually: sudo dnf install $pkg" \
                "Check enabled repositories with: dnf repolist" \
                "If the package is missing, enable the repository that provides it and try again."
        done
        return
    fi

    if command -v yum >/dev/null 2>&1; then
        local packages=(curl flatpak xdg-desktop-portal xdg-desktop-portal-gtk gnome-keyring)
        local pkg

        log "installing base packages with yum"
        for pkg in "${packages[@]}"; do
            log "installing yum package: $pkg"
            sudo yum install -y "$pkg" || die_with_help \
                "failed to install yum package: $pkg" \
                "Run manually: sudo yum install $pkg" \
                "Check enabled repositories with: yum repolist" \
                "If the package is missing, enable the repository that provides it and try again."
        done
        return
    fi

    die_with_help \
        "unsupported system: apt, dnf, or yum is required" \
        "Install Flatpak and the required desktop integration packages manually for your distribution." \
        "Then rerun this script as a normal user."
}

write_udev_rules() {
    need_cmd tee
    if ! command -v udevadm >/dev/null 2>&1; then
        die_with_help \
            "missing command: udevadm" \
            "This system does not provide udev management tools, so the USB access rules cannot be reloaded automatically." \
            "Install the udev package for your distribution, then rerun this script."
    fi

    log "writing udev rules"
    sudo tee "$RULES_FILE" >/dev/null <<'EOF'
SUBSYSTEM=="usb", ATTRS{idVendor}=="389c", TAG+="uaccess", MODE="0660"
EOF

    log "reloading udev rules"
    sudo udevadm control --reload-rules || die_with_help \
        "failed to reload udev rules" \
        "Check whether the system udev service is available, then rerun this script."
    sudo udevadm trigger || die_with_help \
        "failed to trigger udev rules" \
        "Check whether the system udev service is available, then rerun this script."
}

setup_flatpak_runtime() {
    need_cmd flatpak

    log "adding flathub remote"
    flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

    log "installing runtime $RUNTIME"
    flatpak install --user -y flathub "$RUNTIME"
}

install_app() {
    local flatpak_file="$1"

    log "installing app package: $flatpak_file"
    flatpak install --user -y "$flatpak_file"
}

run_app() {
    log "starting $APP_ID"
    if flatpak run "$APP_ID"; then
        log "app started successfully"
    else
        log "failed to start app"
        log "manual launch command: flatpak run $APP_ID"
    fi
}

main() {
    local flatpak_file

    install_base_packages
    need_cmd curl
    flatpak_file="$(download_flatpak_file)"
    write_udev_rules
    setup_flatpak_runtime
    install_app "$flatpak_file"

    log "installation completed"
    log "replug the USB device if it is already connected"
    log "if you use GNOME and the app icon does not appear yet, log out and log back in"
    log "manual launch command: flatpak run $APP_ID"

    run_app
}

main "$@"

