#!/bin/sh -eu
# Emby universal repository and app helper (POSIX sh)
# Usage:
#   Interactive:
#     sh get.sh
#   One-liners (non-interactive):
#     Register repo (stable): curl -fsSL https://pkg.emby.media/get.sh | env ch=stable rop=R sh
#     Show packages (beta, install client): curl -fsSL https://pkg.emby.media/get.sh | env ch=beta rop=S app=media.emby.client.beta aop=I sh
#
# Design:
# - Single-process state machine (no re-exec, no header reprint)
# - Library resolved once (no re-fetch on back navigation)
# - Strict input: Enter to confirm; first allowed alnum; b=back; q=quit
# - Clear separation of public env (ch, rop, app, aop, GET_LOCAL) vs internal vars

# -------- Public interface (env) --------
_CH="${ch:-}"
_ROP_RAW="${rop:-}"
_APP="${app:-}"
_AOP_RAW="${aop:-}"
_GET_LOCAL="${GET_LOCAL:-}"

# Stop reading from environment again; keep internals isolated
unset ch rop app aop GET_LOCAL

# -------- Constants --------
VERSION="0.20.0"
BASE_URL="https://pkg.emby.media"

# -------- Helpers --------
available() { command -v "${1:?}" >/dev/null 2>&1; }
first_of() { for c in "$@"; do if available "$c"; then echo "$c"; return 0; fi; done; return 1; }
upper() { printf "%s" "${1:-}" | tr '[:lower:]' '[:upper:]'; }
error() { printf >&2 "Error: %s\n" "${1:?}"; shift || true; for s in "$@"; do printf >&2 "%s\n" "$s"; done; exit 1; }
is_tty() { [ -r /dev/tty ]; }
has_appstream_handler() { available xdg-mime && xdg-mime query default x-scheme-handler/appstream 2>/dev/null | grep -q .; }

# Check if a version string represents a stable (release) version
# Release versions: end with ".0" or are in the explicit stable version list
is_release_version() {
  ver="$1"
  [ -z "$ver" ] && return 1
  case "$ver" in
    # Versions ending with .0 are release versions
    *".0") return 0 ;;
    # Explicit list of stable versions (hotfix releases)
    "4.9.1.40"|"4.9.1.50"|"4.9.1.60"|"4.9.1.70"|"4.9.1.80"|"4.9.1.90") return 0 ;;
    *) return 1 ;;
  esac
}

# Compare semantic versions (handles any number of dot-separated numeric parts)
# Returns: 0 if v1==v2, 1 if v1>v2, 2 if v1<v2
compare_versions() {
  v1="$1"; v2="$2"
  [ "$v1" = "$v2" ] && return 0
  
  # Split versions by dots and compare part by part
  OLDIFS="$IFS"
  IFS='.'
  set -- $v1
  v1_1="${1:-0}"; v1_2="${2:-0}"; v1_3="${3:-0}"; v1_4="${4:-0}"; v1_5="${5:-0}"
  set -- $v2
  v2_1="${1:-0}"; v2_2="${2:-0}"; v2_3="${3:-0}"; v2_4="${4:-0}"; v2_5="${5:-0}"
  IFS="$OLDIFS"
  
  # Strip non-numeric suffixes from all parts
  v1_1=$(printf "%s" "$v1_1" | sed 's/[^0-9].*//' ); v1_1=${v1_1:-0}
  v1_2=$(printf "%s" "$v1_2" | sed 's/[^0-9].*//' ); v1_2=${v1_2:-0}
  v1_3=$(printf "%s" "$v1_3" | sed 's/[^0-9].*//' ); v1_3=${v1_3:-0}
  v1_4=$(printf "%s" "$v1_4" | sed 's/[^0-9].*//' ); v1_4=${v1_4:-0}
  v1_5=$(printf "%s" "$v1_5" | sed 's/[^0-9].*//' ); v1_5=${v1_5:-0}
  
  v2_1=$(printf "%s" "$v2_1" | sed 's/[^0-9].*//' ); v2_1=${v2_1:-0}
  v2_2=$(printf "%s" "$v2_2" | sed 's/[^0-9].*//' ); v2_2=${v2_2:-0}
  v2_3=$(printf "%s" "$v2_3" | sed 's/[^0-9].*//' ); v2_3=${v2_3:-0}
  v2_4=$(printf "%s" "$v2_4" | sed 's/[^0-9].*//' ); v2_4=${v2_4:-0}
  v2_5=$(printf "%s" "$v2_5" | sed 's/[^0-9].*//' ); v2_5=${v2_5:-0}
  
  # Compare each part
  [ "$v1_1" -gt "$v2_1" ] && return 1
  [ "$v1_1" -lt "$v2_1" ] && return 2
  [ "$v1_2" -gt "$v2_2" ] && return 1
  [ "$v1_2" -lt "$v2_2" ] && return 2
  [ "$v1_3" -gt "$v2_3" ] && return 1
  [ "$v1_3" -lt "$v2_3" ] && return 2
  [ "$v1_4" -gt "$v2_4" ] && return 1
  [ "$v1_4" -lt "$v2_4" ] && return 2
  [ "$v1_5" -gt "$v2_5" ] && return 1
  [ "$v1_5" -lt "$v2_5" ] && return 2
  
  return 0
}

