#!/usr/bin/env bash
set -euo pipefail
# POS: network scan — Parallel ping sweep of CIDR

source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"

usage() {
    cat <<EOF
Usage: pos network scan <cidr> [--full] [--retries N]

Two-phase network scan using nmap.

Phase 1: Fast host discovery (finds alive hosts)
Phase 2: Full metadata scan on alive hosts only (--full only)

Options:
  --full        Detailed scan: OS, ports, services, NSE scripts (slower)
  --retries N   Retries per host in discovery (default: 1)

Examples:
  pos network scan 192.168.1.0/24
  pos network scan 10.0.0.0/28 --full
  pos network scan 172.1.1.104
  pos network scan 192.168.1.0/24 --retries 3
EOF
    exit 0
}

# Deps guard before -h|--help (help also errors without nmap).
command -v nmap &>/dev/null || err "nmap not found — install with: sudo apt install nmap"

case "${1:-}" in
    -h|--help|"") usage ;;
esac

net=""
full=0
retries=1

while [[ $# -gt 0 ]]; do
    case "$1" in
        --full)    full=1; shift ;;
        --retries)
            if [[ -z "${2:-}" || "$2" == --* ]]; then
                err "--retries requires a number"
            fi
            retries="$2"; shift 2 ;;
        *)         net="$1"; shift ;;
    esac
done

if [[ -z "$net" ]]; then
    err "CIDR missing — e.g. pos network scan 192.168.1.0/24"
fi

