#!/usr/bin/env bash
# H7 AV CLOSE · multi-avenue desk antivirus closure
#
# Avenues (methods) that can show inject / thrash on Grok item detail:
#   1. CLIP / PRIMARY inject (av-inject-guard)
#   2. PUBLIC listen ports (outside)
#   3. UNKNOWN local listeners (not desk allowlist)
#   4. NEW_COMM process classes (secure-learn)
#   5. FILE inject dumps / staging
#   6. SCAR / thrash pids
#
#   ./Build/h7-av-close.sh once|status|ports|inject
#
# Law: INSIDE in · OUTSIDE out · close every avenue · never ML · God Bless
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT="$ROOT/out"
JSON="$OUT/h7_av_close.json"
FEED="$OUT/h7_vocab_feed.tsv"
cmd="${1:-once}"
TS="$(date -Iseconds)"
mkdir -p "$OUT"

n0() { local v="${1//[^0-9]/}"; echo "${v:-0}"; }
say() { echo "[$TS] AV_CLOSE · $*"; }

# shellcheck source=h7-desk-ports.lib.sh
. "$(cd "$(dirname "$0")" && pwd)/h7-desk-ports.lib.sh"

# desk-known INSIDE listeners (not thrash)
# format: addr:port or just port
is_desk_port() {
  local addr="$1"
  # legit Apache HTTP(S) on own public/WG IPs · LE + email/grok/h7 desk
  is_desk_public_ok "$addr" && return 0
  case "$addr" in
    127.0.0.1:80|127.0.0.1:443|127.0.0.1:3306|127.0.0.1:631|\
    127.0.0.1:18770|127.0.0.1:18771|127.0.0.1:18772|127.0.0.1:18773|\
    127.0.0.1:5335|127.0.0.53:*|127.0.0.54:53|[::1]:631|\
    127.0.0.1:8080|127.0.0.1:3000|127.0.0.1:5173|127.0.0.1:9229)
      return 0 ;;
  esac
  # any pure loopback DNS resolver style
  [[ "$addr" =~ ^127\.0\.0\.(53|54) ]] && return 0
  return 1
}