usage_non_interactive() {
  cat >&2 <<'EOF'
Non-interactive usage requires explicit environment variables:

Examples:
  Register repo:
    curl -fsSL https://pkg.emby.media/get.sh | env ch=stable rop=R sh
  Unregister repo:
    curl -fsSL https://pkg.emby.media/get.sh | env ch=beta rop=U sh
  Show packages and install (beta):
    curl -fsSL https://pkg.emby.media/get.sh | env ch=beta rop=S app=media.emby.client.beta aop=I sh

Variables:
  ch   = stable|beta
  rop  = S|R|U
  app  = platform package id (e.g., emby-server, media.emby.client(.beta), emby-client)   (only required when rop=S)
  aop  = I|U|V|S                    (only required when rop=S)
EOF
}

# Prompt into named variable ($1). Returns non-zero on EOF or no TTY.
prompt() {
  var="${1:?}"; shift
  printf "%s" "$*" >&2
  if [ -t 0 ]; then
    IFS= read -r ans || return 1
  else
    if [ -r /dev/tty ]; then
      IFS= read -r ans </dev/tty || return 1
    else
      return 1
    fi
  fi
  eval "$var=\$ans"
}

# Robust single-char input with Enter confirm, backspace, and ESC/arrow suppression (from test.sh)
prompt_choice() {
  prompt=$1            # label, e.g. "Select option"
  allowed=$2           # bracket-class contents, e.g. 123 or ynq
  oldstty=$(stty -g </dev/tty) || return 1
  trap 'stty "$oldstty" </dev/tty; printf "\n" >/dev/tty; exit 130' INT TERM HUP

  # raw-ish: we handle echo and editing ourselves
  stty -echo -icanon min 1 time 0 </dev/tty

  printf "$prompt" >/dev/tty

  allowed_up=$(printf "%s" "$allowed" | tr '[:lower:]' '[:upper:]')
  sel=
  cr=$(printf '\r')
  bs=$(printf '\b')
  del=$(printf '\177')
  esc=$(printf '\033')
  bel=$(printf '\007')
  ctrlc=$(printf '\003')
  ctrld=$(printf '\004')

  rc=0
  while :; do
    ch=$(dd bs=1 count=1 2>/dev/null < /dev/tty)

    # Treat empty result as LF (Enter) due to command-substitution trimming
    if [ -z "$ch" ] || [ "$ch" = "$cr" ]; then
      if [ -n "$sel" ]; then
        printf "\n" >/dev/tty
        break
      else
        printf "%s" "$bel" >/dev/tty
        continue
      fi
    fi

    if [ "$ch" = "$bs" ] || [ "$ch" = "$del" ]; then
      if [ -n "$sel" ]; then
        sel=
        printf '\b \b' >/dev/tty
      else
        printf "%s" "$bel" >/dev/tty
      fi
      continue
    fi

    if [ "$ch" = "$ctrlc" ]; then
      printf "\n" >/dev/tty
      rc=130
      sel=
      break
    fi

    if [ "$ch" = "$ctrld" ]; then
      if [ -n "$sel" ]; then
        printf "\n" >/dev/tty
        break
      else
        printf "\n" >/dev/tty
        rc=1
        sel=
        break
      fi
    fi

    if [ "$ch" = "$esc" ]; then
      # swallow the rest of the escape sequence (arrows, etc.)
      stty -icanon min 0 time 1 </dev/tty
      while :; do
        junk=$(dd bs=1 count=1 2>/dev/null < /dev/tty) || break
        [ -z "$junk" ] && break
      done
      stty -icanon min 1 time 0 </dev/tty
      continue
    fi

    if [ -z "$sel" ]; then
      ch_up=$(printf "%s" "$ch" | tr '[:lower:]' '[:upper:]')
      case "$ch_up" in
        [$allowed_up]) sel=$ch_up; printf "%s" "$ch_up" >/dev/tty ;;
        *)              printf "%s" "$bel" >/dev/tty ;;
      esac
    else
      printf "%s" "$bel" >/dev/tty
    fi
  done

  stty "$oldstty" </dev/tty
  trap - INT TERM HUP

  # print the selected char to stdout for capture
  printf "%s" "$sel"
  return "$rc"
}

