#!/usr/bin/env bash

# ==============================================================================
# sysinfo.sh - Kompakte Systemübersicht für Linux-Systeme
#
# Autor:   Daniel Nuß
# Version: 2.1.0
# ==============================================================================

set -o pipefail

readonly SCRIPT_VERSION="2.1.0"
readonly EXTERNAL_IP_URL="https://api.ipify.org"
readonly WEATHER_URL="https://wttr.in/Hochheim%20am%20Main?format=%t,+%C&lang=de"

show_help() {
    cat <<EOF
Verwendung: ${0##*/} [OPTION]

Zeigt eine kompakte Übersicht des lokalen Linux-Systems an.

Optionen:
  -h, --help       Diese Hilfe anzeigen
  -V, --version    Skriptversion anzeigen
EOF
}

case "${1:-}" in
    "")
        ;;
    -h|--help)
        show_help
        exit 0
        ;;
    -V|--version)
        printf '%s %s\n' "${0##*/}" "$SCRIPT_VERSION"
        exit 0
        ;;
    *)
        printf 'Unbekannte Option: %s\n\n' "$1" >&2
        show_help >&2
        exit 2
        ;;
esac

# Farben nur verwenden, wenn die Ausgabe tatsächlich an ein Terminal geht.
color_green=""
color_red=""
color_white_bg=""
color_reset=""

if [[ -t 1 && -n "${TERM:-}" ]] && command -v tput >/dev/null 2>&1; then
    color_green=$(tput setaf 2 2>/dev/null || true)
    color_red=$(tput setaf 1 2>/dev/null || true)
    color_white_bg=$(tput setab 7 2>/dev/null || true)
    color_reset=$(tput sgr0 2>/dev/null || true)
fi

