#!/bin/bash
# Show which SBo packages require a given package, marking installed ones.

SBO_REPO="${SBO_REPO:-/var/lib/sbopkg/SBo-git}"
PKG_LOG="${PKG_LOG:-/var/log/packages}"

# Colors (disabled when stdout is not a terminal)
if [[ -t 1 ]]; then
    C_RESET='\033[0m'
    C_INSTALLED='\033[1;32m'    # bold green  — installed
    C_MISSING='\033[0;33m'      # yellow      — not installed
    C_TARGET='\033[1;36m'       # bold cyan   — the queried package
    C_CATEGORY='\033[2;37m'     # dim white   — category label
    C_HEADER='\033[1;37m'       # bold white  — section headers
    C_ERROR='\033[1;31m'        # bold red    — errors
    C_SEP='\033[0;90m'          # dark grey   — separators
else
    C_RESET='' C_INSTALLED='' C_MISSING=''
    C_TARGET='' C_CATEGORY='' C_HEADER='' C_ERROR='' C_SEP=''
fi

usage() {
    printf "Usage: %s [OPTIONS] <package>\n" "$(basename "$0")"
    printf "\nShow SBo packages that list <package> in their REQUIRES field.\n"
    printf "\nOptions:\n"
    printf "  -i   show only installed dependents\n"
    printf "  -u   show only uninstalled dependents\n"
    printf "  -r   recursive: also find what requires those packages (one level)\n"
    printf "  -h   this help\n"
    printf "\nSymbols and colors:\n"
    printf "  ${C_INSTALLED}[I] installed${C_RESET}    package is present in %s\n" "$PKG_LOG"
    printf "  ${C_MISSING}[ ] missing${C_RESET}     package is not installed\n"
    printf "  ${C_TARGET}<package>${C_RESET}     queried package name (in header)\n"
    printf "  ${C_CATEGORY}(category)${C_RESET}    SBo category of the dependent\n"
    exit 0
}

die() { printf "${C_ERROR}error:${C_RESET} %s\n" "$*" >&2; exit 1; }

OPT_INSTALLED=0
OPT_UNINSTALLED=0
OPT_RECURSIVE=0

while getopts "iurh" opt; do
    case "$opt" in
        i) OPT_INSTALLED=1 ;;
        u) OPT_UNINSTALLED=1 ;;
        r) OPT_RECURSIVE=1 ;;
        h) usage ;;
        *) usage ;;
    esac
done
shift $((OPTIND - 1))

[[ $# -lt 1 ]] && usage
TARGET="$1"

[[ -d "$SBO_REPO" ]] || die "SBo repo not found at $SBO_REPO (set \$SBO_REPO)"
[[ -d "$PKG_LOG"  ]] || die "package log not found at $PKG_LOG (set \$PKG_LOG)"

# Build installed-package name set (strip version-arch-build suffix)
declare -A INSTALLED
while IFS= read -r entry; do
    name="${entry##*/}"              # basename
    name="${name%-*-*-*}"            # strip last three dash-fields
    INSTALLED["$name"]=1
done < <(find "$PKG_LOG" -maxdepth 1 -follow -type f)

is_installed() { [[ -n "${INSTALLED[$1]+set}" ]]; }

# Search .info files for packages that require TARGET (whole-word match)
find_dependents() {
    local pkg="$1"
    grep -rl "REQUIRES=.*\b${pkg}\b" "$SBO_REPO" --include="*.info" 2>/dev/null
}

print_result() {
    local info_file="$1"
    local prgnam
    prgnam=$(grep -m1 '^PRGNAM=' "$info_file" | cut -d'"' -f2)
    local category
    category=$(awk -F'/' '{print $(NF-2)}' <<< "$info_file")

    if is_installed "$prgnam"; then
        [[ $OPT_UNINSTALLED -eq 1 ]] && return
        printf "${C_INSTALLED}[I] %-40s${C_RESET}  ${C_CATEGORY}(%s)${C_RESET}\n" \
            "$prgnam" "$category"
    else
        [[ $OPT_INSTALLED -eq 1 ]] && return
        printf "${C_MISSING}[ ] %-40s${C_RESET}  ${C_CATEGORY}(%s)${C_RESET}\n" \
            "$prgnam" "$category"
    fi
}

printf "${C_HEADER}Packages requiring ${C_TARGET}'%s'${C_RESET}${C_HEADER}:${C_RESET}\n" "$TARGET"
printf "${C_SEP}%s${C_RESET}\n" "────────────────────────────────────────────────────"

mapfile -t DIRECT < <(find_dependents "$TARGET")

if [[ ${#DIRECT[@]} -eq 0 ]]; then
    printf "${C_SEP}(none found)${C_RESET}\n"
    exit 0
fi

for f in "${DIRECT[@]}"; do
    print_result "$f"
done | sort

if [[ $OPT_RECURSIVE -eq 1 ]]; then
    declare -A SEEN
    for f in "${DIRECT[@]}"; do
        prgnam=$(grep -m1 '^PRGNAM=' "$f" | cut -d'"' -f2)
        SEEN["$prgnam"]=1
    done

    printf "\n${C_HEADER}Packages requiring those (one level up):${C_RESET}\n"
    printf "${C_SEP}%s${C_RESET}\n" "────────────────────────────────────────────────────"

    declare -A SECOND_LEVEL
    for f in "${DIRECT[@]}"; do
        prgnam=$(grep -m1 '^PRGNAM=' "$f" | cut -d'"' -f2)
        while IFS= read -r f2; do
            p2=$(grep -m1 '^PRGNAM=' "$f2" | cut -d'"' -f2)
            [[ -z "${SEEN[$p2]+set}" && -z "${SECOND_LEVEL[$p2]+set}" ]] || continue
            SECOND_LEVEL["$p2"]="$f2"
        done < <(find_dependents "$prgnam")
    done

    if [[ ${#SECOND_LEVEL[@]} -eq 0 ]]; then
        printf "${C_SEP}(none found)${C_RESET}\n"
    else
        for p2 in "${!SECOND_LEVEL[@]}"; do
            print_result "${SECOND_LEVEL[$p2]}"
        done | sort
    fi
fi