# Wrapper to keep existing call-sites; strips any [...] from msg to avoid duplicate bracket printing
read_choice() {
  var="${1:?}"; shift
  msg="$1"; shift
  allowed="${1:?}"; shift || true
  # short_msg="$(printf "%s" "$msg" | sed 's/\[[^]]*]//g')"
  short_msg="$msg"
  choice="$(prompt_choice "$short_msg" "$allowed")" || return 1
  eval "$var=\$choice"
  return 0
}

# -------- Elevation and fetch tools (internal) --------
if [ "$(id -u)" -eq 0 ]; then
  _SUDO=""
  _SUDO_BIN=""
else
  _SUDO="$(first_of sudo doas run0 pkexec sudo-rs || true)"
  if [ -z "$_SUDO" ]; then
    _SUDO_BIN=""
  else
    # shellcheck disable=SC2086
    _SUDO_BIN=$(printf "%s\n" $_SUDO | awk '{print $1}')
  fi
fi

if [ "$(first_of curl wget || true)" = "wget" ]; then
  _CURL_BIN="wget"
  _CURL_OPTS="-qO-"
else
  _CURL_BIN="curl"
  _CURL_OPTS="-fsSL"
fi

# -------- Platform and library resolution --------
detect_platform() {
  if available apt-get; then
    _PLATFORM="apt"
  elif available pacman; then
    _PLATFORM="pac"
  elif available rpm-ostree && rpm-ostree status >/dev/null 2>&1; then
    # rpm-ostree detected - not yet supported
    cat >&2 <<'EOF'

This system uses rpm-ostree which is not yet supported.

To manually install Emby:
  1. Download and install the repo file:
     sudo curl -fsSL https://pkg.emby.media/rpm/emby.repo -o /etc/yum.repos.d/emby.repo
     (or use emby-beta.repo for beta channel)
  
  2. Install Emby with rpm-ostree:
     rpm-ostree install emby-server
     or
     rpm-ostree install media.emby.client.beta
  
  3. Reboot to apply the changes:
     systemctl reboot

For more information, visit: https://pkg.emby.media

EOF
    exit 1
  elif available dnf; then
    _PLATFORM="dnf"
  elif available zypper; then
    _PLATFORM="zypper"
  else
    error "Could not find a supported package manager (apt/pacman/dnf/zypper)."
  fi
}

resolve_platform_lib() {
  case "$_PLATFORM" in
    apt)    _SCRIPT_URL="$BASE_URL/apt/setup.sh" ;;
    pac)    _SCRIPT_URL="$BASE_URL/pac/setup.sh" ;;
    dnf)    _SCRIPT_URL="$BASE_URL/rpm/setup-dnf.sh" ;;
    zypper) _SCRIPT_URL="$BASE_URL/rpm/setup-zypper.sh" ;;
    *) error "Unknown platform '$_PLATFORM'." ;;
  esac

  if [ -n "$_GET_LOCAL" ]; then
    # Derive local tree_update root
    _LOCAL_ROOT=""
    # If script is on filesystem, prefer its directory
    if [ -n "${0:-}" ] && [ -f "$0" ]; then
      # shellcheck disable=SC2164
      _SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" 2>/dev/null && pwd -P)
      if [ -d "$_SCRIPT_DIR" ]; then
        _LOCAL_ROOT="$_SCRIPT_DIR"
      fi
    fi
    # Fallback to ./tree_update when running from project root
    if [ -z "$_LOCAL_ROOT" ] && [ -d "./tree_update" ]; then
      _LOCAL_ROOT="./tree_update"
    fi
    # Require tree_update
    if [ ! -d "$_LOCAL_ROOT" ]; then
      error "GET_LOCAL=1 requires this script to be run from the Emby repo with a local 'tree_update' folder."
    fi
    case "$_PLATFORM" in
      apt)    _LIB_PATH="$_LOCAL_ROOT/apt/setup.sh" ;;
      pac)    _LIB_PATH="$_LOCAL_ROOT/pac/setup.sh" ;;
      dnf)    _LIB_PATH="$_LOCAL_ROOT/rpm/setup-dnf.sh" ;;
      zypper) _LIB_PATH="$_LOCAL_ROOT/rpm/setup-zypper.sh" ;;
    esac
    [ -r "$_LIB_PATH" ] || error "Local platform script not readable: $_LIB_PATH"
    _LIB_MODE="local"
  else
    _LIB_MODE="remote"
    _LIB_PATH=""
  fi
}

# -------- Library invocation --------
_TMP_LIB=""
_TMP_LIB_CLEAN=0