scan_ports() {
  PUBLIC=()       # thrash public only
  DESK_PUB=()     # allowlisted public HTTP(S)
  LOCAL_OK=()
  LOCAL_UNK=()
  local line addr
  while read -r line; do
    addr=$(echo "$line" | awk '{print $4}')
    [[ -z "$addr" ]] && continue
    if is_desk_loopback "$addr"; then
      if is_desk_port "$addr"; then
        LOCAL_OK+=("$addr")
      else
        # still loopback · soft unknown (method still open if weird port)
        LOCAL_UNK+=("$addr")
      fi
    elif is_desk_public_ok "$addr"; then
      DESK_PUB+=("$addr")
      LOCAL_OK+=("$addr")  # count as desk-ok surface
    else
      PUBLIC+=("$addr")
    fi
  done < <(ss -ltnH 2>/dev/null || true)

  N_PUB=${#PUBLIC[@]}
  N_DESK_PUB=${#DESK_PUB[@]}
  N_LOK=${#LOCAL_OK[@]}
  N_LUNK=${#LOCAL_UNK[@]}
}

run_inject() {
  INJ_FLAG=0 INJ_CLR=0 INJ_RM=0
  if [[ -x "$ROOT/Build/av-inject-guard.sh" ]]; then
    timeout 20 bash "$ROOT/Build/av-inject-guard.sh" >>"$OUT/h7_av_close.log" 2>&1 || true
  fi
  if [[ -f "$OUT/av_inject_latest.json" ]]; then
    INJ_FLAG=$(python3 -c "import json;d=json.load(open('$OUT/av_inject_latest.json'));print(int(d.get('inject_flagged') or 0))" 2>/dev/null || echo 0)
    INJ_CLR=$(python3 -c "import json;d=json.load(open('$OUT/av_inject_latest.json'));print(int(d.get('primary_cleared') or 0))" 2>/dev/null || echo 0)
    INJ_RM=$(python3 -c "import json;d=json.load(open('$OUT/av_inject_latest.json'));print(int(d.get('files_removed') or 0))" 2>/dev/null || echo 0)
  fi
  INJ_FLAG=$(n0 "$INJ_FLAG"); INJ_CLR=$(n0 "$INJ_CLR"); INJ_RM=$(n0 "$INJ_RM")
}

read_secure() {
  NEWC=0 SCAR=0 SEC_OK=true PUB_H=0
  if [[ -f "$OUT/h7_secure_learn.json" ]]; then
    NEWC=$(python3 -c "import json;d=json.load(open('$OUT/h7_secure_learn.json'));print((d.get('learned') or {}).get('new_comm_classes') or 0)" 2>/dev/null || echo 0)
    PUB_H=$(python3 -c "import json;d=json.load(open('$OUT/h7_secure_learn.json'));print((d.get('inside_out') or {}).get('public_listen_n') or 0)" 2>/dev/null || echo 0)
    SEC_OK=$(python3 -c "import json;d=json.load(open('$OUT/h7_secure_learn.json'));print(bool(d.get('ok')))" 2>/dev/null || echo True)
  fi
  if [[ -f "$OUT/scar_check_latest.json" ]]; then
    SCAR=$(python3 -c "import json;d=json.load(open('$OUT/scar_check_latest.json'));print(d.get('scar_count') or 0)" 2>/dev/null || echo 0)
  fi
  NEWC=$(n0 "$NEWC"); SCAR=$(n0 "$SCAR"); PUB_H=$(n0 "$PUB_H")
}

close_score() {
  # higher = more open avenues (worse)
  local open=0
  (( N_PUB > 0 )) && open=$((open + 3 + N_PUB))
  (( INJ_FLAG > 0 )) && open=$((open + 4))
  (( SCAR > 0 )) && open=$((open + SCAR))
  (( NEWC > 5 )) && open=$((open + 2))
  (( N_LUNK > 4 )) && open=$((open + 1))
  OPEN=$open
  local ok=true
  (( open > 0 )) && ok=false
  OK=$ok
}

write_json() {
  local pub_js desk_pub_js unk_js ok_js methods
  pub_js=$(printf '%s\n' "${PUBLIC[@]:-}" | head -20 | python3 -c 'import sys,json; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))')
  desk_pub_js=$(printf '%s\n' "${DESK_PUB[@]:-}" | head -20 | python3 -c 'import sys,json; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))')
  unk_js=$(printf '%s\n' "${LOCAL_UNK[@]:-}" | head -30 | python3 -c 'import sys,json; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))')
  ok_js=$(printf '%s\n' "${LOCAL_OK[@]:-}" | head -30 | python3 -c 'import sys,json; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))')
  N_DESK_PUB=${N_DESK_PUB:-0}

  # method status lines for item detail
  methods=$(python3 - <<PY
import json
avenues=[
  {"id":"inject_clip","name":"CLIP/PRIMARY inject","open": ${INJ_FLAG} > 0, "detail":"flagged=${INJ_FLAG} cleared=${INJ_CLR} files_rm=${INJ_RM}"},
  {"id":"public_listen","name":"PUBLIC thrash ports","open": ${N_PUB} > 0, "detail":"thrash_n=${N_PUB} · desk_https=${N_DESK_PUB}"},
  {"id":"unknown_local","name":"UNKNOWN local listeners","open": ${N_LUNK} > 4, "detail":"n=${N_LUNK} (soft if loopback)"},
  {"id":"new_comm","name":"NEW_COMM process class","open": ${NEWC} > 5, "detail":"new_comm=${NEWC}"},
  {"id":"scar","name":"SCAR residual","open": ${SCAR} > 0, "detail":"scar=${SCAR}"},
  {"id":"secure_learn","name":"secure-learn ok","open": str("${SEC_OK}").lower() in ("false","0"), "detail":"ok=${SEC_OK} pub_h=${PUB_H}"},
]
closed=sum(1 for a in avenues if not a["open"])
print(json.dumps({"avenues":avenues,"closed":closed,"total":len(avenues)}))
PY
)

  cat >"$JSON" <<EOF
{
  "ts": "$TS",
  "word": "H7_AV_CLOSE",
  "she": "Hostess 7",
  "ok": $OK,
  "open_score": $OPEN,
  "closure": "multi-avenue · inject + thrash ports + comm + scar · desk HTTPS allowlisted",
  "ports": {
    "public_n": $N_PUB,
    "public": $pub_js,
    "desk_public_n": $N_DESK_PUB,
    "desk_public": $desk_pub_js,
    "desk_ok_n": $N_LOK,
    "desk_ok": $ok_js,
    "local_unknown_n": $N_LUNK,
    "local_unknown": $unk_js
  },
  "inject": {
    "flagged": $INJ_FLAG,
    "primary_cleared": $INJ_CLR,
    "files_removed": $INJ_RM
  },
  "secure": {
    "ok": $([[ "${SEC_OK}" == "True" || "${SEC_OK}" == "true" ]] && echo true || echo false),
    "new_comm": $NEWC,
    "public_listen_h": $PUB_H,
    "scar_count": $SCAR
  },
  "methods": $methods,
  "detail_line": "AV close open=$OPEN · thrash_pub=$N_PUB · desk_https=$N_DESK_PUB · inject=$INJ_FLAG · scar=$SCAR · new_comm=$NEWC · desk_ports=$N_LOK",
  "law": "close inject + thrash public · Apache 80/443 on own IPs = desk legit · God Bless"
}
EOF

  {
    printf '%s\t%s\t%s\t%s\n' "$TS" "av_close" "SECURITY" "learn"
    if (( N_PUB > 0 )); then
      printf '%s\t%s\t%s\t%s\n' "$TS" "PUBLIC_${N_PUB}" "SECURITY" "alert"
    fi
    if (( INJ_FLAG > 0 )); then
      printf '%s\t%s\t%s\t%s\n' "$TS" "INJECT_${INJ_FLAG}" "SECURITY" "alert"
    fi
  } >>"$FEED" 2>/dev/null || true
}

run_once() {
  scan_ports
  run_inject
  read_secure
  close_score
  write_json
  say "$OK open=$OPEN thrash_pub=$N_PUB desk_https=${N_DESK_PUB:-0} inject=$INJ_FLAG scar=$SCAR newc=$NEWC desk=$N_LOK unk=$N_LUNK"
  cat "$JSON"
}

status() {
  if [[ -f "$JSON" ]]; then cat "$JSON"; else say "no close yet · run once"; fi
  echo "--- listen ---"
  ss -ltnH 2>/dev/null | head -25 || true
}

case "$cmd" in
  once|close) run_once ;;
  ports)
    scan_ports
    INJ_FLAG=0; INJ_CLR=0; INJ_RM=0
    read_secure; close_score; write_json; cat "$JSON"
    ;;
  inject) run_once ;;
  status) status ;;
  *)
    echo "usage: $0 once|ports|inject|status" >&2
    exit 2
    ;;
esac
