#!/usr/bin/env bash
# BigBrother setup -- Idempotent bootstrap for network surveillance implant
# Targets: Orange Pi Zero 3 (primary), RPi Zero 2W (jumpbox), generic Debian
set -euo pipefail

# ── Colors ──────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

# ── Globals ─────────────────────────────────────────────────────────────────
INSTALL_DIR="/opt/.cache/bb"
TOOLS_DIR="/opt/tools"
VENV_DIR="${INSTALL_DIR}/.venv"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_FILE="/tmp/bb_setup_$(date +%Y%m%d_%H%M%S).log"

# Flags
FLAG_REINSTALL=0
FLAG_JUMPBOX=0
FLAG_NO_TOOLS=0
FLAG_VERIFY_ONLY=0
FLAG_ENABLE_SVC=0

# ── Output helpers ──────────────────────────────────────────────────────────
info()  { echo -e "${GREEN}[+]${NC} $*"; }
warn()  { echo -e "${YELLOW}[!]${NC} $*"; }
error() { echo -e "${RED}[-]${NC} $*"; }
step()  { echo -e "${CYAN}[*]${NC} ${BOLD}$*${NC}"; }
debug() { echo -e "${BLUE}[.]${NC} $*"; }

die() {
    error "$*"
    exit 1
}

log_cmd() {
    # Run command, log output, show status
    "$@" >> "$LOG_FILE" 2>&1 || { error "Command failed: $*  (see $LOG_FILE)"; return 1; }
}

# ── Argument parsing ────────────────────────────────────────────────────────
usage() {
    echo "Usage: $0 [OPTIONS]"
    echo ""
    echo "Options:"
    echo "  --reinstall        Force reinstall all components"
    echo "  --jumpbox          Minimal install for RPi Zero 2W jumpbox mode"
    echo "  --no-tools         Skip GitHub tool installation"
    echo "  --enable-service   Enable and start bigbrother-core.service after setup"
    echo "  --verify-only      Check installation without changes"
    echo "  -h, --help         Show this help"
    exit 0
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --reinstall)       FLAG_REINSTALL=1; shift ;;
        --jumpbox)         FLAG_JUMPBOX=1; shift ;;
        --no-tools)        FLAG_NO_TOOLS=1; shift ;;
        --enable-service)  FLAG_ENABLE_SVC=1; shift ;;
        --verify-only)     FLAG_VERIFY_ONLY=1; shift ;;
        -h|--help)         usage ;;
        *)                 die "Unknown option: $1" ;;
    esac
done

# ── Root check ──────────────────────────────────────────────────────────────
[[ $EUID -eq 0 ]] || die "Must be run as root"

# ── Platform detection ──────────────────────────────────────────────────────
detect_platform() {
    if [ -f /proc/device-tree/model ]; then
        local model
        model=$(tr -d '\0' < /proc/device-tree/model)
        if echo "$model" | grep -qi "orange pi zero3"; then
            echo "opi_zero3"
        elif echo "$model" | grep -qi "orange pi zero 3"; then
            echo "opi_zero3"
        elif echo "$model" | grep -qi "raspberry pi zero 2"; then
            echo "pi_zero"
        elif echo "$model" | grep -qi "raspberry pi 4"; then
            echo "pi4"
        else
            echo "generic"
        fi
    else
        echo "generic"
    fi
}

detect_arch() {
    local arch
    arch=$(dpkg --print-architecture 2>/dev/null || uname -m)
    case "$arch" in
        arm64|aarch64) echo "arm64" ;;
        amd64|x86_64)  echo "amd64" ;;
        armhf|armv7l)  echo "armhf" ;;
        *)             echo "$arch" ;;
    esac
}

PLATFORM=$(detect_platform)
ARCH=$(detect_arch)

step "Platform: ${PLATFORM} (${ARCH})"
echo "  Install dir: ${INSTALL_DIR}"
echo "  Tools dir:   ${TOOLS_DIR}"
echo "  Log file:    ${LOG_FILE}"
echo ""

# Force jumpbox mode on Pi Zero
if [[ "$PLATFORM" == "pi_zero" ]]; then
    FLAG_JUMPBOX=1
    info "Pi Zero 2W detected -- forcing jumpbox mode"
fi

