#!/bin/bash
set -euo pipefail

MEM_WARN_MIB=1024
MEM_CRIT_MIB=4096
SWAP_WARN_MIB=2048
SWAP_CRIT_MIB=8192
CPU_WARN=1.0
CPU_CRIT=2.0

W_NAME_VAL=40

RED="\033[0;31m"
YELLOW="\033[0;33m"
GREEN="\033[0;32m"
BLUE="\033[0;34m"
NC="\033[0m"

to_mib() { echo $(( (${1:-0}) / 1024 / 1024 )); }

human_bytes() {
  local b="${1:-0}"
  if [ -z "$b" ] || [ "$b" = "0" ]; then echo "unlimited"; return; fi
  local mib=$((b / 1024 / 1024))
  if [ "$mib" -ge 1024 ]; then echo "$((mib/1024))GiB"; else echo "${mib}MiB"; fi
}

pad_name() {
  # fixed width name (NO COLORS)
  printf "%-${W_NAME_VAL}.${W_NAME_VAL}s" "$1"
}

declare -A ST_CPU
declare -A ST_MEM
while IFS=$'\t' read -r name cpu mem; do
  name="$(echo "$name" | xargs)"
  ST_CPU["$name"]="$(echo "$cpu" | xargs)"
  ST_MEM["$name"]="$(echo "$mem" | xargs)"
done < <(docker stats --no-stream --format '{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}' 2>/dev/null || true)

tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT

# status|name|sev|cpu_lim|mem_lim|swap_lim|cpu_used|mem_used|oom|cid
for c in $(docker ps -aq); do
  cid="$(docker inspect -f '{{.Id}}' "$c" | cut -c1-12)"
  name="$(docker inspect -f '{{.Name}}' "$c" | sed 's#^/##')"
  status="$(docker inspect -f '{{.State.Status}}' "$c")"
  oom="$(docker inspect -f '{{.State.OOMKilled}}' "$c")"

  mem_b="$(docker inspect -f '{{.HostConfig.Memory}}' "$c")"
  swap_b="$(docker inspect -f '{{.HostConfig.MemorySwap}}' "$c")"
  mem_mib="$(to_mib "$mem_b")"
  swap_mib="$(to_mib "$swap_b")"

  mem_lim="$(human_bytes "$mem_b")"
  swap_lim="$(human_bytes "$swap_b")"

  nanocpus="$(docker inspect -f '{{.HostConfig.NanoCpus}}' "$c")"
  if [ -z "$nanocpus" ] || [ "$nanocpus" = "0" ]; then
    cpu_lim="unlimited"
  else
    cpu_lim="$(awk -v n="$nanocpus" 'BEGIN { printf "%.2f", n/1000000000 }')"
  fi

  cpu_used="${ST_CPU[$name]:--}"
  mem_used="${ST_MEM[$name]:--}"

  # severity for name
  sev="green"
  if [ "$oom" = "true" ]; then sev="red"; fi
  if [ "$mem_mib" -ge "$MEM_CRIT_MIB" ] || [ "$swap_mib" -ge "$SWAP_CRIT_MIB" ]; then sev="red"; fi

  if [ "$sev" != "red" ]; then
    if [ "$status" != "running" ]; then sev="yellow"; fi
    if [ "$mem_mib" -ge "$MEM_WARN_MIB" ] || [ "$swap_mib" -ge "$SWAP_WARN_MIB" ]; then sev="yellow"; fi
    if [ "$cpu_lim" != "unlimited" ]; then
      awk -v c="$cpu_lim" -v warn="$CPU_WARN" 'BEGIN{ exit !(c+0 >= warn) }' && sev="yellow"
      awk -v c="$cpu_lim" -v crit="$CPU_CRIT" 'BEGIN{ exit !(c+0 >= crit) }' && sev="red"
    fi
  fi

  echo "${status}|${name}|${sev}|${cpu_lim}|${mem_lim}|${swap_lim}|${cpu_used}|${mem_used}|${oom}|${cid}" >> "$tmp"
done

print_section() {
  local title="$1"
  local filter="$2"

  echo
if [ "$title" = "RUNNING" ]; then
  printf "${GREEN}===== %s =====${NC}\n" "$title"
else
  printf "${RED}===== %s =====${NC}\n" "$title"
fi

#  echo "===== ${title} ====="

  # Table without colors, aligned by column
  {
    echo "NAME|CPU_LIM|MEM_LIM|SWAP_LIM|CPU%|MEM_USED|STATUS|OOM|CONTAINER_ID"

    awk -F'|' "$filter {print \$0}" "$tmp" | sort -t'|' -k2,2 | \
    while IFS='|' read -r status name sev cpu_lim mem_lim swap_lim cpu_used mem_used oom cid; do
      name_fixed="$(pad_name "$name")"
      echo "${name_fixed}|${cpu_lim}|${mem_lim}|${swap_lim}|${cpu_used}|${mem_used}|${status}|${oom}|${cid}|${sev}"
    done
  } | column -t -s'|' | \
  while IFS= read -r line; do
    # colorize NAME only after alignment
    # last column is severity, remove it from output
    sev="$(echo "$line" | awk '{print $NF}')"
    line_no_sev="${line% *}"

    name_field="$(echo "$line_no_sev" | cut -d' ' -f1)"
    rest="${line_no_sev#"$name_field"}"

    if [ "$name_field" = "NAME" ]; then
      echo "$line_no_sev"
      continue
    fi

    case "$sev" in
      red)    printf "%b%s\n" "${RED}${name_field}${NC}" "$rest" ;;
      yellow) printf "%b%s\n" "${YELLOW}${name_field}${NC}" "$rest" ;;
      *)      printf "%b%s\n" "${GREEN}${name_field}${NC}" "$rest" ;;
    esac
  done
}


print_section "RUNNING" '$1=="running"'
print_section "NOT RUNNING" '$1!="running"'