cleanup() {
  if [ "$_TMP_LIB_CLEAN" -eq 1 ] && [ -n "$_TMP_LIB" ]; then rm -f "$_TMP_LIB" || true; fi
}
trap 'cleanup; exit 130' INT
trap cleanup 0 TERM HUP

ensure_lib_ready() {
  if [ -n "$_TMP_LIB" ] && [ -f "$_TMP_LIB" ]; then
    return 0
  fi
  if [ "$_LIB_MODE" = "local" ]; then
    _TMP_LIB="$_LIB_PATH"
    _TMP_LIB_CLEAN=0
    return 0
  fi
  # remote
  _TMP_LIB="$(mktemp)"
  _TMP_LIB_CLEAN=1
  echo "Fetching platform helper: $_CURL_BIN $_CURL_OPTS $_SCRIPT_URL > $_TMP_LIB" >&2
  # shellcheck disable=SC2086
  if ! $_CURL_BIN $_CURL_OPTS "$_SCRIPT_URL" >"$_TMP_LIB"; then
    rm -f "$_TMP_LIB" || true
    _TMP_LIB=""
    _TMP_LIB_CLEAN=0
    error "Failed to fetch $_SCRIPT_URL"
  fi
}

lib_call_user() {
  func="${1:?}"; shift
  ensure_lib_ready
  # shellcheck disable=SC2016
  EMBY_LIB_ONLY=1 bash -c '. "$0"; "$@"' "$_TMP_LIB" "$func" "$@"
}

lib_call_root() {
  func="${1:?}"; shift
  ensure_lib_ready
  if [ -z "$_SUDO" ] && [ "$(id -u)" -ne 0 ]; then
    error "No elevation tool available to perform '$func'."
  fi
  # shellcheck disable=SC2016
  ${_SUDO:-} env EMBY_LIB_ONLY=1 bash -c '. "$0"; "$@"' "$_TMP_LIB" "$func" "$@"
}

# -------- Screens (state functions) --------

# Globals shared across screens:
# CHANNEL, REGISTERED, GUI
# CATALOG lines, arrays APP_ARR/NAME_ARR/HASAS_ARR, PKG_NAME, APP_NAME, APP_HAS_AS, APP_INSTALLED

screen_channel_select() {
  # Validate env for non-interactive (allow prompts via /dev/tty when piped)
  if [ -z "$_CH" ] && [ ! -r /dev/tty ]; then
    usage_non_interactive
    exit 64
  fi

  # Auto from env
  case "${_CH:-}" in stable|beta) CHANNEL="$_CH";; esac
  if [ -n "${CHANNEL:-}" ]; then STATE="REPO"; return 0; fi

  # Interactive prompt
  ST_STATUS="Not registered"; if lib_call_user emby_repo_is_registered "stable"; then ST_STATUS="Registered"; fi
  BT_STATUS="Not registered"; if lib_call_user emby_repo_is_registered "beta"; then BT_STATUS="Registered"; fi
  printf "Which repository do you want to work on?\n\n"
  echo "    1) STABLE                    $ST_STATUS"
  echo "    2) BETA                      $BT_STATUS"
  echo ""
  if ! read_choice ans "Select [1-2] (Q: quit): " "12Qq"; then
    echo "Invalid selection."
    return 0
  fi
  case "$ans" in
    Q) echo "Aborted."; exit 0 ;;
    1) CHANNEL="stable" ;;
    2) CHANNEL="beta" ;;
  esac
  STATE="REPO"
}

