#!/bin/bash

# This script acts as a basic sudo wrapper using run0.
# It handles the common case of switching to root to run a command.

__config_file="/etc/run0-wrappers/sudo.conf"
__run0_exec="run0"
RUN0_BACKGROUND="--background="
secure_path="/usr/sbin:/usr/bin:/sbin:/bin"

if [ -f "${__config_file}" ]; then
    # shellcheck source=/dev/null
    . "${__config_file}"
fi

# --- Function to display usage information ---

show_help() {
    cat << 'EOF'
run0-sudo - execute a command as another user via run0

usage: run0-sudo -h | -K | -k | -V
usage: run0-sudo -v [-g group] [-u user]
usage: run0-sudo [-EHkNnP] [-D directory] [-g group]
            [-u user] [VAR=value] [-i | -s] [command [arg ...]]

Options:
  -D, --chdir=<directory>   Change the working directory
  -E, --preserve-env        Preserve user environment
      --preserve-env=<list> Preserve specific environment variables
  -g, --group=<group>       Run command as specified group or GID
  -H, --set-home            Set HOME variable, default with run0
  -h, --help                Display help text and exit
  -i, --login               Run login shell as the target user
  -K, --remove-timestamp    Invalidate polkit keep
  -k, --reset-timestamp     Invalidate polkit keep
  -s, --shell               Run the specified shell
  -u, --user=user           Run command as specified user or UID
  -V, --version             Display version and exit
  -v, --validate            Update timestamp without running a command
EOF
}

show_version() {
    run0_version="$(run0 --version 2>/dev/null | head -n 1 | cut -d' ' -f3 | sed 's/[()]//g')"
    echo "run0-sudo (run0-wrappers) 0.4.0"
    echo "run0 (systemd) $run0_version"
}

# --- Get shell for the target user or current user ---
get_shell() {
    local targetuser=$1

    if [ -n "$SHELL" ]; then
        echo "$SHELL"
    else
	# otherwise, get the shell of the target user
	local shell
	shell=$(getent passwd "$targetuser" 2>/dev/null | cut -d: -f7)
	echo "$shell"
    fi
}

# --- option handling ---

# Array to hold the arguments for run0
__run0_args=("$RUN0_BACKGROUND")
# Array to hold the final command for run0
__run0_command=()
__run0_env=()
# Shell to use if one is specified
__run0_shell=""
__run0_login_shell=""
__run0_validate_only=""
__run0_preserve_env=""
# Variables to track user and command
__run0_target_user=""
__run0_target_group=""
__run0_target_cwd=""

