#!/usr/bin/env bash

# Fluxion Diagnostics Script
# Collects system, tool, and wireless adapter information for troubleshooting.
# Usage: sudo bash scripts/diagnostics.sh [wireless_interface]
# If no interface is given, all wireless interfaces are diagnosed.

set -o pipefail

RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BOLD='\033[1m'
RESET='\033[0m'

section() { echo -e "\n${BOLD}=== $1 ===${RESET}"; }
item() { printf "  %-24s %s\n" "$1:" "$2"; }
ok() { echo -e "${GREEN}$1${RESET}"; }
warn() { echo -e "${YELLOW}$1${RESET}"; }
err() { echo -e "${RED}$1${RESET}"; }
cmd_version() {
	local cmd="$1"
	if command -v "$cmd" &>/dev/null; then
		local ver
		ver=$("$cmd" --version 2>&1 | head -1) || ver=$("$cmd" -v 2>&1 | head -1) || ver="(installed)"
		item "$cmd" "$ver"
	else
		item "$cmd" "$(err 'NOT FOUND')"
	fi
}

# Resolve fluxion root directory.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "$SCRIPT_DIR/../fluxion.sh" ]; then
	FLUXION_ROOT="$SCRIPT_DIR/.."
elif [ -f "./fluxion.sh" ]; then
	FLUXION_ROOT="."
else
	echo "Error: cannot find fluxion.sh. Run from the fluxion directory or scripts/." >&2
	exit 1
fi

# Source interface utilities if available.
HAS_IFACE_UTILS=0
if source "$FLUXION_ROOT/lib/InterfaceUtils.sh" 2>/dev/null; then
	HAS_IFACE_UTILS=1
fi

# ── Fluxion ──────────────────────────────────────────────────────────────────
section "Fluxion"
fluxion_version=$(grep -oP 'FLUXIONVersion=\K[0-9]+' "$FLUXION_ROOT/fluxion.sh" 2>/dev/null)
fluxion_revision=$(grep -oP 'FLUXIONRevision=\K[0-9]+' "$FLUXION_ROOT/fluxion.sh" 2>/dev/null)
item "Version" "${fluxion_version:-?}.${fluxion_revision:-?}"
item "Path" "$(cd "$FLUXION_ROOT" && pwd)"
if command -v git &>/dev/null && [ -d "$FLUXION_ROOT/.git" ]; then
	item "Git branch" "$(git -C "$FLUXION_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null)"
	item "Git commit" "$(git -C "$FLUXION_ROOT" log -1 --format='%h %s' 2>/dev/null)"
	local_changes=$(git -C "$FLUXION_ROOT" status --porcelain 2>/dev/null | wc -l)
	[ "$local_changes" -gt 0 ] && item "Uncommitted changes" "$(warn "$local_changes file(s)")"
fi

# ── System ───────────────────────────────────────────────────────────────────
section "System"
item "Kernel" "$(uname -r)"
item "OS" "$(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || uname -o)"
item "Arch" "$(uname -m)"
item "Hostname" "$(hostname)"
item "Uptime" "$(uptime -p 2>/dev/null || uptime | sed 's/.*up/up/')"
item "Running as" "$(id -un) (UID $(id -u))"
if [ "$(id -u)" -ne 0 ]; then
	echo "  $(warn 'WARNING: Not running as root. Some checks will be incomplete.')"
fi

# ── Display Environment ─────────────────────────────────────────────────────
section "Display Environment"
if [ -n "$TMUX" ]; then
	item "Inside tmux" "yes ($(tmux display-message -p '#{session_name}' 2>/dev/null))"
	item "tmux version" "$(tmux -V 2>/dev/null)"
else
	item "Inside tmux" "no"
	cmd_version tmux
fi
if [ -n "$DISPLAY" ]; then
	item "DISPLAY" "$DISPLAY"
	if command -v xdpyinfo &>/dev/null; then
		item "X11 server" "$(xdpyinfo 2>/dev/null | grep 'vendor string' | sed 's/.*: *//' || echo 'unavailable')"
	fi
	cmd_version xterm
else
	item "DISPLAY" "$(warn 'not set (xterm mode will fail)')"
fi

# ── Dependencies ─────────────────────────────────────────────────────────────
section "Dependencies"
# Core tools in the order they appear in requiredCLITools
for tool in \
	aircrack-ng airodump-ng aireplay-ng airmon-ng \
	hostapd lighttpd php-cgi dhcpd dnsmasq \
	mdk4 cowpatty iw macchanger rfkill nmap openssl \
	iptables curl bc awk route fuser killall unzip 7zr; do
	cmd_version "$tool"
done

# ── Wireless Interfaces ─────────────────────────────────────────────────────
section "Wireless Interfaces"

# Build interface list: from argument or auto-detect.
if [ -n "$1" ]; then
	ifaces=("$1")
