#!/usr/bin/env bash
# epc670-notifier — Check for epc package updates and notify the desktop user.
#
# Reads monitored packages from /etc/epc670/monitored-packages.conf.
# If updates are available or pending, shows a yad dialog that stays open
# until the user explicitly acts on it.
# Clicking "Update now" opens epc670-update in lxterminal and BLOCKS until
# the update finishes before returning to the caller.
#
# Runs as a systemd user service (epc670-notifier.timer) so DISPLAY,
# DBUS_SESSION_BUS_ADDRESS and XAUTHORITY are inherited from the desktop session.
#
# Options:
#   --prompt-verbose    Show a dialog even when all packages are up to date
#                       or when the device is not connected to the internet.

set -euo pipefail

EPC_VENV="/opt/venv/epc670"
PACKAGES_CONF="/etc/epc670/monitored-packages.conf"
ICON_UPDATE="/usr/share/pixmaps/update.png"
ICON_FALLBACK="software-update-available"
REBOOT_REQUIRED_FLAG="/tmp/epc-reboot-required"

# ---------------------------------------------------------------------------
# Parse arguments
# ---------------------------------------------------------------------------
PROMPT_VERBOSE=false
for ARG in "$@"; do
    case "$ARG" in
        --prompt-verbose) PROMPT_VERBOSE=true ;;
        *) echo "Warning: unknown argument '${ARG}'" >&2 ;;
    esac
done

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

log() { echo "[epc670-notifier] $*"; }

yad_info() {
    local TITLE="$1"
    local TEXT="$2"
    NO_AT_BRIDGE=1 yad \
        --title="$TITLE" \
        --image="dialog-information" \
        --text="$TEXT" \
        --button="OK:0" \
        --on-top \
        --center \
        --sticky \
        2>/dev/null || true
}

# ---------------------------------------------------------------------------
# Reboot required check — warn if a previous update was not followed by reboot
# ---------------------------------------------------------------------------
if [[ -f "$REBOOT_REQUIRED_FLAG" ]]; then
    log "Reboot required flag found — prompting user."
    REBOOT_CHOICE=$(NO_AT_BRIDGE=1 yad \
        --title="Reboot Required" \
        --image="system-restart" \
        --text="A previous update requires a reboot to take full effect.\n\nReboot now?" \
        --button="Reboot now:0" \
        --button="Later:1" \
        --button="Don't show again:2" \
        --on-top \
        --center \
        --sticky \
        2>/dev/null; echo $?)
    case "$REBOOT_CHOICE" in
        0)
            rm -f "$REBOOT_REQUIRED_FLAG"
            sudo reboot
            ;;
        2)
            log "User chose not to be reminded again — clearing flag."
            rm -f "$REBOOT_REQUIRED_FLAG"
            ;;
        *)
            log "User deferred reboot again."
            ;;
    esac
fi

# ---------------------------------------------------------------------------
# Parse monitored-packages.conf
# ---------------------------------------------------------------------------
APT_PACKAGES=()
APT_PACKAGES_OPTIONAL=()
PYTHON_PACKAGES=()
PYTHON_PACKAGES_OPTIONAL=()

if [[ ! -f "$PACKAGES_CONF" ]]; then
    log "ERROR: config not found at ${PACKAGES_CONF}"
    exit 1
fi

while IFS= read -r LINE; do
    LINE="${LINE%%#*}"
    LINE="${LINE//[[:space:]]/}"
    [[ -z "$LINE" ]] && continue

    TYPE="${LINE%%:*}"
    SPEC="${LINE#*:}"

    case "$TYPE" in
        apt)
            if [[ "$SPEC" == *'?' ]]; then
                APT_PACKAGES_OPTIONAL+=("${SPEC%?}")
            else
                APT_PACKAGES+=("$SPEC")
            fi
            ;;
        pip)
            if [[ "$SPEC" == *'?' ]]; then
                PYTHON_PACKAGES_OPTIONAL+=("${SPEC%?}")
            else
                PYTHON_PACKAGES+=("$SPEC")
            fi
            ;;
    esac
done < "$PACKAGES_CONF"

# ---------------------------------------------------------------------------
# Network check — bail silently if offline (timer will retry in 12 h)
# ---------------------------------------------------------------------------
NETWORK_OK=false
for HOST in "8.8.8.8" "1.1.1.1" "9.9.9.9"; do
    if ping -c 1 -W 3 "$HOST" &>/dev/null; then
        NETWORK_OK=true
        break
    fi
done

if [[ "$NETWORK_OK" == false ]]; then
    log "Offline — skipping update check."
    if [[ "$PROMPT_VERBOSE" == true ]]; then
        yad_info "No Internet Connection" \
            "The device is not connected to the internet.\nCould not check for updates."
    fi
    exit 0
fi

# ---------------------------------------------------------------------------
# Check APT packages
# ---------------------------------------------------------------------------
log "Checking apt packages..."
apt-get update -qq 2>/dev/null || true

UPDATES_AVAILABLE=()

