#!/usr/bin/env bash
#
# gnf – Friendly wrapper around 'sudo dnf'
# Version 1.2
# Author: You
#
# Features:
#   • Implicit '-y' on install/remove/reinstall/downgrade (toggle with -n/--no-confirm)
#   • 'gnf update' pipeline:
#       - dnf update [-y] [--refresh]
#       - flatpak update (user + system)
#       - optional fwupd refresh+update
#     with its own flags: -r/--refresh, -f/--firmware, -n/--no-confirm, -y/--yes
#   • Passes any other 'dnf' subcommand straight to sudo dnf
#   • 'copr' sub-commands get auto '-y' for addrepo/removerepo/enable/disable/list

set -euo pipefail

PROGRAM=$(basename "$0")
VERSION="1.2"

usage() {
  cat <<EOF
$PROGRAM – wrapper for sudo dnf with smart defaults

Usage:
  $PROGRAM [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS] [ARGS...]

GLOBAL OPTIONS (must precede COMMAND):
  -h, --help          Show this help and exit
      --version       Show version and exit
  -n, --no-confirm    Disable the automatic '-y' on supported commands
  -y, --yes           (Re-)enable the automatic '-y' (default)

COMMANDS:
  install, remove, reinstall, downgrade
     → Implicit '-y' unless disabled by global '-n'

  update   (alias: upgrade)
     → dnf update + flatpak update + optional fwupd
     → COMMAND OPTIONS:
          -r, --refresh     Pass --refresh to dnf update
          -f, --firmware    After dnf+flatpak, run fwupdmgr refresh && update
          -n, --no-confirm  Do NOT add '-y' to dnf update
          -y, --yes         Force '-y' on dnf update (default)
     → You may group short flags: e.g. -rf or -fr

  copr     (subcommands: addrepo, removerepo, enable, disable, list, …)
     → For addrepo/removerepo/enable/disable/list, we auto '-y'
     → Other copr actions are passed straight through

  any other dnf COMMAND is forwarded to 'sudo dnf'

EXAMPLES:
  gnf install vim git
  gnf -n install firefox         # interactive remove/install
  gnf update                     # dnf update -y ; flatpak
  gnf update -r                  # + --refresh
  gnf update -rf                 # + firmware
  gnf -n update --refresh        # no -y, + refresh
  gnf copr addrepo user/project

EOF
}

# If no args, show help
if [[ $# -eq 0 ]]; then
  usage
  exit 0
fi

#
# 1) Parse GLOBAL OPTIONS
#
auto_yes=true
while [[ $# -gt 0 && "$1" == -* ]]; do
  case "$1" in
    -h|--help)       usage; exit 0 ;;
    --version)       echo "$PROGRAM $VERSION"; exit 0 ;;
    -n|--no-confirm) auto_yes=false; shift ;;
    -y|--yes)        auto_yes=true;  shift ;;
    --)              shift; break ;;  # end of globals
    *)               break ;;         # first non-global dash-opt
  esac
done

# Must have at least one positional argument now: the COMMAND
if [[ $# -lt 1 ]]; then
  echo "Error: no COMMAND specified." >&2
  usage
  exit 1
fi

cmd=$1; shift
# Treat 'upgrade' as alias for 'update'
[[ "$cmd" == "upgrade" ]] && cmd=update

#
# 2) Dispatch on COMMAND
#
case "$cmd" in

  # ---------------------------------------------------------------
  # install/remove/reinstall/downgrade  (auto '-y' unless -n)
  # ---------------------------------------------------------------
  install|remove|reinstall|downgrade)
    dnf_args=()
    $auto_yes && dnf_args+=(-y)
    sudo dnf "$cmd" "${dnf_args[@]}" "$@"
    exit $?
    ;;

  # ---------------------------------------------------------------
  # update pipeline
  # ---------------------------------------------------------------
  update)
    # 2.1) Map any long opts to short ones for getopts
    mapped=()
    for arg in "$@"; do
      case "$arg" in
        --refresh)    mapped+=(-r) ;;
        --firmware)   mapped+=(-f) ;;
        --no-confirm) mapped+=(-n) ;;
        --yes)        mapped+=(-y) ;;
        --)           mapped+=(--);;
        *)            mapped+=("$arg");;
      esac
    done

    # 2.2) Parse update-specific flags with getopts (supports grouping: -rf)
    refresh=false
    firmware=false
    confirm=$auto_yes
    OPTIND=1
    # note: the leading colon suppresses getopts’ own error msg
    while getopts ":rfny" opt "${mapped[@]}"; do
      case "$opt" in
        r) refresh=true ;;
        f) firmware=true ;;
        n) confirm=false ;;
        y) confirm=true ;;
        \?) echo "Unknown option '-$OPTARG' for update" >&2; exit 1 ;;
      esac
    done
    shift $((OPTIND - 1))

    # 2.3) Run the dnf update
    dnf_args=()
    $confirm && dnf_args+=(-y)
    $refresh && dnf_args+=(--refresh)
    sudo dnf update "${dnf_args[@]}" "$@"

    # 2.4) Run flatpak updates (user then system)
    flatpak update -y
    sudo flatpak update

    # 2.5) Optional firmware via fwupd
    if $firmware; then
      sudo fwupdmgr refresh
      sudo fwupdmgr update
    fi

    exit $?
    ;;

  # ---------------------------------------------------------------
  # copr sub-commands
  # ---------------------------------------------------------------
  copr)
    if [[ $# -lt 1 ]]; then
      echo "Error: 'gnf copr' requires a subcommand." >&2
      exit 1
    fi
    sub=$1; shift
    case "$sub" in
      addrepo|removerepo|enable|disable|list)
        sudo dnf copr "$sub" -y "$@" ;;
      *)
        sudo dnf copr "$sub" "$@" ;;
    esac
    exit $?
    ;;

  # ---------------------------------------------------------------
  # anything else → pass straight to sudo dnf
  # ---------------------------------------------------------------
  *)
    sudo dnf "$cmd" "$@"
    exit $?
    ;;
esac