# ════════════════════════════════════════════════════════════════════════════
#  VERIFY-ONLY MODE
# ════════════════════════════════════════════════════════════════════════════
if [[ $FLAG_VERIFY_ONLY -eq 1 ]]; then
    step "Running verification checks..."
    ERRORS=0

    verify_bin() {
        if command -v "$1" &>/dev/null; then
            info "  $1 ... found ($(command -v "$1"))"
        else
            error "  $1 ... MISSING"
            ((ERRORS++))
        fi
    }

    verify_file() {
        if [[ -f "$1" ]]; then
            info "  $1 ... exists"
        else
            error "  $1 ... MISSING"
            ((ERRORS++))
        fi
    }

    verify_dir() {
        if [[ -d "$1" ]]; then
            info "  $1 ... exists"
        else
            error "  $1 ... MISSING"
            ((ERRORS++))
        fi
    }

    verify_db() {
        if [[ -f "$1" ]] && sqlite3 "$1" "SELECT 1;" &>/dev/null; then
            local tables
            tables=$(sqlite3 "$1" ".tables" 2>/dev/null)
            info "  $1 ... valid (tables: $tables)"
        else
            error "  $1 ... MISSING or invalid"
            ((ERRORS++))
        fi
    }

    echo ""
    step "System binaries"
    for bin in python3 tcpdump tshark nmap bettercap macchanger aircrack-ng \
               zstd cryptsetup tmux jq sqlite3 curl wget git; do
        verify_bin "$bin"
    done

    echo ""
    step "Directory structure"
    for dir in "${INSTALL_DIR}" "${INSTALL_DIR}/core" "${INSTALL_DIR}/modules" \
               "${INSTALL_DIR}/utils" "${INSTALL_DIR}/config" "${INSTALL_DIR}/data" \
               "${INSTALL_DIR}/storage" "${INSTALL_DIR}/services" "${INSTALL_DIR}/scripts"; do
        verify_dir "$dir"
    done

    echo ""
    step "Python venv"
    verify_dir "${VENV_DIR}"
    if [[ -f "${VENV_DIR}/bin/python3" ]]; then
        info "  Python venv functional"
        if "${VENV_DIR}/bin/python3" -c "import scapy, click, rich, yaml, cryptography, requests, dpkt, zstandard, dnslib, jinja2" 2>/dev/null; then
            info "  Core Python imports OK"
        else
            error "  Some Python imports FAILED"
            ((ERRORS++))
        fi
    fi

    echo ""
    step "Data databases"
    verify_db "${INSTALL_DIR}/data/innocuous_macs.db"
    verify_db "${INSTALL_DIR}/data/oui.db"
    verify_db "${INSTALL_DIR}/data/os_sigs.db"
    verify_db "${INSTALL_DIR}/data/dhcp_fingerprints.db"
    verify_db "${INSTALL_DIR}/data/ja3_fingerprints.db"

    if [[ $FLAG_NO_TOOLS -eq 0 ]]; then
        echo ""
        step "External tools"
        verify_dir "${TOOLS_DIR}/Responder"
        verify_dir "${TOOLS_DIR}/enum4linux-ng"
        verify_dir "${TOOLS_DIR}/smbmap"
        verify_bin chisel
        verify_bin ligolo-agent
        verify_bin kerbrute
    fi

    echo ""
    if [[ $ERRORS -eq 0 ]]; then
        info "All verification checks passed"
    else
        error "${ERRORS} verification check(s) failed"
    fi
    exit $ERRORS
fi

# ════════════════════════════════════════════════════════════════════════════
#  1. SYSTEM PACKAGES
# ════════════════════════════════════════════════════════════════════════════
step "Installing system packages..."

export DEBIAN_FRONTEND=noninteractive

# Core package list
PACKAGES=(
    build-essential python3-dev python3-venv python3-pip python3-setuptools
    libpcap-dev libssl-dev libffi-dev libsqlite3-dev
    tcpdump tshark nmap masscan
    net-tools iproute2 bridge-utils wireless-tools wpasupplicant
    macchanger
    iptables nftables ebtables arptables
    cryptsetup
    zstd
    tmux screen
    curl wget git jq sqlite3 file
    dnsutils hostapd dnsmasq
    wireguard-tools
    autossh socat stunnel4
    iodine
    ppp usb-modeswitch
)

# Only install bluetooth packages on platforms that support it
if [[ "$PLATFORM" != "pi_zero" ]]; then
    PACKAGES+=(bluez libbluetooth-dev)
fi

# Check if proxychains is available (package name varies)
if apt-cache show proxychains4 &>/dev/null 2>&1; then
    PACKAGES+=(proxychains4)
elif apt-cache show proxychains-ng &>/dev/null 2>&1; then
    PACKAGES+=(proxychains-ng)
fi

# Check if sshuttle is available as system package
if apt-cache show sshuttle &>/dev/null 2>&1; then
    PACKAGES+=(sshuttle)
fi

# aircrack-ng not available on all Ubuntu arm64 mirrors — add only if a real candidate exists
if apt-cache policy aircrack-ng 2>/dev/null | grep -q "Candidate: [^(none)]"; then
    PACKAGES+=(aircrack-ng)