check_apt_pkg() {
    local PKG="$1"
    local OPTIONAL="${2:-false}"

    local CANDIDATE INSTALLED
    CANDIDATE=$(apt-cache policy "$PKG" 2>/dev/null | awk '/Candidate:/{print $2}') || true
    INSTALLED=$(apt-cache policy "$PKG" 2>/dev/null | awk '/Installed:/{print $2}') || true

    if [[ -z "$CANDIDATE" || "$CANDIDATE" == "(none)" ]]; then
        : # not in repository yet — silent
    elif [[ "$INSTALLED" != "$CANDIDATE" ]]; then
        UPDATES_AVAILABLE+=("apt:${PKG} ${INSTALLED:-not installed} → ${CANDIDATE}")
    fi
}

for PKG in "${APT_PACKAGES[@]}";          do check_apt_pkg "$PKG" false || true; done
for PKG in "${APT_PACKAGES_OPTIONAL[@]}"; do check_apt_pkg "$PKG" true  || true; done

# ---------------------------------------------------------------------------
# Check pip packages
# ---------------------------------------------------------------------------
if [[ -x "${EPC_VENV}/bin/pip" ]]; then
    log "Checking pip packages..."

    check_pip_pkg() {
        local SPEC="$1"
        local OPTIONAL="${2:-false}"
        local PKG_NAME="${SPEC%%[><=]*}"

        local INSTALLED_VER
        INSTALLED_VER=$("${EPC_VENV}/bin/pip" show "$PKG_NAME" 2>/dev/null \
            | awk '/^Version:/{print $2}') || true

        local DRY_RUN_TMP DRY_RUN_OUT
        DRY_RUN_TMP=$(mktemp)
        "${EPC_VENV}/bin/pip" install --dry-run --upgrade "$SPEC" >"$DRY_RUN_TMP" 2>&1 || true
        DRY_RUN_OUT=$(cat "$DRY_RUN_TMP" 2>/dev/null) || true
        rm -f "$DRY_RUN_TMP"

        if echo "$DRY_RUN_OUT" | grep -qiE "Could not find|No matching|not find a version|No versions found" 2>/dev/null; then
            return 0
        fi

        local LATEST_VER
        LATEST_VER=$(set +o pipefail; echo "$DRY_RUN_OUT" \
            | grep -oP "(?<=Would install ${PKG_NAME}-)\S+" \
            | head -1) || true

        if [[ -n "$LATEST_VER" && "$INSTALLED_VER" != "$LATEST_VER" ]]; then
            UPDATES_AVAILABLE+=("pip:${PKG_NAME} ${INSTALLED_VER:-not installed} → ${LATEST_VER}")
        fi
    }

    for PKG in "${PYTHON_PACKAGES[@]}";          do check_pip_pkg "$PKG" false || true; done
    for PKG in "${PYTHON_PACKAGES_OPTIONAL[@]}"; do check_pip_pkg "$PKG" true  || true; done
fi

# ---------------------------------------------------------------------------
# Nothing to report
# ---------------------------------------------------------------------------
if [[ ${#UPDATES_AVAILABLE[@]} -eq 0 ]]; then
    log "All packages up to date."
    if [[ "$PROMPT_VERBOSE" == true ]]; then
        yad_info "epc Packages Up To Date" \
            "All packages are up to date!"
    fi
    exit 0
fi

# ---------------------------------------------------------------------------
# Build dialog text
# ---------------------------------------------------------------------------
ICON_PATH="$([[ -f "$ICON_UPDATE" ]] && echo "$ICON_UPDATE" || echo "$ICON_FALLBACK")"

TEXT=""
SUMMARY="${#UPDATES_AVAILABLE[@]} epc update(s) available"
TEXT+="${SUMMARY}\n"
TEXT+="$(printf '%s\n' "${UPDATES_AVAILABLE[@]}" | sed 's/^/• /')\n"

log "$SUMMARY"

# ---------------------------------------------------------------------------
# Show dialog — stays open until user explicitly closes it.
# If "Update now" is clicked, launch epc670-update in lxterminal and BLOCK
# until it finishes (via a done-flag file) before returning.
# ---------------------------------------------------------------------------
if NO_AT_BRIDGE=1 yad \
    --title="epc Update Available" \
    --image="$ICON_PATH" \
    --text="$TEXT" \
    --button="Update now:0" \
    --button="Dismiss:1" \
    --on-top \
    --center \
    --sticky \
    2>/dev/null; then

    log "User chose Update now — launching epc670-update"

    # Create a temp flag file path; remove it so we can wait for its creation
    DONE_FLAG=$(mktemp)
    rm -f "$DONE_FLAG"

    # Launch terminal; epc670-update writes the flag when it finishes.
    lxterminal --title="epc Update" \
        -e "bash -c 'epc670-update --noinput; touch ${DONE_FLAG}'"

    # Block here until epc670-update signals completion
    log "Waiting for update to complete..."
    while [[ ! -f "$DONE_FLAG" ]]; do
        sleep 1
    done
    rm -f "$DONE_FLAG"

    log "Update completed — continuing startup."
else
    log "User dismissed the dialog"
fi