screen_repo_ops() {
  # Determine registration
  REGISTERED=0; if lib_call_user emby_repo_is_registered "$CHANNEL"; then REGISTERED=1; fi

  printf "\nChoose repository operation:\n\n"
  if [ "$REGISTERED" -eq 1 ]; then
    echo "    S) Show Emby Packages"
    echo "    R) Reload repository data"
    echo "    U) Unregister Emby $(upper "$CHANNEL") repository"
    allowed="SRU"
  else
    echo "    R) Register   Emby $(upper "$CHANNEL") repository"
    allowed="R"
  fi
  echo ""
  # Non-interactive rop shortcut
  case "$(upper "${_ROP_RAW:-}")" in
    S) [ "$REGISTERED" -eq 1 ] && sel="S" || sel="";;
    R) sel="R";;  # R works for both register and reload
    U) [ "$REGISTERED" -eq 1 ] && sel="U" || sel="";;
    *) sel="";;
  esac

  if [ -z "$sel" ]; then
    if ! read_choice sel "Select [$(printf "%s" "$allowed" | sed 's/./&\//g' | sed 's#/$##')] (B: back, Q: quit): " "${allowed}QB"; then
      echo "Invalid selection."
      return 0
    fi
  fi

  case "$sel" in
    Q) echo "Aborted."; exit 0 ;;
    B) CHANNEL=""; STATE="CHANNEL";;
    S) STATE="LIST";;
    R)
      if lib_call_root emby_repo_install "$CHANNEL" "${_SUDO_BIN:-sudo}"; then
        if [ "$REGISTERED" -eq 1 ]; then
          echo "Repository data reloaded."
        fi
        # Auto-block cross-channel server updates to prevent accidental upgrades
        if [ "${_PLATFORM:-}" != "pac" ]; then
          server_ver="$(lib_call_user emby_app_get_installed_version "emby-server")"
          if [ -n "$server_ver" ]; then
            if [ "$CHANNEL" = "beta" ] && is_release_version "$server_ver"; then
              # Registering beta repo with stable server installed - block beta server
              if ! lib_call_user emby_pkg_is_blocked "beta" "emby-server"; then
                lib_call_root emby_pkg_block_updates "beta" "emby-server" "${_SUDO_BIN:-sudo}"
                echo ""
                echo "An Emby Server (stable) installation was detected on this system."
                echo "The Emby Server package from the beta repo has been blocked automatically"
                echo "in order to prevent automatic updates being offered, which could"
                echo "lead to unwanted installation of a beta server version over your"
                echo "stable installation."
                echo ""
              fi
            elif [ "$CHANNEL" = "stable" ] && ! is_release_version "$server_ver"; then
              # Registering stable repo with beta server installed - block stable server
              if ! lib_call_user emby_pkg_is_blocked "stable" "emby-server"; then
                lib_call_root emby_pkg_block_updates "stable" "emby-server" "${_SUDO_BIN:-sudo}"
                echo ""
                echo "An Emby Server (beta) installation was detected on this system."
                echo "The Emby Server package from the stable repo has been blocked automatically"
                echo "in order to prevent automatic updates being offered, which could"
                echo "lead to unwanted installation of a stable server version over your"
                echo "beta installation."
                echo ""
              fi
            fi
          fi
        fi
        STATE="LIST"
      else
        if [ "$REGISTERED" -eq 1 ]; then
          error "Repository reload failed for channel '$CHANNEL'."
        else
          error "Repository register failed for channel '$CHANNEL'."
        fi
      fi
      ;;
    U)
      if lib_call_root emby_repo_drop "$CHANNEL" "${_SUDO_BIN:-sudo}"; then
        echo "Unregistered."
        exit 0
      else
        error "Repository unregister failed for channel '$CHANNEL'."
      fi
      ;;
  esac
}

screen_show_list() {
  echo ""
  printf "Emby Packages:\n\n"

  CATALOG="$(lib_call_user emby_catalog_list "$CHANNEL" || true)"
  [ -n "$CATALOG" ] || error "No packages discovered in Emby repo for this channel."

  i=1
  APP_ARR=""
  NAME_ARR=""
  HASAS_ARR=""
  OLDIFS="$IFS"
  IFS='
'
  for line in $CATALOG; do
    IFS='|'; set -- $line; IFS="$OLDIFS"
    pkg="$1"; name="$2"; has="$3"
    [ -n "$pkg" ] || continue
    # Installed status computed on pkg op screen; list shows simple status for now
    status="Not installed"; if lib_call_user emby_app_is_installed "$pkg"; then status="Installed"; fi
    if [ "${_PLATFORM:-}" != "pac" ]; then
      if lib_call_user emby_pkg_is_blocked "$CHANNEL" "$pkg"; then
        status="$status (updates blocked)"
      fi
    fi
    printf "    %d) %-30s %s\n" "$i" "$name" "$status"
    if [ -z "$APP_ARR" ]; then
      APP_ARR="$pkg"; NAME_ARR="$name"; HASAS_ARR="$has"
    else
      APP_ARR="$APP_ARR;$pkg"
      NAME_ARR="$NAME_ARR;$name"
      HASAS_ARR="$HASAS_ARR;$has"
    fi
    i=$((i+1))
  done
  echo ""
  IFS="$OLDIFS"

  # Non-interactive requires app (allow prompts via /dev/tty when piped)
  if [ -z "${_APP:-}" ] && [ ! -r /dev/tty ]; then
    usage_non_interactive
    exit 64
  fi

  # Try env app map to pkg
  PKG_NAME=""
  if [ -n "${_APP:-}" ]; then
    IFS=';'; set -- $APP_ARR; IFS="$OLDIFS"
    for entry in "$@"; do [ "$entry" = "$_APP" ] && PKG_NAME="$entry" && break; done
    [ -n "$PKG_NAME" ] || error "Invalid env app='$_APP' for current channel."
  fi

  if [ -z "$PKG_NAME" ]; then
    # Build allowed digits up to 9
    allowed_digits=""; max=$((i-1)); [ "$max" -gt 9 ] && max=9
    d=1; while [ "$d" -le "$max" ]; do allowed_digits="${allowed_digits}${d}"; d=$((d+1)); done
    if ! read_choice ans "Choose App [1-$((i-1))] (B: back, Q: quit): " "${allowed_digits}BQ"; then
      echo "Invalid selection."
      return 0
    fi
    case "$ans" in
      Q) echo "Aborted."; exit 0 ;;
      B) STATE="REPO"; return 0 ;;
      *)
        idx="$ans"; n=1; sel=""
        IFS=';'; set -- $APP_ARR; IFS="$OLDIFS"
        for entry in "$@"; do
          [ -z "$entry" ] && continue
          if [ "$n" -eq "$idx" ]; then sel="$entry"; break; fi
          n=$((n+1))
        done
        [ -n "$sel" ] || { echo "Invalid index."; return 0; }
        PKG_NAME="$sel"
        ;;
    esac
  fi

  # Lookup selected metadata
  # find name and has flag at same index
  IFS=';'; set -- $APP_ARR; IFS="$OLDIFS"; n=1; found_idx=0
  for entry in "$@"; do
    [ -z "$entry" ] && continue
    if [ "$entry" = "$PKG_NAME" ]; then found_idx=$n; break; fi
    n=$((n+1))
  done
  # name
  IFS=';'; set -- $NAME_ARR; IFS="$OLDIFS"; c=1; APP_NAME=""
  for nm in "$@"; do [ $c -eq $found_idx ] && APP_NAME="$nm" && break; c=$((c+1)); done
  # has appstream
  IFS=';'; set -- $HASAS_ARR; IFS="$OLDIFS"; c=1; hs=""
  for h in "$@"; do [ $c -eq $found_idx ] && hs="$h" && break; c=$((c+1)); done
  APP_HAS_AS="N"; [ "$hs" = "Y" ] && APP_HAS_AS="Y"

  # Installed?
  APP_INSTALLED=0; if lib_call_user emby_app_is_installed "$PKG_NAME"; then APP_INSTALLED=1; fi

  STATE="PKG"
}

