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

origin='github.com/bottlesdevs/bottles'
min_version='2.14.13'
cpak_bin="${BOTTLES_CPAK_BIN:-/usr/bin/cpak}"
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/bottles-cpak"
umask 077
mkdir -p -- "$state_dir"
log="$state_dir/launch.log"

usage() {
    cat <<'USAGE'
Usage: bottles-cpak [setup [bottles|business|enterprise] | run [ARGS...] | install | update | remove | doctor | help]

  setup     Configure Bottles for this user, optionally adding Microsoft 365
  run       Start Bottles without running Office setup
  install   Install official Bottles into the current user's CPak store
  update    Update the current user's Bottles installation
  remove    Uninstall Bottles from CPak without purging personal data
  doctor    Run CPak's environment diagnostics
USAGE
}

log_message() {
    printf '[%s] %s\n' "$(date -Is)" "$*" >> "$log"
}

log_output() {
    local line
    while IFS= read -r line || [[ -n "$line" ]]; do
        printf '[%s] %s\n' "$(date -Is)" "$line" | tee -a "$log"
    done
}

run_logged() {
    local status
    set +e
    "$@" 2>&1 | log_output
    status=${PIPESTATUS[0]}
    set -e
    return "$status"
}

show_error() {
    log_message "ERROR: $*"
    printf 'Bottles: %s\nLog: %s\n' "$*" "$log" >&2
}

check_cpak() {
    [[ -x "$cpak_bin" ]] || { show_error "CPak executable not found at $cpak_bin"; return 1; }
    local detected
    detected="$("$cpak_bin" --version 2>&1)" || { show_error "Unable to read CPak version: $detected"; return 1; }
    detected="$(grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' <<< "$detected" | head -n 1 || :)"
    if [[ -z "$detected" || "$(printf '%s\n' "$min_version" "$detected" | sort -V | head -n 1)" != "$min_version" ]]; then
        show_error "CPak $min_version or newer is required (found ${detected:-unknown})."
        return 1
    fi
}

installed() {
    local inventory
    inventory="$("$cpak_bin" list --json 2>> "$log")" || return 2
    grep -Fq '"origin": "github.com/bottlesdevs/bottles"' <<< "$inventory" ||
        grep -Fq '"origin":"github.com/bottlesdevs/bottles"' <<< "$inventory"
}

install_bottles() {
    local result=0
    installed || result=$?
    if (( result == 0 )); then
        log_message 'Bottles is already present in the user CPak store.'
        return 0
    elif (( result != 1 )); then
        show_error 'CPak could not list installed applications.'
        return 1
    fi
    log_message 'Installing official Bottles for this user.'
    if ! run_logged "$cpak_bin" install --yes -b main "$origin"; then
        show_error 'CPak failed installing Bottles.'
        return 1
    fi
    if ! installed; then
        show_error 'CPak exited without registering Bottles.'
        return 1
    fi
}

choose_setup() {
    local choice="${1:-}"
    if [[ -z "$choice" ]]; then
        [[ -t 0 ]] || { show_error 'Select bottles, business or enterprise explicitly when no terminal is available.'; return 2; }
        printf '%s\n' 'Configure Bottles for this user:' '  1) Bottles only' '  2) Bottles + Microsoft 365 Business' '  3) Bottles + Microsoft 365 Enterprise' >&2
        printf 'Choose [1-3]: ' >&2
        IFS= read -r choice || return 1
    fi
    case "$choice" in
        1|bottles) printf 'bottles\n' ;;
        2|business) printf 'business\n' ;;
        3|enterprise) printf 'enterprise\n' ;;
        *) show_error 'Expected bottles, business or enterprise.'; return 2 ;;
    esac
}

setup() {
    local choice
    choice="$(choose_setup "${1:-}")" || return $?
    if [[ "$choice" != bottles ]] && ! command -v bottles-office-experimental >/dev/null 2>&1; then
        show_error 'Microsoft 365 support requires bottles-office-experimental. Install that package first.'
        return 1
    fi
    install_bottles || return 1
    if [[ "$choice" != bottles ]]; then
        log_message 'Updating the official Bottles CPak image before Office setup.'
        if ! run_logged "$cpak_bin" update "$origin"; then
            show_error 'Failed to update Bottles. Office setup was not started.'
            return 1
        fi
    fi
    if command -v bottles-soda-import >/dev/null 2>&1; then
        log_message 'Importing the installed experimental Soda runner.'
        if ! run_logged bottles-soda-import; then
            show_error 'Failed to import the experimental Soda runner.'
            return 1
        fi
    fi
    if [[ "$choice" != bottles ]]; then
        log_message "Starting optional Microsoft 365 $choice setup."
        if ! bottles-office-experimental install "$choice"; then
            show_error 'Optional Microsoft 365 setup failed. Bottles remains installed.'
            return 1
        fi
    fi
    printf '%s\n' 'Bottles is ready. Open the official Bottles entry in the application menu.'
}

command="${1:-setup}"
if (( $# > 0 )); then shift; fi
case "$command" in
    help|-h|--help) usage ;;
    *)
        if [[ "$(id -u)" == 0 ]]; then
            show_error 'Run as your normal desktop user, never with sudo.'
            exit 1
        fi
        check_cpak || exit 1
        case "$command" in
            setup)
                (( $# <= 1 )) || { usage >&2; exit 2; }
                setup "${1:-}"
                ;;
            install)
                (( $# == 0 )) || { usage >&2; exit 2; }
                install_bottles
                ;;
            run)
                install_bottles || exit 1
                log_message 'Launching Bottles.'
                if ! run_logged "$cpak_bin" run "$origin" bottles "$@"; then
                    show_error 'Bottles failed to launch.'
                    exit 1
                fi
                ;;
            update|remove|doctor)
                (( $# == 0 )) || { usage >&2; exit 2; }
                case "$command" in
                    update) run_logged "$cpak_bin" update "$origin" ;;
                    remove) run_logged "$cpak_bin" remove "$origin" ;;
                    doctor) run_logged "$cpak_bin" doctor ;;
                esac
                ;;
            *) usage >&2; exit 2 ;;
        esac
        ;;
esac