else
    warn "aircrack-ng has no install candidate -- skipping (install manually if needed)"
fi

info "Updating package cache..."
log_cmd apt-get update -qq

info "Installing ${#PACKAGES[@]} packages..."
log_cmd apt-get install -y -qq -o Dpkg::Options::="--force-confold" "${PACKAGES[@]}"

info "System packages installed"

# ════════════════════════════════════════════════════════════════════════════
#  2. PI-SPECIFIC OPTIMIZATIONS
# ════════════════════════════════════════════════════════════════════════════
if [[ "$PLATFORM" == "opi_zero3" || "$PLATFORM" == "pi_zero" || "$PLATFORM" == "pi4" ]]; then
    step "Applying Pi/SBC optimizations..."

    # RPi-specific: reduce GPU memory
    if [[ "$PLATFORM" == "pi_zero" || "$PLATFORM" == "pi4" ]]; then
        if [[ -f /boot/config.txt ]]; then
            if ! grep -q "^gpu_mem=16" /boot/config.txt; then
                echo "gpu_mem=16" >> /boot/config.txt
                info "Set gpu_mem=16 in /boot/config.txt"
            fi
        elif [[ -f /boot/firmware/config.txt ]]; then
            if ! grep -q "^gpu_mem=16" /boot/firmware/config.txt; then
                echo "gpu_mem=16" >> /boot/firmware/config.txt
                info "Set gpu_mem=16 in /boot/firmware/config.txt"
            fi
        fi
    fi

    # Disable unnecessary services
    for svc in bluetooth avahi-daemon triggerhappy ModemManager; do
        if systemctl is-enabled "$svc" &>/dev/null 2>&1; then
            systemctl disable --now "$svc" 2>/dev/null || true
            info "Disabled $svc"
        fi
    done

    # Disable swap
    swapoff -a 2>/dev/null || true
    systemctl disable dphys-swapfile 2>/dev/null || true
    systemctl disable zram-config 2>/dev/null || true
    # Remove swap entries from fstab
    if grep -q "swap" /etc/fstab 2>/dev/null; then
        sed -i '/swap/s/^/#/' /etc/fstab
        info "Commented out swap entries in /etc/fstab"
    fi
    info "Swap disabled"

    # Kernel parameters
    SYSCTL_CONF="/etc/sysctl.d/99-bigbrother.conf"
    cat > "$SYSCTL_CONF" << 'SYSCTL'
# BigBrother network parameters
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1

# Performance tuning for packet capture
net.core.rmem_max=16777216
net.core.rmem_default=1048576
net.core.netdev_max_backlog=5000
SYSCTL
    sysctl --system >> "$LOG_FILE" 2>&1 || true
    info "Kernel parameters applied"
fi

# ════════════════════════════════════════════════════════════════════════════
#  3. DIRECTORY STRUCTURE
# ════════════════════════════════════════════════════════════════════════════
step "Creating directory structure..."

mkdir -p "${INSTALL_DIR}"/{core,modules/{passive,active,stealth,intel,connectivity},utils}
mkdir -p "${INSTALL_DIR}"/config/{caplets,mitmproxy_addons,templates}
mkdir -p "${INSTALL_DIR}"/data/{bpf_filters,wordlists,ids_rules}
mkdir -p "${INSTALL_DIR}"/storage/{pcaps,logs,baseline,intel,credentials,config,creds,dns_logs,sni_logs,audit,tool_logs}
mkdir -p "${INSTALL_DIR}"/{services,scripts/operator,templates/{captive_portals,dns_zones,hostapd,responder,lkm},tests}