else
	ifaces=()
	for dev in /sys/class/net/*/wireless; do
		[ -e "$dev" ] || continue
		ifaces+=("$(basename "$(dirname "$dev")")")
	done
	# Also pick up monitor-mode interfaces (no wireless/ dir but type monitor).
	for dev in /sys/class/net/*/; do
		iface=$(basename "$dev")
		if iw dev "$iface" info 2>/dev/null | grep -q "type monitor"; then
			# Add if not already in list.
			found=0
			for existing in "${ifaces[@]}"; do [ "$existing" = "$iface" ] && found=1; done
			[ "$found" -eq 0 ] && ifaces+=("$iface")
		fi
	done
fi

if [ ${#ifaces[@]} -eq 0 ]; then
	echo "  $(err 'No wireless interfaces found!')"
else
	for iface in "${ifaces[@]}"; do
		echo -e "\n  ${BOLD}[$iface]${RESET}"
		# Basic info from iw
		iw_info=$(iw dev "$iface" info 2>/dev/null)
		if [ -n "$iw_info" ]; then
			item "  Type" "$(echo "$iw_info" | grep -oP 'type \K\w+' || echo 'unknown')"
			chan=$(echo "$iw_info" | grep -oP 'channel \K[0-9]+' || true)
			freq=$(echo "$iw_info" | grep -oP '\(\K[0-9]+ MHz' || true)
			[ -n "$chan" ] && item "  Channel" "$chan ($freq)"
			txpower=$(echo "$iw_info" | grep -oP 'txpower \K[0-9.]+' || true)
			[ -n "$txpower" ] && item "  TX Power" "${txpower} dBm"
		fi

		# Link state
		link_state=$(cat "/sys/class/net/$iface/operstate" 2>/dev/null || echo "unknown")
		item "  Link state" "$link_state"

		# Driver
		driver=$(readlink "/sys/class/net/$iface/device/driver" 2>/dev/null | xargs basename 2>/dev/null)
		item "  Driver" "${driver:-unknown}"

		# USB/PCI bus info
		devpath=$(readlink -f "/sys/class/net/$iface/device" 2>/dev/null)
		if [[ "$devpath" == */usb* ]]; then
			# Walk up to the USB device directory containing idVendor.
			usbdir="$devpath"
			while [ -n "$usbdir" ] && [ ! -f "$usbdir/idVendor" ]; do
				usbdir="${usbdir%/*}"
			done
			if [ -f "$usbdir/idVendor" ]; then
				usb_id="$(cat "$usbdir/idVendor"):$(cat "$usbdir/idProduct")"
			else
				usb_id="unknown"
			fi
			item "  Bus" "USB ($usb_id)"
		elif [[ "$devpath" == */pci* ]]; then
			item "  Bus" "PCI"
		else
			item "  Bus" "unknown"
		fi

		# Chipset via airmon-ng
		chipset=$(airmon-ng 2>/dev/null | grep -F "$iface" | awk '{for(i=4;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/ *$//')
		[ -n "$chipset" ] && item "  Chipset" "$chipset"

		# Band support
		if [ "$HAS_IFACE_UTILS" -eq 1 ]; then
			interface_bands "$iface" 2>/dev/null
			item "  Bands" "${InterfaceBands:-unknown}"
		else
			# Fallback: check phy info directly
			phy=$(cat "/sys/class/net/$iface/phy80211/name" 2>/dev/null)
			if [ -n "$phy" ]; then
				bands=""
				iw phy "$phy" info 2>/dev/null | grep -qE "24[0-9]{2}" && bands="2.4GHz"
				iw phy "$phy" info 2>/dev/null | grep -qE "5[0-9]{3}" && { [ -n "$bands" ] && bands="$bands/"; bands="${bands}5GHz"; }
				item "  Bands" "${bands:-unknown}"
			fi
		fi

		# Monitor mode support
		phy=$(cat "/sys/class/net/$iface/phy80211/name" 2>/dev/null)
		if [ -n "$phy" ]; then
			if iw phy "$phy" info 2>/dev/null | grep -q "\* monitor"; then
				item "  Monitor mode" "$(ok 'supported')"
			else
				item "  Monitor mode" "$(err 'NOT supported')"
			fi
			if iw phy "$phy" info 2>/dev/null | grep -q "\* AP"; then
				item "  AP mode" "$(ok 'supported')"
			else
				item "  AP mode" "$(warn 'NOT supported')"
			fi
		fi

		# Injection test (only if root and interface is up)
		if [ "$(id -u)" -eq 0 ] && [ "$link_state" != "down" ]; then
			inj_result=$(timeout 10 aireplay-ng --test "$iface" 2>&1 | grep -oE "Injection is working!|No Answer..." | head -1)
			if [ "$inj_result" = "Injection is working!" ]; then
				item "  Injection" "$(ok 'working')"
			elif [ -n "$inj_result" ]; then
				item "  Injection" "$(err 'FAILED (no answer)')"
			else
				item "  Injection" "$(warn 'test inconclusive')"
			fi
		fi
	done
fi

# ── rfkill ───────────────────────────────────────────────────────────────────
section "RF Kill Status"
if command -v rfkill &>/dev/null; then
	rfkill list 2>/dev/null | while IFS= read -r line; do echo "  $line"; done
else
	echo "  $(warn 'rfkill not found')"
fi

# ── Interfering Processes ────────────────────────────────────────────────────
section "Interfering Processes"
for proc in NetworkManager wpa_supplicant dhclient avahi-daemon; do
	pid=$(pgrep -x "$proc" 2>/dev/null | head -1)
	if [ -n "$pid" ]; then
		item "$proc" "$(warn "running (PID $pid)")"
	else
		item "$proc" "$(ok 'not running')"
	fi
done

# ── Regulatory Domain ───────────────────────────────────────────────────────
section "Regulatory Domain"
if command -v iw &>/dev/null; then
	reg_country=$(iw reg get 2>/dev/null | grep -oP 'country \K\S+' | head -1)
	item "Current domain" "${reg_country:-unknown}"
	# Show DFS status
	dfs_regions=$(iw reg get 2>/dev/null | grep -c "DFS" || true)
	item "DFS channels" "$dfs_regions entries"
else
	echo "  $(warn 'iw not found')"
fi

# ── iptables ─────────────────────────────────────────────────────────────────
section "iptables"
if command -v iptables &>/dev/null && [ "$(id -u)" -eq 0 ]; then
	rules=$(iptables -L -n 2>/dev/null | grep -Ecv '^Chain|^target|^$'; true)
	item "Active rules" "$rules"
	nat_rules=$(iptables -t nat -L -n 2>/dev/null | grep -Ecv '^Chain|^target|^$'; true)
	item "NAT rules" "$nat_rules"
	if [ -f "$FLUXION_ROOT/iptables-rules" ]; then
		item "Backup file" "$(ok 'present')"
	else
		item "Backup file" "not present"
	fi
else
	echo "  $(warn 'Requires root')"
fi

# ── AppArmor ────────────────────────────────────────────────────────────────
section "AppArmor"
if [ -d /etc/apparmor.d ]; then
	if command -v aa-status &>/dev/null && [ "$(id -u)" -eq 0 ]; then
		enforced=$(aa-status 2>/dev/null | grep -oP '\K[0-9]+(?= profiles are in enforce mode)' || echo "?")
		item "Profiles enforced" "$enforced"
	fi
	if [ -f /etc/apparmor.d/usr.sbin.dhcpd ]; then
		item "dhcpd profile" "present"
		if [ -f /etc/apparmor.d/local/usr.sbin.dhcpd ]; then
			if grep -q "fluxspace" /etc/apparmor.d/local/usr.sbin.dhcpd 2>/dev/null; then
				item "dhcpd fluxspace override" "$(ok 'configured')"
			else
				item "dhcpd fluxspace override" "$(warn 'file exists but no fluxspace rule')"
			fi
		else
			item "dhcpd fluxspace override" "$(err 'MISSING — dhcpd will fail to read /tmp/fluxspace/')"
		fi
	else
		item "dhcpd profile" "not present (OK)"
	fi
else
	item "AppArmor" "not installed"
fi

# ── lighttpd ─────────────────────────────────────────────────────────────────
section "lighttpd Service"
if command -v systemctl &>/dev/null; then
	lighttpd_active=$(systemctl is-active lighttpd 2>&1) || true
	lighttpd_enabled=$(systemctl is-enabled lighttpd 2>&1) || true
	item "Service active" "${lighttpd_active:-unknown}"
	item "Service enabled" "${lighttpd_enabled:-unknown}"
	if [ "$lighttpd_active" = "active" ]; then
		echo "  $(warn 'WARNING: lighttpd system service is running and will conflict with Captive Portal (port 80)')"
	fi
else
	item "systemctl" "not available"
fi
# Check if port 80 is in use.
if [ "$(id -u)" -eq 0 ] && command -v ss &>/dev/null; then
	port80=$(ss -tlnp 'sport = :80' 2>/dev/null | tail -n +2)
	if [ -n "$port80" ]; then
		item "Port 80" "$(warn "IN USE: $(echo "$port80" | awk '{print $NF}' | head -1)")"
	else
		item "Port 80" "$(ok 'available')"
	fi
fi

# ── Workspace ────────────────────────────────────────────────────────────────
section "Workspace"
workspace="/tmp/fluxspace"
if [ -d "$workspace" ]; then
	item "Path" "$workspace (exists)"
	item "Contents" "$(ls -1 "$workspace" 2>/dev/null | wc -l) items"
	item "Disk usage" "$(du -sh "$workspace" 2>/dev/null | awk '{print $1}')"
else
	item "Path" "$workspace (does not exist)"
fi
tmp_free=$(df -h /tmp 2>/dev/null | tail -1 | awk '{print $4}')
item "/tmp free space" "${tmp_free:-unknown}"

# ── Summary ──────────────────────────────────────────────────────────────────
section "End of Diagnostics"
echo "  Collected at: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo "  Paste this output when reporting issues."