print_banner_line() {
    local text="${1:-}"
    local width=59
    local text_length=${#text}
    local left_padding=0
    local right_padding=0

    if (( text_length < width )); then
        left_padding=$(( (width - text_length) / 2 ))
        right_padding=$(( width - text_length - left_padding ))
    fi

    printf '%s%s%*s%s%*s%s\n' \
        "$color_red" "$color_white_bg" \
        "$left_padding" "" "$text" "$right_padding" "" \
        "$color_reset"
}

print_banner() {
    local line_number

    for ((line_number = 0; line_number < 5; line_number++)); do
        print_banner_line
    done

    print_banner_line "Daniel Nuß & Marcus Heße-Nuß"
    print_banner_line "Am Bittelborn 23A"
    print_banner_line "65239 Hochheim am Main"

    for ((line_number = 0; line_number < 5; line_number++)); do
        print_banner_line
    done
}

get_uptime() {
    local uptime_raw="0"
    local uptime_seconds=0
    local days=0
    local hours=0
    local minutes=0
    local seconds=0

    if [[ -r /proc/uptime ]]; then
        read -r uptime_raw _ < /proc/uptime
        uptime_seconds=${uptime_raw%%.*}
    fi

    [[ "$uptime_seconds" =~ ^[0-9]+$ ]] || uptime_seconds=0

    seconds=$((uptime_seconds % 60))
    minutes=$((uptime_seconds / 60 % 60))
    hours=$((uptime_seconds / 3600 % 24))
    days=$((uptime_seconds / 86400))

    printf '%d Tage, %02dh%02dm%02ds' "$days" "$hours" "$minutes" "$seconds"
}

get_device_model() {
    local model_file

    for model_file in \
        /proc/device-tree/model \
        /sys/firmware/devicetree/base/model; do
        if [[ -f "$model_file" && -r "$model_file" ]]; then
            tr -d '\0' < "$model_file"
            return 0
        fi
    done

    return 1
}

get_cpu_temperature() {
    local temperature=""
    local raw_temperature=""
    local sensor_file

    if command -v vcgencmd >/dev/null 2>&1; then
        temperature=$(vcgencmd measure_temp 2>/dev/null || true)
        if [[ "$temperature" =~ ([0-9]+([.][0-9]+)?) ]]; then
            printf '%s°C' "${BASH_REMATCH[1]}"
            return 0
        fi
    fi

    for sensor_file in /sys/class/thermal/thermal_zone*/temp; do
        [[ -r "$sensor_file" ]] || continue
        read -r raw_temperature < "$sensor_file"

        if [[ "$raw_temperature" =~ ^[0-9]+$ ]] && \
           (( raw_temperature > 0 && raw_temperature < 200000 )); then
            awk -v value="$raw_temperature" 'BEGIN { printf "%.1f°C", value / 1000 }'
            return 0
        fi
    done

    return 1
}

get_memory_value() {
    local field="$1"
    awk -v field="$field" '$1 == field ":" { print $2; exit }' /proc/meminfo 2>/dev/null
}

get_process_count() {
    local process_directory
    local process_count=0

    for process_directory in /proc/[0-9]*; do
        [[ -d "$process_directory" ]] || continue
        ((process_count += 1))
    done

    printf '%d' "$process_count"
}

print_ipv4_addresses() {
    local interface_name
    local address
    local found=0

    if command -v ip >/dev/null 2>&1; then
        while read -r interface_name address; do
            [[ -n "$interface_name" && -n "$address" ]] || continue
            interface_name=${interface_name%%@*}
            address=${address%%/*}
            printf 'IPv4 %-16s: %s\n' "$interface_name" "$address"
            found=1
        done < <(ip -o -4 address show scope global 2>/dev/null | awk '{ print $2, $4 }')
    fi

    if (( found == 0 )); then
        printf 'IPv4................: nicht ermittelbar\n'
    fi
}

fetch_https() {
    local url="$1"

    if command -v curl >/dev/null 2>&1; then
        curl --ipv4 --fail --silent --show-error \
            --connect-timeout 3 --max-time 5 "$url" 2>/dev/null
    elif command -v wget >/dev/null 2>&1; then
        wget --inet4-only --quiet --output-document=- \
            --timeout=5 --tries=1 "$url" 2>/dev/null
    else
        return 127
    fi
}

get_external_ip() {
    local external_ip=""

    external_ip=$(fetch_https "$EXTERNAL_IP_URL" | tr -d '\r\n' || true)

    if [[ -n "$external_ip" ]]; then
        printf '%s' "$external_ip"
    else
        printf 'nicht ermittelbar'
    fi
}

get_weather() {
    local weather=""

    weather=$(fetch_https "$WEATHER_URL" | tr -d '\r\n' || true)

    if [[ -n "$weather" ]]; then
        printf '%s' "$weather"
    else
        printf 'nicht ermittelbar'
    fi
}

get_apt_update_status() {
    local apt_output=""
    local update_count=0

    if ! command -v apt >/dev/null 2>&1; then
        printf 'nicht geprüft (APT nicht installiert)'
        return 0
    fi

    # Absichtlich kein "apt update": Der Schnellcheck verwendet ausschließlich
    # die bereits lokal vorhandenen Paketlisten und verändert das System nicht.
    if ! apt_output=$(LC_ALL=C apt list --upgradable 2>/dev/null); then
        printf 'Prüfung fehlgeschlagen (APT)'
        return 0
    fi

    update_count=$(awk 'NR > 1 && NF > 0 { count++ } END { print count + 0 }' \
        <<< "$apt_output")

    if (( update_count > 0 )); then
        printf 'JA - %d APT-Update(s) verfügbar (lokale Paketlisten)' "$update_count"
    else
        printf 'NEIN - laut lokalen APT-Paketlisten'
    fi
}

print_disk_space() {
    if command -v pydf >/dev/null 2>&1; then
        pydf
    else
        df -hT
    fi
}

uptime_text=$(get_uptime)
device_model=$(get_device_model || true)
cpu_temperature=$(get_cpu_temperature || true)
memory_total_kb=$(get_memory_value MemTotal)
memory_available_kb=$(get_memory_value MemAvailable)
load_one="-"
load_five="-"
load_fifteen="-"

if [[ -r /proc/loadavg ]]; then
    read -r load_one load_five load_fifteen _ < /proc/loadavg
fi

# MemAvailable existiert seit Linux 3.14. Auf sehr alten Systemen zurückfallen.
if [[ -z "$memory_available_kb" ]]; then
    memory_available_kb=$(get_memory_value MemFree)
fi

print_banner

printf '%s%s\n' "$color_green" "$(date '+%A, %e. %B %Y, %T')"
printf '%s\n' "$(uname -srmo)"
printf '%s' "$color_red"

printf 'Uptime...............: %s\n' "$uptime_text"

if [[ -n "$device_model" ]]; then
    printf 'Gerätetyp............: %s\n' "$device_model"
fi

printf 'Speicher verfügbar...: %s kB / %s kB gesamt\n' \
    "${memory_available_kb:-unbekannt}" "${memory_total_kb:-unbekannt}"

if [[ -n "$cpu_temperature" ]]; then
    printf 'CPU-Temperatur.......: %s\n' "$cpu_temperature"
fi

printf 'Last (Durchschnitt)..: %s, %s, %s (1, 5, 15 min)\n' \
    "$load_one" "$load_five" "$load_fifteen"
printf 'Laufende Prozesse....: %s\n' "$(get_process_count)"

print_ipv4_addresses

printf 'IP-Adresse extern....: %s\n' "$(get_external_ip)"
printf 'Hochheim a.M. Wetter.: %s\n' "$(get_weather)"
printf 'Freier Speicherplatz.:\n'
print_disk_space

# Gewünschte letzte sichtbare Ausgabezeile.
printf 'Paket-Updates........: %s\n' "$(get_apt_update_status)"

printf '%s' "$color_reset"