command_args() {
    if [ -n "$__run0_shell" ]; then
	__run0_command+=("$__run0_shell")
	if [ -n "$__run0_login_shell" ]; then
	    __run0_command+=("$__run0_login_shell")
	fi
	if [ $# -gt 0 ]; then
    	    __run0_command+=("-c" "$*")
	fi
    else
	__run0_command=("$@")
    fi
}

parse_arguments() {
    # Define short and long options
    # + prefix means stop at first non-option (critical for sudo behavior)
    local short_opts="+AbBC:D:Eu:g:isHnkKPlvhVN"
    local long_opts="askpass,background,bell,close-from:,chdir:,user:,group:,login,shell,set-home,preserve-env::,non-interactive,reset-timestamp,remove-timestamp,preserve-groups,list,validate,help,version,no-update"

    # Parse arguments using getopt
    local parsed_args
    if ! parsed_args=$(getopt -o "$short_opts" -l "$long_opts" -n "run0-sudo" -- "$@"); then
	show_help >&2
	exit 1
    fi

    # Set parsed arguments
    eval set -- "$parsed_args"

    # Process parsed options
    while true; do
        case "$1" in
            -A|--askpass)
		echo "-A/--askpass is not supported" >&2
		shift
		;;
	    -b|--background)
		echo "-b/--background is not supported" >&2
		shift
		;;
	    -B|--bell)
		echo "-B/--bell is not supported" >&2
		shift
		;;
	    -C|--close-from)
		echo "-C/--close-from <num> is not supported" >&2
		shift 2
		;;
            -D|--chdir)
                __run0_target_cwd="$2"
                shift 2
                ;;
            -E|--preserve-env)
                if [[ "$1" == "--preserve-env" && $# -gt 1 && "$2" != "--" ]]; then
                    # --preserve-env with a value (could be empty)
                    if [[ -z "$2" ]]; then
                        # Empty value - this is an error
                        echo "run0-sudo: option '--preserve-env' requires a variable list" >&2
                        show_help >&2
                        exit 1
                    fi
		    __run0_preserve_env="$2"
                    shift 2
                else
                    # -E format (preserve all) or --preserve-env without argument
                    __run0_preserve_env="all"
                    shift 1
                fi
                ;;
            -g|--group)
		__run0_target_group="$2"
                shift 2
                ;;
            -H|--set-home)
		# HOME is set by run0, no need to do anything
                shift
                ;;
            -h|--help)
                show_help
                exit 0
                ;;
            -i|--login)
		__run0_shell=$(get_shell "$__run0_target_user")
		__run0_login_shell="-l"
		# We need to change CWD to ~
		# run0 will do that if a target user is specified
		if [ -z "$__run0_target_user" ]; then
		    __run0_target_user="root"
		fi
		shift
		;;
            -k|--reset-timestamp|-K|--remove-timestamp)
		run0 --no-ask-password false >/dev/null 2>&1
		exit 0
		;;
            -l|--list)
		echo "-l/--list is not supported" >&2
		shift
		;;
	    -N|--no-update)
		echo "-N/--no-update is not supported" >&2
                shift
                ;;
            -n|--non-interactive)
		__run0_args+=("--no-ask-password")
		shift
		;;
            -P|--preserve-groups)
    		 echo "-P|--preserve-groups is not supported" >&2
		 shift
		 ;;
            -s|--shell)
		__run0_shell=$(get_shell "$__run0_target_user")
                shift
                ;;
            -u|--user)
                __run0_target_user="$2"
                shift 2
                ;;
            -v|--validate)
		__run0_validate_only=1
		shift
		;;
            -V|--version)
                show_version
                exit 0
                ;;
            --)
                shift
                break
                ;;
            *)
		echo "Invalid option: $1" >&2
		;;
        esac
    done

    # Show usage if no command is provided (default run0 behavior is to run a shell)
    if [ $# -eq 0 ] && \
       [ -z "$__run0_login_shell" ] && \
       [ -z "$__run0_shell" ]; then
        show_help >&2
        exit 1
    fi

    # Build run0 arguments and the command to execute
    command_args "$@"
}


# --- Main Logic ---

parse_arguments "$@"

if [ -n "$__run0_target_user" ]; then
    __run0_args+=("--user=${__run0_target_user}")
fi
if [ -n "$__run0_target_group" ]; then
    __run0_args+=("--group=${__run0_target_group}")
fi
if [ -n "$__run0_target_cwd" ]; then
    __run0_args+=("--chdir=${__run0_target_cwd}")
fi

if [ "$__run0_preserve_env" == "all" ]; then
    mapfile -t __run0_env < <(printenv | grep -v ^LOGNAME= | grep -v ^USER= | grep -v ^_ | grep -v ^PATH= | grep -v ^HOME= | grep -v ^SUDO_ | awk '{print "--setenv="$0}')
elif [ -n "$__run0_preserve_env" ]; then
    __vars=$(echo "$__run0_preserve_env" | tr ',' ' ')
    for var in $__vars; do
        # Remove whitespace
	var="${var//[[:blank:]]/}"
	__run0_env+=("--setenv=$var")
    done
fi

export PATH="${secure_path}"
unset secure_path

if [ "$__run0_validate_only" == "1" ]; then
    exec "${__run0_exec}" "${__run0_args[@]}" "${__run0_env[@]}" -- true
else
    exec "${__run0_exec}" "${__run0_args[@]}" "${__run0_env[@]}" -- "${__run0_command[@]}"
fi