screen_select_version() {
  # Parse AVAILABLE_VERS (comma-separated) into array and prompt user
  printf "\nAvailable versions:\n\n"
  i=1
  VER_ARR=""
  OLDIFS="$IFS"
  IFS=','
  for ver in $AVAILABLE_VERS; do
    ver=$(printf "%s" "$ver" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
    [ -n "$ver" ] || continue
    printf "    %d) %s\n" "$i" "$ver"
    if [ -z "$VER_ARR" ]; then
      VER_ARR="$ver"
    else
      VER_ARR="$VER_ARR;$ver"
    fi
    i=$((i+1))
  done
  IFS="$OLDIFS"
  echo ""
  
  # Build allowed digits
  allowed_digits=""; max=$((i-1)); [ "$max" -gt 9 ] && max=9
  d=1; while [ "$d" -le "$max" ]; do allowed_digits="${allowed_digits}${d}"; d=$((d+1)); done
  
  if ! read_choice ans "Select version [1-$((i-1))] (B: back, Q: quit): " "${allowed_digits}BQ"; then
    SELECTED_VER=""
    return 1
  fi
  
  case "$ans" in
    Q) echo "Aborted."; exit 0 ;;
    B) SELECTED_VER=""; return 1 ;;
    *)
      idx="$ans"; n=1; sel=""
      IFS=';'; set -- $VER_ARR; IFS="$OLDIFS"
      for entry in "$@"; do
        [ -z "$entry" ] && continue
        if [ "$n" -eq "$idx" ]; then sel="$entry"; break; fi
        n=$((n+1))
      done
      [ -n "$sel" ] || { echo "Invalid index."; SELECTED_VER=""; return 1; }
      SELECTED_VER="$sel"
      return 0
      ;;
  esac
}

