#!/usr/bin/env bash
#
# onx-process-list — `ps aux` + /proc/loadavg JSON çıktısı.
#
# Stdin:  JSON {"sort":"cpu|mem|pid|time","limit":50,"filter_user":"apache"}
# Stdout: JSON {
#           "processes": [{user,pid,cpu,mem,vsz_kb,rss_kb,state,started,time,cmd}, ...],
#           "summary":   {total,running,sleeping,zombie,stopped},
#           "load":      {"1m":..., "5m":..., "15m":..., "cores":N}
#         }
# Exit:   0=ok  1=invalid_input  3=execution_fail
#
# Sudoers: apache ALL=(root) NOPASSWD: /usr/local/onoxsoft/bin/onx-process-list
#
# Master Plan §5 — HestiaCP modeli

set -euo pipefail

# Load shared helpers
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/_lib/common.sh"

# ─── Parse stdin ─────────────────────────────────────────────────────────────
onx_json_input

SORT=$(onx_json_field 'sort' 'cpu')
LIMIT=$(onx_json_field 'limit' '50')
FILTER_USER=$(onx_json_field 'filter_user' '')

# Validate sort key (whitelist)
case "$SORT" in
    cpu|mem|pid|time) ;;
    *) onx_die 1 "invalid sort: must be cpu|mem|pid|time" ;;
esac

# Validate limit (1..500)
[[ "$LIMIT" =~ ^[0-9]+$ ]] || onx_die 1 "limit must be integer"
(( LIMIT >= 1 && LIMIT <= 500 )) || onx_die 1 "limit out of range (1..500)"

# Validate user (lowercase, digits, underscore — Linux username pattern)
if [[ -n "$FILTER_USER" ]]; then
    [[ "$FILTER_USER" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] || onx_die 1 "invalid filter_user"
fi

# ─── ps sort flag map ────────────────────────────────────────────────────────
case "$SORT" in
    cpu)  PS_SORT='-%cpu' ;;
    mem)  PS_SORT='-%mem' ;;
    pid)  PS_SORT='pid'   ;;
    time) PS_SORT='-time' ;;
esac

require_cmd ps
require_cmd awk
require_cmd jq

# ─── Collect ps aux ──────────────────────────────────────────────────────────
# Fields: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# Note: --sort works on most Linux ps implementations; if absent we sort in awk.
if ps_out=$(ps -eo user:32,pid,%cpu,%mem,vsz,rss,stat,start_time,time,comm,args --sort="${PS_SORT}" --no-headers 2>/dev/null); then
    : # ok
else
    # Fallback: ps aux (BSD format)
    ps_out=$(ps aux --no-headers 2>/dev/null || ps aux 2>/dev/null | tail -n +2)
fi

# Optional user filter
if [[ -n "$FILTER_USER" ]]; then
    ps_out=$(printf '%s\n' "$ps_out" | awk -v u="$FILTER_USER" '$1==u { print }')
fi

# Take top N
ps_top=$(printf '%s\n' "$ps_out" | head -n "$LIMIT")

# ─── Build processes JSON array (awk → jq -s for safe escaping) ──────────────
processes_json=$(printf '%s\n' "$ps_top" | awk '
    NF >= 11 {
        user=$1; pid=$2; cpu=$3; mem=$4; vsz=$5; rss=$6; state=$7;
        # start_time + time may both be in fields; harden against locale.
        started=$8; cputime=$9;
        cmd=""
        for (i=11; i<=NF; i++) cmd = (cmd=="" ? $i : cmd " " $i)
        # JSON-escape cmd via jq via env var (avoids shell-escape issues)
        printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", user, pid, cpu, mem, vsz, rss, state, started, cputime, cmd
    }
' | jq -Rsn '
    [inputs | split("\n") | .[] | select(length > 0) | split("\t") |
        {
            user: .[0],
            pid:  (.[1] | tonumber? // 0),
            cpu:  (.[2] | tonumber? // 0),
            mem:  (.[3] | tonumber? // 0),
            vsz_kb: (.[4] | tonumber? // 0),
            rss_kb: (.[5] | tonumber? // 0),
            state: .[6],
            started: .[7],
            time: .[8],
            cmd: .[9]
        }
    ]
')

# ─── Summary counts (full ps_out, not just top N) ────────────────────────────
total=$(printf '%s\n' "$ps_out" | grep -cv '^$' || true)
running=$(printf '%s\n' "$ps_out"  | awk '$7 ~ /^R/' | grep -cv '^$' || true)
sleeping=$(printf '%s\n' "$ps_out" | awk '$7 ~ /^[SI]/' | grep -cv '^$' || true)
zombie=$(printf '%s\n' "$ps_out"   | awk '$7 ~ /^Z/' | grep -cv '^$' || true)
stopped=$(printf '%s\n' "$ps_out"  | awk '$7 ~ /^[T]/' | grep -cv '^$' || true)

# ─── Load average + CPU cores (from /proc) ───────────────────────────────────
load_1m=0
load_5m=0
load_15m=0
if [[ -r /proc/loadavg ]]; then
    read -r load_1m load_5m load_15m _rest </proc/loadavg
fi

cores=$(nproc 2>/dev/null || grep -c '^processor' /proc/cpuinfo 2>/dev/null || echo 1)

# ─── Final JSON envelope ─────────────────────────────────────────────────────
jq -n \
    --argjson processes "$processes_json" \
    --argjson total "$total" \
    --argjson running "$running" \
    --argjson sleeping "$sleeping" \
    --argjson zombie "$zombie" \
    --argjson stopped "$stopped" \
    --argjson load_1m "$load_1m" \
    --argjson load_5m "$load_5m" \
    --argjson load_15m "$load_15m" \
    --argjson cores "$cores" \
    '{
        processes: $processes,
        summary: {
            total:    $total,
            running:  $running,
            sleeping: $sleeping,
            zombie:   $zombie,
            stopped:  $stopped
        },
        load: {
            "1m":  $load_1m,
            "5m":  $load_5m,
            "15m": $load_15m,
            cores: $cores
        }
    }'

exit 0