# ── Input validation ───────────────────────────────────────────
# Bare IP → /32
if [[ "$net" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
    net="${net}/32"
# Valid CIDR
elif [[ "$net" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
    : # ok
else
    err "bad target '$net' — expected IP (172.1.1.104) or CIDR (192.168.1.0/24)"
fi

# ── Estimate host count ────────────────────────────────────────
cidr_bits="${net##*/}"
if [[ "$cidr_bits" -ge 24 ]]; then
    host_estimate=$(( 1 << (32 - cidr_bits) ))
elif [[ "$cidr_bits" -ge 16 ]]; then
    host_estimate="$(( 1 << (32 - cidr_bits) ))+"
else
    host_estimate="many"
fi

is_root=0
[[ $EUID -eq 0 ]] && is_root=1

can_sudo=0
if [[ "$is_root" -eq 1 ]]; then
    can_sudo=1
elif sudo -n nmap -V &>/dev/null; then
    can_sudo=1
elif [[ "$full" -eq 1 && -t 0 ]]; then
    can_sudo=1
fi

tmpfile=$(mktemp /tmp/scan-XXXXXX.txt)
trap 'rm -f "$tmpfile"' EXIT

# ── Phase 1: Fast host discovery ───────────────────────────────
nmap_cmd="nmap"
[[ "$can_sudo" -eq 1 ]] && nmap_cmd="sudo nmap"

echo "Discovering hosts in $net (~$host_estimate) ..."
echo

$nmap_cmd -sn -T5 -n \
    --min-rate 1000 \
    --min-parallelism 1024 \
    --min-hostgroup 1024 \
    --max-retries "$retries" \
    --host-timeout 5s \
    "$net" 2>/dev/null | awk '/^Nmap scan report for/ {
    ip = $(NF);
    gsub(/[()]/, "", ip);
    print ip;
}' > "$tmpfile"

host_count=$(wc -l < "$tmpfile")

if [[ "$host_count" -eq 0 ]]; then
    echo "No hosts found."
    exit 0
fi

echo "Found $host_count host(s)."
echo

if [[ "$full" -eq 0 ]]; then
    cat "$tmpfile"
    echo
    echo "Done."
    exit 0
fi

# ── Phase 2: Full metadata scan ────────────────────────────────
echo "Scanning $host_count host(s) (full) ..."
echo

nmap_args="-sV --version-intensity 9 -sC -T4 -n"
nmap_args="$nmap_args --min-parallelism 256 --max-parallelism 512"
nmap_args="$nmap_args --min-hostgroup 256 --max-hostgroup 512"
nmap_args="$nmap_args --min-rate 1000 --max-retries 1"
nmap_args="$nmap_args --host-timeout 60s --max-rtt-timeout 200ms"
nmap_args="$nmap_args --script ssh-hostkey,ssl-cert,http-title,http-server-header,smb-os-discovery,nbstat,rpcinfo"
[[ "$can_sudo" -eq 1 ]] && nmap_args="$nmap_args -O --osscan-guess"

# shellcheck disable=SC2086
$nmap_cmd $nmap_args -iL "$tmpfile" 2>/dev/null | \
    awk -v cy="$CYAN" -v bo="$BOLD" -v di="$DIM" -v re="$RESET" '
BEGIN { ip_count = 0; has_os = 0; hc = cy bo; dc = di }

/^Nmap scan report for/ {
    ip = $(NF);
    gsub(/[()]/, "", ip);
    hostname = "";
    if ($(NF) ~ /^\(/) {
        hostname = $(NF-1);
    } else if (NF > 4) {
        if (ip != $(NF-1) && $(NF-1) !~ /^(for|[0-9])/) {
            hostname = $(NF-1);
        }
    }
    if (ip_count > 0) printf "\n";
    ip_count++;
    has_os = 0;
    if (hostname != "" && hostname != ip)
        printf "%s%s  (%s)%s\n", hc, ip, hostname, re;
    else
        printf "%s%s%s\n", hc, ip, re;
}

/^MAC Address/ {
    vendor = $0;
    sub(/.*\(/, "", vendor);
    sub(/\).*/, "", vendor);
    printf "  %s%-10s%s %s  %s\n", dc, "MAC:", re, $3, vendor;
}

/^Aggressive OS guesses:/ {
    has_os = 1;
    line = $0;
    sub(/.*guesses: /, "", line);
    gsub(/\s*\(.*/, "", line);
    printf "  %s%-10s%s %s\n", dc, "OS:", re, line;
}

/^OS details:/ {
    has_os = 1;
    sub(/.*OS details: /, "");
    printf "  %s%-10s%s %s\n", dc, "OS:", re, $0;
}

/^Running:/ {
    sub(/.*Running: /, "");
    printf "  %s%-10s%s %s\n", dc, "OS:", re, $0;
}

/^Service Info:/ {
    line = $0;
    sub(/.*Service Info:/, "", line);
    gsub(/^ +/, "", line);
    if (has_os == 0)
        printf "  %s%-10s%s %s\n", dc, "Info:", re, line;
}

/^\| ssh-hostkey:/ {
    line = $0;
    sub(/.*ssh-hostkey:/, "", line);
    gsub(/^ +/, "", line);
    if (line ~ /SHA256/) {
        match(line, /SHA256:[A-Za-z0-9+\/=]+/);
        key = substr(line, RSTART, RLENGTH);
        printf "  %s%-10s%s %s\n", dc, "SSH:", re, key;
    }
}

/^\| http-title:/ {
    line = $0;
    sub(/.*http-title:/, "", line);
    gsub(/^ +/, "", line);
    gsub(/\s*\[.*$/, "", line);
    if (line != "" && line !~ /^No/)
        printf "  %s%-10s%s %s\n", dc, "HTTP Title:", re, line;
}

/^\| http-server-header:/ {
    line = $0;
    sub(/.*http-server-header:/, "", line);
    gsub(/^ +/, "", line);
    printf "  %s%-10s%s %s\n", dc, "HTTP Server:", re, line;
}

/^\|_?NetBIOS name:/ {
    line = $0;
    sub(/.*NetBIOS name:/, "", line);
    sub(/,.*$/, "", line);
    gsub(/^ +/, "", line);
    printf "  %s%-10s%s %s\n", dc, "NetBIOS:", re, line;
}

/^\|_?SMB OS:/ {
    line = $0;
    sub(/.*SMB OS:/, "", line);
    gsub(/^ +/, "", line);
    printf "  %s%-10s%s %s\n", dc, "SMB:", re, line;
}

/^\|_?Domain:/ {
    line = $0;
    sub(/.*Domain:/, "", line);
    gsub(/^ +/, "", line);
    if (line != "" && line !~ /^WORKGROUP/)
        printf "  %s%-10s%s %s\n", dc, "Domain:", re, line;
}

/^\|   [0-9]+\/tcp/ {
    line = $0;
    gsub(/^ *\| */, "", line);
    printf "  %s%-10s%s %s\n", dc, "RPC:", re, line;
}

/^[0-9]+\/tcp[[:space:]]+open/ {
    port = $1; service = $3;
    version = "";
    for (i = 4; i <= NF; i++) version = version " " $i;
    gsub(/^ +/, "", version);
    if (version != "")
        printf "  %s%-10s%s %s — %s\n", dc, port, re, service, version;
    else
        printf "  %s%-10s%s %s\n", dc, port, re, service;
}
'

echo
echo "Done."
