#!/bin/bash
# egctl — EdgeGuard Non-interaktives CLI Tool
# Für Scripting, Monitoring und Automation
# API-Calls laufen gegen localhost:8080 mit gespeichertem Token

EDGEGUARD_SHELL=0  # Verhindert Shell-Redirect in profile.d

OS_VERSION=$(cat /etc/edgeguard/os-version 2>/dev/null || echo "unknown")
APP_VERSION=$(cat /opt/edgeguard/.version 2>/dev/null || echo "unknown")
API_BASE="http://127.0.0.1:8080/api/v1"
TOKEN_CACHE="/run/egctl-token"           # tmpfs, weg nach Reboot
CREDS_FILE="/etc/edgeguard/egctl.conf"  # root:root 600

# ── Farben (nur wenn TTY) ──────────────────────────────────────────────────────
if [ -t 1 ]; then
    RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'
    CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
else
    RED=''; YELLOW=''; GREEN=''; CYAN=''; BOLD=''; RESET=''
fi

# ── JSON-Flag ─────────────────────────────────────────────────────────────────
JSON=0
ARGS=()
for arg in "$@"; do
    [ "$arg" = "--json" ] && JSON=1 || ARGS+=("$arg")
done
set -- "${ARGS[@]:-}"

# ── Hilfe ─────────────────────────────────────────────────────────────────────
usage() {
    cat << EOF
${BOLD}egctl${RESET} v${OS_VERSION} — EdgeGuard CLI

${BOLD}Usage:${RESET} egctl <befehl> [argumente] [--json]

${BOLD}Befehle:${RESET}
  status                  Systemstatus (OS, App, CPU, RAM, Uptime)
  version                 OS + App Version
  network show            Netzwerk-Interfaces mit IP/Status
  service status          Container + native Service Status
  ha status               HA Cluster Status
  reboot-required         Exit 0 = Neustart nötig, Exit 1 = nicht nötig
  update os check         Verfügbare OS-Updates prüfen
  update os install       OS-Update starten (async, Status via 'update os status')
  update os status        Status eines laufenden OS-Updates
  update app check        Verfügbare App-Updates prüfen (Registry)
  firewall status         nftables Firewall-Status
  dns status              Unbound DNS Status + Cache-Statistiken

${BOLD}Optionen:${RESET}
  --json                  Ausgabe als JSON (für Scripting/Monitoring)
  --help                  Diese Hilfe

${BOLD}Konfiguration:${RESET}
  ${CREDS_FILE}   # Credentials für API-Zugriff
  Format: USERNAME=admin
          PASSWORD=geheim

${BOLD}Beispiele:${RESET}
  egctl status --json
  egctl service status | grep edgeguard-api
  if egctl reboot-required; then echo "Neustart erforderlich"; fi
  egctl update os check && egctl update os install
EOF
}