screen_pkg_ops() {
  # GUI flag
  GUI=0; { [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; } && GUI=1

  # Non-interactive validate aop if provided (do this once at start)
  AOP=""; _AOP_UP="$(upper "${_AOP_RAW:-}")"
  if [ -n "$_AOP_UP" ]; then
    case "$_AOP_UP" in
      I) [ $APP_INSTALLED -eq 0 ] && AOP="I" ;;
      U) [ $APP_INSTALLED -eq 1 ] && AOP="U" ;;
      V) [ $APP_INSTALLED -eq 1 ] && [ $VER_COUNT -gt 1 ] && AOP="V" ;;
      P) [ $APP_INSTALLED -eq 0 ] && [ $VER_COUNT -gt 1 ] && AOP="P" ;;
      G) [ $APP_INSTALLED -eq 1 ] && [ $NEWER_AVAILABLE -eq 1 ] && AOP="G" ;;
      S) [ $APP_INSTALLED -eq 1 ] && AOP="S" ;;
      O)
        if [ $GUI -eq 0 ] || [ "$APP_HAS_AS" != "Y" ] || ! has_appstream_handler; then
          error "Invalid or disallowed aop='O' for current app state."
        fi
        AOP="O"
        ;;
    esac
    [ -n "$AOP" ] || error "Invalid or disallowed aop='$_AOP_UP' for current app state."
  fi

  while :; do
    # Re-assess state at the start of each loop iteration
    INSTALLED_VER="$(lib_call_user emby_app_get_installed_version "$PKG_NAME" || echo "")"
    AVAILABLE_VERS="$(lib_call_user emby_app_list_available_versions "$PKG_NAME" "$CHANNEL" || echo "")"
    
    # Parse version count and check if newer available
    VER_COUNT=0
    if [ -n "$AVAILABLE_VERS" ]; then
      VER_COUNT=$(printf "%s," "$AVAILABLE_VERS" | awk -F',' '{print NF-1}')
      [ "$VER_COUNT" -lt 1 ] && VER_COUNT=1
    fi
    
    NEWER_AVAILABLE=0
    if [ $APP_INSTALLED -eq 1 ] && [ -n "$INSTALLED_VER" ] && [ $VER_COUNT -gt 0 ]; then
      # Get first (newest) available version
      NEWEST_VER=$(printf "%s" "$AVAILABLE_VERS" | cut -d',' -f1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
      if [ -n "$NEWEST_VER" ]; then
        compare_versions "$NEWEST_VER" "$INSTALLED_VER"
        case $? in
          1) NEWER_AVAILABLE=1 ;;  # newest > installed
        esac
      fi
    fi

    BLOCKED=0
    if [ "${_PLATFORM:-}" != "pac" ]; then
      if lib_call_user emby_pkg_is_blocked "$CHANNEL" "$PKG_NAME"; then
        BLOCKED=1
      fi
    fi

    if [ -z "$AOP" ]; then
      printf "\n"
      printf "Package: %s (%s)\n" "$PKG_NAME" "$APP_NAME"
      if [ $APP_INSTALLED -eq 1 ] && [ -n "$INSTALLED_VER" ]; then
        printf "Installed version: %s\n" "$INSTALLED_VER"
      else
        printf "Installed version: Not installed\n"
      fi
      if [ -n "$AVAILABLE_VERS" ]; then
        printf "Available versions: %s\n" "$AVAILABLE_VERS"
      fi
      printf "\nChoose operation:\n\n"
      
      # Build menu dynamically based on state
      allowed_ops=""
      if [ $APP_INSTALLED -eq 0 ]; then
        # Not installed
        echo "    I) Install App (latest)"; allowed_ops="${allowed_ops}I"
        if [ $VER_COUNT -gt 1 ]; then
          echo "    V) Install specific Version"; allowed_ops="${allowed_ops}V"
        fi
        if [ $GUI -eq 1 ] && [ "$APP_HAS_AS" = "Y" ] && has_appstream_handler; then
          echo "    O) Open in Software Center"; allowed_ops="${allowed_ops}O"
        fi
      else
        # Installed
        echo "    U) Uninstall App"; allowed_ops="${allowed_ops}U"
        if [ $NEWER_AVAILABLE -eq 1 ]; then
          echo "    L) Upgrade to Latest version"; allowed_ops="${allowed_ops}L"
        fi
        if [ $VER_COUNT -gt 1 ]; then
          echo "    V) Install different Version"; allowed_ops="${allowed_ops}V"
        fi
        echo "    S) Show App Info"; allowed_ops="${allowed_ops}S"
        if [ "${_PLATFORM:-}" != "pac" ]; then
          if [ "$BLOCKED" -eq 1 ]; then
            echo "    K) Unblock package from auto-updating"
          else
            echo "    K) Block package from auto-updating"
          fi
          allowed_ops="${allowed_ops}K"
        fi
        if [ $GUI -eq 1 ] && [ "$APP_HAS_AS" = "Y" ] && has_appstream_handler; then
          echo "    O) Open in Software Center"; allowed_ops="${allowed_ops}O"
        fi
      fi
      echo ""
      
      # Format prompt with operations in brackets, B/Q as separate hint
      ops_display=$(printf "%s" "$allowed_ops" | sed 's/./&\//g' | sed 's#/$##')
      if ! read_choice sel "Select [$ops_display] (B: back, Q: quit): " "${allowed_ops}BQ"; then
        echo "Invalid selection."
        continue
      fi
      case "$sel" in
        Q) echo "Aborted."; exit 0 ;;
        B) STATE="LIST"; return 0 ;;
        *) AOP="$sel" ;;
      esac
    fi

    case "$AOP" in
      I)
        # Install app (only when not installed)
        [ $APP_INSTALLED -eq 0 ] || error "App is already installed."
        if lib_call_root emby_app_install "$PKG_NAME" "$CHANNEL" "${_SUDO_BIN:-sudo}"; then
          echo "Installed."
          APP_INSTALLED=1
          INSTALLED_VER="$(lib_call_user emby_app_get_installed_version "$PKG_NAME" || echo "")"
        else
          error "Install failed for package '$PKG_NAME'."
        fi
        ;;
      V)
        # Install specific version
        [ $VER_COUNT -gt 1 ] || error "Only one version available."
        
        if screen_select_version && [ -n "$SELECTED_VER" ]; then
          if lib_call_root emby_app_install_version "$PKG_NAME" "$SELECTED_VER" "$CHANNEL" "${_SUDO_BIN:-sudo}"; then
            echo "Installed version $SELECTED_VER."
            APP_INSTALLED=1
            INSTALLED_VER="$SELECTED_VER"
          else
            error "Install failed for package '$PKG_NAME' version '$SELECTED_VER'."
          fi
        fi
        ;;
      L)
        # Upgrade to latest
        [ $APP_INSTALLED -eq 1 ] || error "App is not installed."
        [ $NEWER_AVAILABLE -eq 1 ] || error "No newer version available."
        
        if lib_call_root emby_app_install "$PKG_NAME" "$CHANNEL" "${_SUDO_BIN:-sudo}"; then
          echo "Upgraded to latest version."
          INSTALLED_VER="$(lib_call_user emby_app_get_installed_version "$PKG_NAME" || echo "")"
          NEWER_AVAILABLE=0
        else
          error "Upgrade failed for package '$PKG_NAME'."
        fi
        ;;
      U)
        [ $APP_INSTALLED -eq 1 ] || error "App is not installed."
        if lib_call_root emby_app_uninstall "$PKG_NAME" "${_SUDO_BIN:-sudo}"; then
          echo "Uninstalled."
          APP_INSTALLED=0
          INSTALLED_VER=""
        else
          error "Uninstall failed for package '$PKG_NAME'."
        fi
        ;;
      S)
        # Show app info
        echo ""
        lib_call_user emby_app_view_info "$PKG_NAME" || true
        ;;
      O)
        # Open in software center
        [ $GUI -eq 1 ] || error "No GUI session available (DISPLAY/WAYLAND_DISPLAY not set)."
        [ "$APP_HAS_AS" = "Y" ] || error "Software Center open is not available for this app."
        xdg-open "appstream://$PKG_NAME" >/dev/null 2>&1 || error "Failed to open Software Center for '$PKG_NAME'."
        ;;
      K)
        # Block/unblock package updates for this repo (non-pac platforms)
        if [ "${_PLATFORM:-}" = "pac" ]; then
          error "Blocking updates is not supported on this platform."
        fi
        if [ "$BLOCKED" -eq 1 ]; then
          if lib_call_root emby_pkg_unblock_updates "$CHANNEL" "$PKG_NAME" "${_SUDO_BIN:-sudo}"; then
            echo "Updates unblocked for $APP_NAME."
            BLOCKED=0
          else
            error "Failed to unblock updates for '$PKG_NAME'."
          fi
        else
          if lib_call_root emby_pkg_block_updates "$CHANNEL" "$PKG_NAME" "${_SUDO_BIN:-sudo}"; then
            echo "Updates blocked for $APP_NAME."
            BLOCKED=1
          else
            error "Failed to block updates for '$PKG_NAME'."
          fi
        fi
        ;;
      *) error "Invalid app operation '$AOP'." ;;
    esac

    # In non-interactive mode, perform single action then exit
    if [ ! -r /dev/tty ]; then
      exit 0
    fi

    # After operations, clear AOP to show menu again
    AOP=""
  done
}

# -------- Start --------
echo "Emby Package Repository Installer v${VERSION}"
printf "%s\n\n" "https://pkg.emby.media - © 2025 softworkz for Emby LLC."

# Detect platform and resolve library once
detect_platform
resolve_platform_lib
# Warm the library once; subsequent calls will reuse _TMP_LIB
ensure_lib_ready

# GUI flag (used in pkg ops)
GUI=0; { [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; } && GUI=1

# -------- Dispatcher (state machine) --------
STATE="CHANNEL"
CHANNEL=""
REGISTERED=0
CATALOG=""

while :; do
  case "$STATE" in
    CHANNEL) screen_channel_select ;;
    REPO)    screen_repo_ops ;;
    LIST)    screen_show_list ;;
    PKG)     screen_pkg_ops ;;
    *)       error "Invalid state '$STATE'." ;;
  esac
done