chmod 700 "${INSTALL_DIR}/storage"
chmod 700 "${INSTALL_DIR}/storage"/*

# Tools directory
mkdir -p "${TOOLS_DIR}"

info "Directory structure created"

# ════════════════════════════════════════════════════════════════════════════
#  4. PYTHON VIRTUAL ENVIRONMENT
# ════════════════════════════════════════════════════════════════════════════
step "Setting up Python virtual environment..."

if [[ ! -d "${VENV_DIR}" ]] || [[ $FLAG_REINSTALL -eq 1 ]]; then
    rm -rf "${VENV_DIR}"
    python3 -m venv "${VENV_DIR}"
    info "Created venv at ${VENV_DIR}"
else
    info "Venv already exists at ${VENV_DIR}"
fi

# Upgrade pip
"${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel >> "$LOG_FILE" 2>&1

# Install requirements from source tree if available, else from install dir
REQ_FILE=""
if [[ -f "${SCRIPT_DIR}/requirements.txt" ]]; then
    REQ_FILE="${SCRIPT_DIR}/requirements.txt"
elif [[ -f "${INSTALL_DIR}/requirements.txt" ]]; then
    REQ_FILE="${INSTALL_DIR}/requirements.txt"
fi

if [[ -n "$REQ_FILE" ]]; then
    info "Installing Python requirements from ${REQ_FILE}..."
    "${VENV_DIR}/bin/pip" install -r "$REQ_FILE" >> "$LOG_FILE" 2>&1
fi

# Additional pip packages (tools installed into venv)
VENV_PIP_PACKAGES=(
    impacket
    certipy-ad
    bloodhound
    pyjwt
    graphviz
    numpy
)

# Jumpbox mode: skip heavy packages
if [[ $FLAG_JUMPBOX -eq 0 ]]; then
    VENV_PIP_PACKAGES+=(
        mitmproxy
        mitm6
        coercer
        pypykatz
        ldapdomaindump
    )
fi

info "Installing additional Python packages..."
for pkg in "${VENV_PIP_PACKAGES[@]}"; do
    if "${VENV_DIR}/bin/pip" show "$pkg" &>/dev/null && [[ $FLAG_REINSTALL -eq 0 ]]; then
        debug "  $pkg already installed"
    else
        info "  Installing $pkg..."
        "${VENV_DIR}/bin/pip" install "$pkg" >> "$LOG_FILE" 2>&1 || warn "  Failed to install $pkg (non-fatal)"
    fi
done

info "Python environment ready"

# ════════════════════════════════════════════════════════════════════════════
#  5. INSTALL BETTERCAP
# ════════════════════════════════════════════════════════════════════════════
step "Installing bettercap..."

install_bettercap() {
    if command -v bettercap &>/dev/null && [[ $FLAG_REINSTALL -eq 0 ]]; then
        info "bettercap already installed: $(bettercap -version 2>/dev/null || echo 'unknown version')"
        return 0
    fi

    info "Downloading bettercap binary..."

    local bc_arch
    case "$ARCH" in
        arm64)  bc_arch="arm64" ;;
        amd64)  bc_arch="amd64" ;;
        armhf)  bc_arch="armhf" ;;
        *)      warn "Unknown arch $ARCH for bettercap, trying amd64"; bc_arch="amd64" ;;
    esac

    # Get latest release URL from GitHub
    local release_url
    release_url=$(curl -fsSL "https://api.github.com/repos/bettercap/bettercap/releases/latest" \
        | jq -r ".assets[] | select(.name | test(\"linux_${bc_arch}\")) | .browser_download_url" \
        | head -1)

    if [[ -z "$release_url" || "$release_url" == "null" ]]; then
        # Fallback: try to install from apt
        if apt-cache show bettercap &>/dev/null 2>&1; then
            log_cmd apt-get install -y -qq bettercap
            info "bettercap installed from apt"
            return 0
        fi
        warn "Could not find bettercap release for ${bc_arch} -- manual install required"
        return 1
    fi

    local tmp_dir
    tmp_dir=$(mktemp -d)
    trap "rm -rf '$tmp_dir'" RETURN

    curl -fsSL "$release_url" -o "${tmp_dir}/bettercap.zip"

    # Extract
    if command -v unzip &>/dev/null; then
        unzip -o "${tmp_dir}/bettercap.zip" -d "${tmp_dir}/bc" >> "$LOG_FILE" 2>&1
    else
        apt-get install -y -qq unzip >> "$LOG_FILE" 2>&1
        unzip -o "${tmp_dir}/bettercap.zip" -d "${tmp_dir}/bc" >> "$LOG_FILE" 2>&1
    fi

    # Find and install the binary
    local bc_bin
    bc_bin=$(find "${tmp_dir}/bc" -name "bettercap" -type f | head -1)
    if [[ -z "$bc_bin" ]]; then
        warn "Could not find bettercap binary in archive"
        return 1
    fi

    cp "$bc_bin" /usr/local/bin/bettercap
    chmod 755 /usr/local/bin/bettercap
    setcap cap_net_raw,cap_net_admin=eip /usr/local/bin/bettercap 2>/dev/null || true

    info "bettercap installed to /usr/local/bin/bettercap"
    trap - RETURN
    rm -rf "$tmp_dir"
}

if [[ $FLAG_JUMPBOX -eq 0 ]]; then
    install_bettercap || warn "bettercap installation failed (non-fatal)"
else
    info "Skipping bettercap (jumpbox mode)"
fi

# ════════════════════════════════════════════════════════════════════════════
#  6. INSTALL TOOLS FROM GITHUB
# ════════════════════════════════════════════════════════════════════════════
if [[ $FLAG_NO_TOOLS -eq 0 ]]; then
    step "Installing external tools..."

    clone_or_pull() {
        local repo_url="$1"
        local dest="$2"
        local name
        name=$(basename "$dest")

        if [[ -d "$dest/.git" ]] && [[ $FLAG_REINSTALL -eq 0 ]]; then
            debug "  $name already cloned, pulling updates..."
            git -C "$dest" pull --ff-only >> "$LOG_FILE" 2>&1 || true
        else
            rm -rf "$dest"
            info "  Cloning $name..."
            git clone --depth 1 "$repo_url" "$dest" >> "$LOG_FILE" 2>&1 || {
                warn "  Failed to clone $name"
                return 1
            }
        fi
    }

    download_release_binary() {
        local repo="$1"        # e.g. "jpillora/chisel"
        local bin_name="$2"    # e.g. "chisel"
        local dest="$3"        # e.g. "/usr/local/bin/chisel"
        local arch_pattern="$4"  # e.g. "linux_arm64"

        if [[ -f "$dest" ]] && [[ $FLAG_REINSTALL -eq 0 ]]; then
            debug "  $bin_name already installed at $dest"
            return 0
        fi

        info "  Downloading $bin_name..."

        local release_url
        release_url=$(curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" \
            | jq -r ".assets[] | select(.name | test(\"${arch_pattern}\")) | .browser_download_url" \
            | head -1)

        if [[ -z "$release_url" || "$release_url" == "null" ]]; then
            warn "  Could not find release for $bin_name (${arch_pattern})"
            return 1
        fi

        local tmp_file
        tmp_file=$(mktemp)

        curl -fsSL "$release_url" -o "$tmp_file"

        # Detect archive type and extract
        local file_type
        file_type=$(file -b "$tmp_file")

        if echo "$file_type" | grep -qi "gzip"; then
            if echo "$release_url" | grep -qi "\.tar\.gz"; then
                local tmp_dir
                tmp_dir=$(mktemp -d)
                tar xzf "$tmp_file" -C "$tmp_dir" 2>/dev/null
                local found_bin
                found_bin=$(find "$tmp_dir" -name "$bin_name" -type f | head -1)
                if [[ -n "$found_bin" ]]; then
                    cp "$found_bin" "$dest"
                else
                    # Try the first executable file
                    found_bin=$(find "$tmp_dir" -type f -executable | head -1)
                    if [[ -n "$found_bin" ]]; then
                        cp "$found_bin" "$dest"
                    else
                        warn "  Could not find $bin_name in archive"
                        rm -rf "$tmp_dir" "$tmp_file"
                        return 1
                    fi
                fi
                rm -rf "$tmp_dir"
            else
                gunzip -c "$tmp_file" > "$dest" 2>/dev/null || cp "$tmp_file" "$dest"
            fi
        elif echo "$file_type" | grep -qi "zip"; then
            local tmp_dir
            tmp_dir=$(mktemp -d)
            unzip -o "$tmp_file" -d "$tmp_dir" >> "$LOG_FILE" 2>&1
            local found_bin
            found_bin=$(find "$tmp_dir" -name "$bin_name" -type f | head -1)
            if [[ -n "$found_bin" ]]; then
                cp "$found_bin" "$dest"
            fi
            rm -rf "$tmp_dir"
        else
            # Assume raw binary
            cp "$tmp_file" "$dest"
        fi

        chmod 755 "$dest"
        rm -f "$tmp_file"
        info "  $bin_name installed to $dest"
    }

    # Determine arch pattern for downloads
    case "$ARCH" in
        arm64)  DL_ARCH="linux.*arm64\|linux.*aarch64" ;;
        amd64)  DL_ARCH="linux.*amd64\|linux.*x86_64" ;;
        armhf)  DL_ARCH="linux.*arm\|linux.*armv" ;;
        *)      DL_ARCH="linux.*amd64" ;;
    esac

    # ── Git clone tools ─────────────────────────────────────────────────
    clone_or_pull "https://github.com/lgandx/Responder" "${TOOLS_DIR}/Responder"
    clone_or_pull "https://github.com/cddmp/enum4linux-ng" "${TOOLS_DIR}/enum4linux-ng"
    clone_or_pull "https://github.com/ShawnDEvans/smbmap" "${TOOLS_DIR}/smbmap"

    # ── Binary release tools ────────────────────────────────────────────
    # Chisel
    case "$ARCH" in
        arm64)  download_release_binary "jpillora/chisel" "chisel" "/usr/local/bin/chisel" "linux_arm64" || warn "  Chisel download failed -- install manually" ;;
        amd64)  download_release_binary "jpillora/chisel" "chisel" "/usr/local/bin/chisel" "linux_amd64" || warn "  Chisel download failed -- install manually" ;;
        *)      warn "  Chisel: unsupported arch $ARCH" ;;
    esac

    # Ligolo-ng (agent binary)
    case "$ARCH" in
        arm64)  download_release_binary "nicocha30/ligolo-ng" "ligolo-agent" "/usr/local/bin/ligolo-agent" "agent.*linux_arm64" || warn "  Ligolo-ng download failed -- install manually" ;;
        amd64)  download_release_binary "nicocha30/ligolo-ng" "ligolo-agent" "/usr/local/bin/ligolo-agent" "agent.*linux_amd64" || warn "  Ligolo-ng download failed -- install manually" ;;
        *)      warn "  Ligolo-ng: unsupported arch $ARCH" ;;
    esac

    # Kerbrute (no arm64 binary — build from source)
    case "$ARCH" in
        arm64)
            export HOME=/root GOPATH=/root/go GOMODCACHE=/root/go/pkg/mod
            mkdir -p /root/go
            if command -v go &>/dev/null; then
                log_cmd go install github.com/ropnop/kerbrute@latest
                cp "${GOPATH}/bin/kerbrute" /usr/local/bin/kerbrute 2>/dev/null || true
            else
                GOTAR=$(mktemp -d)
                GOVERSION="1.22.3"
                curl -fsSL "https://go.dev/dl/go${GOVERSION}.linux-arm64.tar.gz" -o "$GOTAR/go.tar.gz"
                tar -C /usr/local -xzf "$GOTAR/go.tar.gz"
                export PATH=$PATH:/usr/local/go/bin
                log_cmd /usr/local/go/bin/go install github.com/ropnop/kerbrute@latest
                cp "${GOPATH}/bin/kerbrute" /usr/local/bin/kerbrute 2>/dev/null || true
                rm -rf "$GOTAR"
            fi
            ;;
        amd64)  download_release_binary "ropnop/kerbrute" "kerbrute" "/usr/local/bin/kerbrute" "linux_amd64" ;;
        *)      warn "  Kerbrute: unsupported arch $ARCH" ;;
    esac

    # ── NetExec (pip or git) ────────────────────────────────────────────
    if ! "${VENV_DIR}/bin/pip" show netexec &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then
        info "  Installing NetExec via pip..."
        "${VENV_DIR}/bin/pip" install netexec >> "$LOG_FILE" 2>&1 || {
            warn "  NetExec pip install failed, trying git clone..."
            clone_or_pull "https://github.com/Pennyw0rth/NetExec" "${TOOLS_DIR}/NetExec"
        }
    else
        debug "  NetExec already installed"
    fi

    # ── Evil-WinRM (gem or skip) ────────────────────────────────────────
    if command -v gem &>/dev/null; then
        if ! command -v evil-winrm &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then
            info "  Installing evil-winrm..."
            gem install evil-winrm >> "$LOG_FILE" 2>&1 || warn "  evil-winrm install failed"
        fi
    else
        debug "  Ruby not available, skipping evil-winrm"
    fi

    # ── OPi Zero 3 / Debian host only tools ─────────────────────────────
    if [[ $FLAG_JUMPBOX -eq 0 ]]; then
        # ROADtools (Azure/Entra recon)
        if ! "${VENV_DIR}/bin/pip" show roadlib &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then
            info "  Installing ROADtools..."
            "${VENV_DIR}/bin/pip" install roadlib roadrecon >> "$LOG_FILE" 2>&1 || warn "  ROADtools install failed"
        fi

        # PetitPotam
        clone_or_pull "https://github.com/topotam/PetitPotam" "${TOOLS_DIR}/PetitPotam"
    fi

    info "External tools installation complete"
else
    info "Skipping external tool installation (--no-tools)"
fi

# ════════════════════════════════════════════════════════════════════════════
#  7. CREATE DATA DATABASES
# ════════════════════════════════════════════════════════════════════════════
step "Building data databases..."

BUILD_DATA="${SCRIPT_DIR}/scripts/build_data.py"
if [[ ! -f "$BUILD_DATA" ]]; then
    BUILD_DATA="${INSTALL_DIR}/scripts/build_data.py"
fi

if [[ -f "$BUILD_DATA" ]]; then
    BUILD_DATA_FLAGS="--output-dir ${INSTALL_DIR}/data"
    [[ $FLAG_REINSTALL -eq 1 ]] && BUILD_DATA_FLAGS="$BUILD_DATA_FLAGS --force"
    "${VENV_DIR}/bin/python3" "$BUILD_DATA" $BUILD_DATA_FLAGS >> "$LOG_FILE" 2>&1
    info "Data databases created"
else
    warn "build_data.py not found -- databases will need to be created manually"
fi

# ════════════════════════════════════════════════════════════════════════════
#  8. INSTALL SYSTEMD SERVICES
# ════════════════════════════════════════════════════════════════════════════
step "Installing systemd services..."

install_service() {
    local svc_file="$1"
    local svc_name
    svc_name=$(basename "$svc_file")

    if [[ -f "$svc_file" ]]; then
        cp "$svc_file" "/etc/systemd/system/${svc_name}"
        info "  Installed $svc_name (not enabled)"
    fi
}

# Look for service files in source tree first, then install dir
SVC_DIR="${SCRIPT_DIR}/services"
if [[ ! -d "$SVC_DIR" ]]; then
    SVC_DIR="${INSTALL_DIR}/services"
fi

if [[ -d "$SVC_DIR" ]]; then
    for svc in "${SVC_DIR}"/*.service "${SVC_DIR}"/*.timer; do
        [[ -f "$svc" ]] && install_service "$svc"
    done
    systemctl daemon-reload
    info "Systemd services installed (none enabled by default)"
else
    debug "No service files found in $SVC_DIR"
fi

# ════════════════════════════════════════════════════════════════════════════
#  9. COPY PROJECT FILES
# ════════════════════════════════════════════════════════════════════════════
step "Deploying project files..."

if [[ "$SCRIPT_DIR" != "$INSTALL_DIR" ]]; then
    # Copy from source tree to install directory
    # rsync preferred for idempotency; fall back to cp
    if command -v rsync &>/dev/null; then
        rsync -a --exclude='.git' --exclude='.venv' --exclude='storage/' \
              --exclude='__pycache__' --exclude='*.pyc' \
              "${SCRIPT_DIR}/" "${INSTALL_DIR}/"
        info "Project files synced to ${INSTALL_DIR}"
    else
        cp -a "${SCRIPT_DIR}"/*.py "${INSTALL_DIR}/" 2>/dev/null || true
        for subdir in core modules utils config data services scripts templates tests; do
            if [[ -d "${SCRIPT_DIR}/${subdir}" ]]; then
                cp -a "${SCRIPT_DIR}/${subdir}" "${INSTALL_DIR}/"
            fi
        done
        info "Project files copied to ${INSTALL_DIR}"
    fi
else
    info "Already running from install directory"
fi

# ════════════════════════════════════════════════════════════════════════════
#  10. SET PERMISSIONS
# ════════════════════════════════════════════════════════════════════════════
step "Setting permissions..."

# Storage: restrictive
chmod 700 "${INSTALL_DIR}/storage"
find "${INSTALL_DIR}/storage" -mindepth 1 -type d -exec chmod 700 {} \; 2>/dev/null || true

# Config files: restrictive
find "${INSTALL_DIR}/config" -type f -exec chmod 600 {} \; 2>/dev/null || true

# Scripts: executable
find "${INSTALL_DIR}/scripts" -name "*.sh" -exec chmod 755 {} \; 2>/dev/null || true
find "${INSTALL_DIR}/scripts" -name "*.py" -exec chmod 755 {} \; 2>/dev/null || true

# Python source: read-only (owner rw)
find "${INSTALL_DIR}" -name "*.py" -not -path "*/.venv/*" -exec chmod 644 {} \; 2>/dev/null || true

# Main CLI executable
[[ -f "${INSTALL_DIR}/bigbrother.py" ]] && chmod 755 "${INSTALL_DIR}/bigbrother.py"

# Installed binaries
for bin in /usr/local/bin/bettercap /usr/local/bin/chisel /usr/local/bin/ligolo-agent /usr/local/bin/kerbrute; do
    [[ -f "$bin" ]] && chmod 755 "$bin"
done

info "Permissions set"

# ════════════════════════════════════════════════════════════════════════════
#  11. FINAL VERIFICATION
# ════════════════════════════════════════════════════════════════════════════
step "Running final verification..."

VERIFY_PASS=0
VERIFY_FAIL=0

check() {
    if "$@" &>/dev/null 2>&1; then
        ((VERIFY_PASS++)) || true
    else
        warn "  FAIL: $*"
        ((VERIFY_FAIL++)) || true
    fi
}

# Core binaries
check command -v python3
check command -v tcpdump
check command -v nmap
check command -v sqlite3
check command -v macchanger
check command -v zstd
check command -v tmux

# Python venv
check test -f "${VENV_DIR}/bin/python3"
check "${VENV_DIR}/bin/python3" -c "import scapy"
check "${VENV_DIR}/bin/python3" -c "import click"
check "${VENV_DIR}/bin/python3" -c "import rich"
check "${VENV_DIR}/bin/python3" -c "import cryptography"
check "${VENV_DIR}/bin/python3" -c "import yaml"

# Directories
check test -d "${INSTALL_DIR}/core"
check test -d "${INSTALL_DIR}/modules"
check test -d "${INSTALL_DIR}/storage"
check test -d "${INSTALL_DIR}/data"

# Databases
for db in innocuous_macs.db oui.db os_sigs.db dhcp_fingerprints.db ja3_fingerprints.db; do
    check test -f "${INSTALL_DIR}/data/${db}"
done

echo ""
info "Verification: ${VERIFY_PASS} passed, ${VERIFY_FAIL} failed"

if [[ $VERIFY_FAIL -gt 0 ]]; then
    warn "Some checks failed -- review $LOG_FILE for details"
else
    info "Setup complete"
fi

echo ""
step "BigBrother deployment summary"
echo "  Platform:    ${PLATFORM} (${ARCH})"
echo "  Install dir: ${INSTALL_DIR}"
echo "  Venv:        ${VENV_DIR}"
echo "  Mode:        $(if [[ $FLAG_JUMPBOX -eq 1 ]]; then echo 'jumpbox (minimal)'; else echo 'full'; fi)"
echo "  Log:         ${LOG_FILE}"
echo ""
info "Run '${INSTALL_DIR}/bigbrother.py' to start"

# ════════════════════════════════════════════════════════════════════════════
#  12. ENABLE AND START SERVICE
# ════════════════════════════════════════════════════════════════════════════
# ════════════════════════════════════════════════════════════════════════════
#  12. NET ALERTER CRON
# ════════════════════════════════════════════════════════════════════════════
step "Configuring net_alerter..."

BB_CONFIG="/root/bb-config.env"
if [[ -f "$BB_CONFIG" ]]; then
    # shellcheck source=/dev/null
    source "$BB_CONFIG"
fi

if [[ "${BB_ALERTER_TYPE:-}" == "matrix" && -n "${BB_ALERTER_MATRIX_TOKEN:-}" ]]; then
    mkdir -p /opt/net_alerter
    cp "${INSTALL_DIR}/net_alerter/net_alerter.py" /opt/net_alerter/net_alerter.py

    cat > /opt/net_alerter/.env <<ALERTENV
MATRIX_HOMESERVER=${BB_ALERTER_MATRIX_HS}
MATRIX_ACCESS_TOKEN=${BB_ALERTER_MATRIX_TOKEN}
MATRIX_ROOM_ID=${BB_ALERTER_MATRIX_ROOM}
NET_ALERTER_DB=/opt/net_alerter/devices.db
NET_ALERTER_INFRA_IPS=${BB_ALERTER_INFRA_IPS:-}
ALERTENV
    chmod 600 /opt/net_alerter/.env

    cat > /opt/net_alerter/run.sh <<'WRAPPER'
#!/usr/bin/env bash
set -a; source /opt/net_alerter/.env; set +a
exec python3 /opt/net_alerter/net_alerter.py
WRAPPER
    chmod +x /opt/net_alerter/run.sh

    CRON="*/5 * * * * /opt/net_alerter/run.sh >> /opt/net_alerter/net_alerter.log 2>&1"
    (crontab -l 2>/dev/null | grep -v net_alerter; echo "$CRON") | crontab -

    info "net_alerter installed — scanning every 5 min, Matrix alerts on join/leave/rejoin"
    info "Seeding baseline (silent first run)..."
    bash /opt/net_alerter/run.sh >> /opt/net_alerter/net_alerter.log 2>&1 || warn "Baseline scan failed — will retry at next cron tick"
else
    debug "No Matrix alerter configured — skipping net_alerter setup"
fi

# ════════════════════════════════════════════════════════════════════════════
#  13. ENABLE AND START SERVICE
# ════════════════════════════════════════════════════════════════════════════
if [[ $FLAG_ENABLE_SVC -eq 1 ]]; then
    step "Enabling and starting BigBrother services..."
    for svc in bigbrother-core.service bigbrother-capture.service bigbrother-watchdog.service bettercap.service; do
        if systemctl enable "$svc" 2>/dev/null; then
            info "  $svc enabled"
        else
            warn "  Failed to enable $svc (may not be installed)"
        fi
    done
    systemctl daemon-reload
    if systemctl start bigbrother-core.service 2>/dev/null; then
        info "bigbrother-core.service started"
        sleep 3
        systemctl status bigbrother-core.service --no-pager -l 2>/dev/null || true
    else
        warn "Failed to start bigbrother-core.service — check journalctl -u bigbrother-core"
    fi
fi