[ ${#ARGS[@]} -eq 0 ] && { usage; exit 1; }

# ── API-Token holen (Login + Cache) ───────────────────────────────────────────
api_token() {
    # Cache prüfen (gültig für 50 Minuten — JWT läuft nach 60 min ab)
    if [ -f "$TOKEN_CACHE" ]; then
        CACHE_AGE=$(( $(date +%s) - $(stat -c %Y "$TOKEN_CACHE" 2>/dev/null || echo 0) ))
        if [ "$CACHE_AGE" -lt 3000 ]; then
            cat "$TOKEN_CACHE"
            return 0
        fi
    fi

    # Credentials laden
    if [ ! -f "$CREDS_FILE" ]; then
        echo "" # kein Token — API-Calls schlagen fehl
        return 1
    fi

    local user pass
    user=$(grep '^USERNAME=' "$CREDS_FILE" 2>/dev/null | cut -d= -f2-)
    pass=$(grep '^PASSWORD=' "$CREDS_FILE" 2>/dev/null | cut -d= -f2-)

    if [ -z "$user" ] || [ -z "$pass" ]; then
        echo ""
        return 1
    fi

    # Login
    local token
    token=$(curl -sf -X POST "${API_BASE}/auth/login" \
        -H "Content-Type: application/json" \
        -d "{\"username\":\"${user}\",\"password\":\"${pass}\"}" \
        --connect-timeout 3 --max-time 5 \
        2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('token',''))" 2>/dev/null)

    if [ -n "$token" ]; then
        echo "$token" > "$TOKEN_CACHE"
        chmod 600 "$TOKEN_CACHE"
    fi
    echo "$token"
}

# ── API-Call Wrapper ───────────────────────────────────────────────────────────
# api_get <pfad>   → gibt JSON zurück oder "" bei Fehler
api_get() {
    local path="$1"
    local token
    token=$(api_token)
    if [ -z "$token" ]; then
        echo ""
        return 1
    fi
    curl -sf "${API_BASE}${path}" \
        -H "Authorization: Bearer ${token}" \
        --connect-timeout 3 --max-time 10 \
        2>/dev/null
}

api_post() {
    local path="$1"
    local body="${2:-{}}"
    local token
    token=$(api_token)
    [ -z "$token" ] && return 1
    curl -sf -X POST "${API_BASE}${path}" \
        -H "Authorization: Bearer ${token}" \
        -H "Content-Type: application/json" \
        -d "$body" \
        --connect-timeout 3 --max-time 10 \
        2>/dev/null
}

# API verfügbar?
api_available() {
    curl -sf "${API_BASE%/v1}/health" --connect-timeout 2 --max-time 3 &>/dev/null
}

# ── Befehle ───────────────────────────────────────────────────────────────────

cmd_status() {
    local ip uptime load mem_total mem_used containers

    ip=$(ip -4 addr show scope global 2>/dev/null | awk '/inet / {print $2}' | head -1 | cut -d/ -f1)
    uptime=$(uptime -p 2>/dev/null || uptime | awk '{print $3,$4}')
    load=$(cut -d' ' -f1-3 /proc/loadavg 2>/dev/null)

    # RAM aus /proc/meminfo
    mem_total=$(awk '/MemTotal/ {printf "%.0f", $2/1024}' /proc/meminfo 2>/dev/null)
    mem_avail=$(awk '/MemAvailable/ {printf "%.0f", $2/1024}' /proc/meminfo 2>/dev/null)
    mem_used=$(( mem_total - mem_avail ))

    containers=$(docker ps -q 2>/dev/null | wc -l)
    reboot_flag=$( [ -f /var/run/reboot-required ] && echo "true" || echo "false" )

    if [ "$JSON" = "1" ]; then
        python3 - << PYEOF
import json, os
print(json.dumps({
    "os_version": "${OS_VERSION}",
    "app_version": "${APP_VERSION}",
    "hostname": os.uname().nodename,
    "ip": "${ip}",
    "uptime": "${uptime}",
    "load": "${load}",
    "memory_mb": {"total": ${mem_total:-0}, "used": ${mem_used:-0}},
    "containers_running": ${containers:-0},
    "reboot_required": ${reboot_flag}
}, indent=2))
PYEOF
    else
        echo -e "${BOLD}EdgeGuard System Status${RESET}"
        echo -e "  OS-Version:   ${CYAN}${OS_VERSION}${RESET}"
        echo -e "  App-Version:  ${CYAN}${APP_VERSION}${RESET}"
        echo -e "  Hostname:     $(hostname -s)"
        echo -e "  IP:           ${ip:-n/a}"
        echo -e "  Uptime:       ${uptime}"
        echo -e "  Load:         ${load}"
        echo -e "  RAM:          ${mem_used}/${mem_total} MB"
        echo -e "  Container:    ${containers} laufend"
        if [ -f /var/run/reboot-required ]; then
            echo -e "  ${YELLOW}⚠ Neustart erforderlich${RESET}"
        fi
    fi
}

cmd_version() {
    if [ "$JSON" = "1" ]; then
        echo "{\"os\": \"${OS_VERSION}\", \"app\": \"${APP_VERSION}\"}"
    else
        echo "OS:  ${OS_VERSION}"
        echo "App: ${APP_VERSION}"
    fi
}

cmd_network_show() {
    local result
    result=$(api_get "/network/interfaces" 2>/dev/null)

    if [ -n "$result" ] && [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi

    # Fallback: direkt aus Kernel lesen
    echo -e "${BOLD}Netzwerk-Interfaces${RESET}"
    for iface in /sys/class/net/*; do
        name=$(basename "$iface")
        [[ "$name" == lo ]] && continue
        [[ "$name" == docker* || "$name" == veth* || "$name" == br-* ]] && continue

        state=$(cat "$iface/operstate" 2>/dev/null || echo "unknown")
        ip=$(ip -4 addr show dev "$name" 2>/dev/null | awk '/inet / {print $2}' | head -1)
        speed=$(cat "$iface/speed" 2>/dev/null || echo "?")

        state_color="$RED"
        [ "$state" = "up" ] && state_color="$GREEN"

        printf "  %-12s %b%-8s%b  %-20s  %s Mbit\n" \
            "$name" "$state_color" "$state" "$RESET" "${ip:-keine IP}" "$speed"
    done
}

cmd_service_status() {
    local result
    result=$(api_get "/services" 2>/dev/null)

    if [ -n "$result" ] && [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi

    echo -e "${BOLD}Container-Status${RESET}"
    docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null \
        | awk 'NR==1 {print "  "$0; next} {
            if ($0 ~ /Up/) color="\033[0;32m"
            else color="\033[0;31m"
            print "  "color$0"\033[0m"
        }'

    echo ""
    echo -e "${BOLD}Native Services${RESET}"
    for svc in keepalived unbound chrony nftables; do
        state=$(systemctl is-active "$svc" 2>/dev/null || echo "inactive")
        color="$RED"
        [ "$state" = "active" ] && color="$GREEN"
        printf "  %-14s %b%s%b\n" "$svc" "$color" "$state" "$RESET"
    done
}

cmd_ha_status() {
    local result
    result=$(api_get "/ha/status" 2>/dev/null)

    if [ -n "$result" ] && [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi

    if [ -n "$result" ]; then
        python3 - << PYEOF
import json, sys
try:
    d = json.loads('''${result}''').get('data', {})
    print(f"  Rolle:     {d.get('role','unknown')}")
    print(f"  Status:    {d.get('state','unknown')}")
    print(f"  VIP:       {d.get('vip','n/a')}")
    print(f"  Peer:      {d.get('peer_ip','n/a')}")
except:
    pass
PYEOF
    else
        # Fallback: ha.conf lesen
        echo -e "${BOLD}HA-Cluster Status${RESET}"
        if [ -f /etc/edgeguard/ha.conf ]; then
            grep -E "^HA_" /etc/edgeguard/ha.conf | while IFS='=' read -r key val; do
                printf "  %-14s %s\n" "$key:" "$val"
            done
        else
            echo "  Keepalived-Status:"
            systemctl is-active keepalived 2>/dev/null | xargs -I{} echo "  State: {}"
        fi
    fi
}

cmd_reboot_required() {
    if [ -f /var/run/reboot-required ]; then
        [ "$JSON" = "1" ] && echo '{"required":true}' || echo "Neustart erforderlich"
        exit 0
    else
        [ "$JSON" = "1" ] && echo '{"required":false}' || echo "Kein Neustart erforderlich"
        exit 1
    fi
}

cmd_update_os_check() {
    echo -e "${BOLD}Prüfe verfügbare OS-Updates...${RESET}"
    local result
    result=$(api_get "/system/os-update/check" 2>/dev/null)

    if [ -z "$result" ]; then
        echo -e "${RED}Fehler: API nicht erreichbar${RESET}" >&2
        exit 1
    fi

    if [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi

    python3 - << PYEOF
import json, sys
d = json.loads('''${result}''').get('data', {})
pkgs = d.get('packages', [])
total = d.get('total', 0)
reboot = d.get('reboot_required', False)
print(f"  Verfügbare Updates: {total}")
if reboot:
    print("  \033[1;33m⚠ Neustart nach Update erforderlich\033[0m")
for p in pkgs[:20]:
    rboot = " \033[33m[reboot]\033[0m" if p.get('requires_reboot') else ""
    print(f"  \033[36m{p['name']:<30}\033[0m {p.get('current_version','?'):<20} → {p.get('new_version','?')}{rboot}")
if len(pkgs) > 20:
    print(f"  ... und {len(pkgs)-20} weitere")
PYEOF
}

cmd_update_os_install() {
    echo -e "${BOLD}Starte OS-Update...${RESET}"

    # Reset + Start
    api_post "/system/os-update/reset" >/dev/null 2>&1
    local result
    result=$(api_post "/system/os-update/install" 2>/dev/null)

    if [ -z "$result" ]; then
        echo -e "${RED}Fehler: Update konnte nicht gestartet werden${RESET}" >&2
        exit 1
    fi

    echo "Update läuft im Hintergrund."
    echo "Status: egctl update os status"
    echo "Log:    egctl update os status --json | python3 -c \"import json,sys; [print(l) for l in json.load(sys.stdin)['data']['log']]\""
}

cmd_update_os_status() {
    local result
    result=$(api_get "/system/os-update/status" 2>/dev/null)
    [ -z "$result" ] && { echo "API nicht erreichbar" >&2; exit 1; }

    if [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi

    python3 - << PYEOF
import json
d = json.loads('''${result}''').get('data', {})
status = d.get('status', 'unknown')
log = d.get('log', [])
err = d.get('error', '')
colors = {'idle':'\033[0m','running':'\033[1;34m','done':'\033[0;32m','error':'\033[0;31m'}
c = colors.get(status, '\033[0m')
print(f"  Status: {c}{status}\033[0m")
if err:
    print(f"  Fehler: \033[31m{err}\033[0m")
for line in log[-10:]:
    print(f"  {line}")
PYEOF
}

cmd_update_app_check() {
    local result
    result=$(api_get "/system/check-update" 2>/dev/null)
    [ -z "$result" ] && { echo "API nicht erreichbar" >&2; exit 1; }
    [ "$JSON" = "1" ] && { echo "$result"; return; }

    python3 - << PYEOF
import json
d = json.loads('''${result}''')
avail = d.get('update_available', False)
ver = d.get('latest_version', '?')
cur = d.get('current_version', '?')
if avail:
    print(f"  \033[1;33m⚠ Update verfügbar: {cur} → {ver}\033[0m")
    print(f"  GUI: Einstellungen → Software-Update")
else:
    print(f"  \033[0;32m✓ App ist aktuell ({cur})\033[0m")
PYEOF
}

cmd_firewall_status() {
    local result
    result=$(api_get "/firewall/status" 2>/dev/null)
    if [ -n "$result" ] && [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi
    echo -e "${BOLD}Firewall-Status (nftables)${RESET}"
    nft list ruleset 2>/dev/null | head -40 || echo "  nftables nicht verfügbar"
}

cmd_dns_status() {
    local result
    result=$(api_get "/dns/status" 2>/dev/null)
    if [ -n "$result" ] && [ "$JSON" = "1" ]; then
        echo "$result"
        return
    fi

    echo -e "${BOLD}DNS-Status (Unbound)${RESET}"
    state=$(systemctl is-active unbound 2>/dev/null || echo "unknown")
    echo -e "  Service: $([ "$state" = "active" ] && echo "${GREEN}${state}${RESET}" || echo "${RED}${state}${RESET}")"

    if systemctl is-active unbound &>/dev/null; then
        stats=$(unbound-control stats_noreset 2>/dev/null | grep -E "^total\.(num\.(queries|cachehits)|recursion\.time\.avg)")
        while IFS='=' read -r key val; do
            printf "  %-40s %s\n" "$key" "$val"
        done <<< "$stats"
    fi
}

# ── Dispatch ──────────────────────────────────────────────────────────────────
case "${ARGS[0]:-}" in
    status)              cmd_status ;;
    version)             cmd_version ;;
    network)
        case "${ARGS[1]:-}" in
            show) cmd_network_show ;;
            *)    echo "egctl network: unbekannt '${ARGS[1]:-}' (verfügbar: show)" >&2; exit 1 ;;
        esac ;;
    service)
        case "${ARGS[1]:-}" in
            status) cmd_service_status ;;
            *)      echo "egctl service: unbekannt '${ARGS[1]:-}'" >&2; exit 1 ;;
        esac ;;
    ha)
        case "${ARGS[1]:-}" in
            status) cmd_ha_status ;;
            *)      echo "egctl ha: unbekannt '${ARGS[1]:-}'" >&2; exit 1 ;;
        esac ;;
    reboot-required)     cmd_reboot_required ;;
    update)
        case "${ARGS[1]:-}" in
            os)
                case "${ARGS[2]:-}" in
                    check)   cmd_update_os_check ;;
                    install) cmd_update_os_install ;;
                    status)  cmd_update_os_status ;;
                    *)       echo "egctl update os: check | install | status" >&2; exit 1 ;;
                esac ;;
            app)
                case "${ARGS[2]:-}" in
                    check)   cmd_update_app_check ;;
                    *)       echo "egctl update app: check" >&2; exit 1 ;;
                esac ;;
            *)  echo "egctl update: os | app" >&2; exit 1 ;;
        esac ;;
    firewall)
        case "${ARGS[1]:-}" in
            status) cmd_firewall_status ;;
            *)      echo "egctl firewall: status" >&2; exit 1 ;;
        esac ;;
    dns)
        case "${ARGS[1]:-}" in
            status) cmd_dns_status ;;
            *)      echo "egctl dns: status" >&2; exit 1 ;;
        esac ;;
    --help|-h|help)      usage ;;
    *)
        echo "egctl: Unbekannter Befehl '${ARGS[0]:-}'" >&2
        echo "Führe 'egctl --help' für eine Übersicht aus." >&2
        exit 1 ;;
esac
