#!/usr/bin/env bash
# ============================================================
# Joey BBRv3 Kernel Build Script - L2TP + VPN Enhanced
# Based on byJoey/Actions-bbr-v3
# ============================================================
set -euo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
JOEY_REPO="$SCRIPT_DIR/joey-bbr-kernel"
WORKDIR="$SCRIPT_DIR/kernel-build"
KERNEL_VERSION="${KERNEL_VERSION:-7.1.8}"
ARCH="${ARCH:-x86_64}"
BUILD_MAX="${BUILD_MAX:-false}"

case "$ARCH" in
    x86_64)   KERNEL_ARCH="x86_64"  ; DEB_ARCH="amd64"  ;;
    arm64|aarch64) KERNEL_ARCH="arm64" ; DEB_ARCH="arm64" ;;
    *) echo "Unsupported ARCH: $ARCH"; exit 1 ;;
esac

BRANCH=$(echo "$KERNEL_VERSION" | grep -oE '^[0-9]+\.[0-9]+')
PATCH_FILE="$JOEY_REPO/patches/bbrv3-linux-$BRANCH.patch"

if [ "$BUILD_MAX" = "true" ]; then
    LOCALVERSION="-joeyblog-bbrv3-max"
    PROFILE_NAME="BBRv3 Max"
else
    LOCALVERSION="-joeyblog-bbrv3"
    PROFILE_NAME="BBRv3"
fi

echo "============================================"
echo " Joey BBRv3 Kernel Builder"
echo " Version: $KERNEL_VERSION ($BRANCH)"
echo " Architecture: $KERNEL_ARCH ($DEB_ARCH)"
echo " Profile: $PROFILE_NAME"
echo " L2TP/VPN: ENABLED"
echo "============================================"

# Step 1: Clone kernel source
mkdir -p "$WORKDIR"
KERNEL_SRC="$WORKDIR/linux-$KERNEL_VERSION"
if [ ! -d "$KERNEL_SRC/.git" ]; then
    echo "[1/8] Cloning kernel source..."
    git clone --depth=1 --branch "linux-$BRANCH.y" \
        https://github.com/gregkh/linux.git "$KERNEL_SRC"
else
    echo "[1/8] Updating kernel source..."
    cd "$KERNEL_SRC"
    git fetch --depth=1 origin "linux-$BRANCH.y"
    git reset --hard FETCH_HEAD
    git clean -fdx
fi
cd "$KERNEL_SRC"
echo "  Kernel source ready."

echo "[2/8] Applying BBRv3 patch..."
if [ ! -f "$PATCH_FILE" ]; then
    echo "ERROR: patch not found: $PATCH_FILE"
    ls "$JOEY_REPO/patches/"
    exit 1
fi
grep -rl "MODULE_DESCRIPTION" net/ipv4/ 2>/dev/null | while read f; do
    sed -i '/MODULE_DESCRIPTION/d' "$f"
done || true
if git apply --check "$PATCH_FILE" 2>/dev/null; then
    git apply "$PATCH_FILE"
    echo "  BBRv3 patch applied cleanly."
else
    echo "  Trying fuzzy matching..."
    patch -p1 --forward --fuzz=3 < "$PATCH_FILE" 2>/dev/null || {
        echo "ERROR: patch failed"; exit 1; }
    echo "  BBRv3 patch applied with fuzz."
fi
grep -q '^#define BBR_VERSION[[:space:]]*3' net/ipv4/tcp_bbr.c || {
    echo "ERROR: BBR_VERSION=3 not found!"; exit 1; }
echo "  BBRv3 verified."

if [ "$BUILD_MAX" = "true" ]; then
    echo "[3/8] Applying BBRv3 Max profile..."
    bash "$JOEY_REPO/scripts/apply-bbrv3-max-profile.sh"
    sed -i '/MODULE_DESCRIPTION/d' net/ipv4/tcp_bbr.c
    echo 'MODULE_DESCRIPTION("TCP BBR v3 Max by Joey");' >> net/ipv4/tcp_bbr.c
else
    echo "[3/8] Standard BBRv3 profile."
fi

echo "[4/8] Preparing kernel config with L2TP+VPN..."
case "$KERNEL_ARCH" in
    arm64)  cp "$JOEY_REPO/arm64.config" .config ;;
    x86_64) cp "$JOEY_REPO/x86-64.config" .config ;;
esac

# kconfig helper
kc() { scripts/config "$@"; }

# === Joey base policy ===
kc --disable SYSTEM_TRUSTED_KEYS
kc --disable SYSTEM_REVOCATION_KEYS
kc --enable TCP_CONG_BBR
kc --disable DEFAULT_CUBIC
kc --enable DEFAULT_BBR
kc --set-str DEFAULT_TCP_CONG bbr
kc --enable NET_SCH_DEFAULT
kc --enable NET_SCH_FQ
kc --module NET_SCH_FQ_CODEL
kc --module NET_SCH_PIE
kc --module NET_SCH_FQ_PIE
kc --module NET_SCH_CAKE
kc --disable DEFAULT_FQ_CODEL
kc --disable DEFAULT_PFIFO_FAST
kc --enable DEFAULT_FQ
kc --set-str DEFAULT_NET_SCH fq
kc --module NETFILTER_XTABLES
kc --enable NETFILTER_XTABLES_LEGACY
kc --module IP_NF_IPTABLES_LEGACY
kc --module IP_NF_FILTER
kc --module IP_NF_NAT
kc --module IP_NF_TARGET_MASQUERADE
kc --module IP_NF_MANGLE
kc --module IP_NF_RAW
kc --module IP6_NF_IPTABLES_LEGACY
kc --module IP6_NF_FILTER
kc --module IP6_NF_NAT
kc --module IP6_NF_MANGLE
kc --module IP6_NF_RAW
kc --disable DEBUG_INFO
kc --enable DEBUG_INFO_NONE
kc --disable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
kc --disable DEBUG_INFO_DWARF4
kc --disable DEBUG_INFO_DWARF5
kc --disable DEBUG_INFO_REDUCED
kc --disable DEBUG_INFO_COMPRESSED
kc --disable DEBUG_INFO_SPLIT
kc --disable DEBUG_INFO_BTF
kc --disable DEBUG_INFO_BTF_MODULES
kc --disable MODULE_ALLOW_BTF_MISMATCH
kc --disable AFS_FS
kc --disable AF_RXRPC
kc --disable RXKAD

echo "  Enabling L2TP + VPN..."

# XFRM/IPsec (was disabled; re-enable as module)
kc --module XFRM_USER
kc --module XFRM_ALGO
kc --module XFRM_ESP
kc --module INET_ESP
kc --module INET6_ESP
kc --module XFRM_IPCOMP
kc --module INET_XFRM_MODE_TRANSPORT
kc --module INET_XFRM_MODE_TUNNEL
kc --module INET_XFRM_MODE_BEET
kc --module INET6_XFRM_MODE_TRANSPORT
kc --module INET6_XFRM_MODE_TUNNEL
kc --module INET6_XFRM_MODE_BEET
kc --module XFRM_STATISTICS
kc --module INET_AH
kc --module INET6_AH

# L2TP
kc --module L2TP
kc --module L2TP_DEBUGFS
kc --module L2TP_V3
kc --module L2TP_IP
kc --module L2TP_ETH

# PPP
kc --module PPP
kc --module PPP_ASYNC
kc --module PPP_SYNC_TTY
kc --module PPP_DEFLATE
kc --module PPP_BSDCOMP
kc --module PPP_MPPE
kc --module PPPOE
kc --module PPTP
kc --module PPPOL2TP
kc --module PPPOLAC
kc --module PPPOPNS
kc --module PPP_MULTILINK

# TUN/WireGuard
kc --module TUN
kc --module TAP
kc --module WIREGUARD
kc --enable WIREGUARD_DEBUG

# GRE/IP tunnels
kc --module NET_IPGRE_DEMUX
kc --module NET_IPGRE
kc --module NET_IPGRE_BROADCAST
kc --module NET_IPIP
kc --module IPV6_TUNNEL
kc --module IPV6_GRE
kc --module NET_FOU
kc --module NET_FOU_IP_TUNNELS
kc --module GENEVE
kc --module VXLAN

# IPsec key management
kc --module NET_KEY
kc --module NET_KEY_MIGRATE

# Crypto for VPN
kc --module CRYPTO_CBC
kc --module CRYPTO_CTR
kc --module CRYPTO_ECB
kc --module CRYPTO_SHA256
kc --module CRYPTO_SHA512
kc --module CRYPTO_AES
kc --module CRYPTO_DES
kc --module CRYPTO_MD5
kc --module CRYPTO_HMAC
kc --module CRYPTO_ARC4
kc --module CRYPTO_SHA1
kc --module CRYPTO_AEAD
kc --module CRYPTO_GCM
kc --module CRYPTO_AUTHENC

# Bridge/VLAN
kc --module BRIDGE
kc --module BRIDGE_NETFILTER
kc --module BRIDGE_VLAN_FILTERING
kc --module VLAN_8021Q

# Extra schedulers for traffic shaping
kc --module NET_SCH_MULTIQ
kc --module NET_SCH_HTB
kc --module NET_SCH_HFSC
kc --module NET_SCH_PRIO
kc --module NET_SCH_SFB
kc --module NET_SCH_QFQ
kc --module NET_SCH_CODEL

# Conntrack for NAT/VPN
kc --module NF_CONNTRACK
kc --module NF_CONNTRACK_FTP
kc --module NF_CONNTRACK_SIP
kc --module NF_CONNTRACK_PPTP
kc --module NF_CONNTRACK_H323
kc --module NF_NAT
kc --module NF_NAT_FTP
kc --module NF_NAT_SIP
kc --module NF_NAT_PPTP
kc --module NF_NAT_H323
kc --module IP_NF_TARGET_REDIRECT
kc --module NETFILTER_XT_MATCH_CONNTRACK
kc --module NETFILTER_XT_MATCH_STATE
kc --module NETFILTER_XT_MATCH_POLICY
kc --module NETFILTER_XT_MATCH_MULTIPORT
kc --module NETFILTER_XT_MATCH_IPRANGE
kc --module NETFILTER_XT_MATCH_HASHLIMIT
kc --module NETFILTER_XT_MATCH_LIMIT
kc --module NETFILTER_XT_MATCH_TCPMSS
kc --module NETFILTER_XT_MATCH_U32
kc --module NETFILTER_XT_TARGET_LOG
kc --module NETFILTER_XT_TARGET_TCPMSS
kc --module NETFILTER_XT_MATCH_OWNER

# MPLS
kc --module MPLS
kc --module MPLS_ROUTING
kc --module MPLS_IPTUNNEL

echo "  Running olddefconfig (pass 1)..."
if [ "$KERNEL_ARCH" = "arm64" ]; then
    timeout 300 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig < /dev/null
else
    timeout 300 make olddefconfig < /dev/null
fi

echo "  Re-applying policy..."
kc --enable TCP_CONG_BBR
kc --disable DEFAULT_CUBIC
kc --enable DEFAULT_BBR
kc --set-str DEFAULT_TCP_CONG bbr
kc --enable NET_SCH_DEFAULT
kc --enable NET_SCH_FQ
kc --disable DEFAULT_FQ_CODEL
kc --disable DEFAULT_PFIFO_FAST
kc --enable DEFAULT_FQ
kc --set-str DEFAULT_NET_SCH fq
kc --disable DEBUG_INFO
kc --enable DEBUG_INFO_NONE
kc --disable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
kc --disable DEBUG_INFO_DWARF4
kc --disable DEBUG_INFO_DWARF5
kc --disable DEBUG_INFO_REDUCED
kc --disable DEBUG_INFO_COMPRESSED
kc --disable DEBUG_INFO_SPLIT
kc --disable DEBUG_INFO_BTF
kc --disable DEBUG_INFO_BTF_MODULES
kc --disable MODULE_ALLOW_BTF_MISMATCH
kc --module L2TP && kc --module PPPOL2TP && kc --module PPTP && kc --module PPPOE
kc --module PPP_MPPE && kc --module WIREGUARD && kc --module TUN
kc --module XFRM_USER && kc --module XFRM_ESP
kc --module INET_ESP && kc --module INET6_ESP
kc --module NET_KEY

echo "  Running olddefconfig (pass 2)..."
if [ "$KERNEL_ARCH" = "arm64" ]; then
    timeout 300 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig < /dev/null
else
    timeout 300 make olddefconfig < /dev/null
fi

echo "[5/8] Validating config..."
v() { if grep -q "$1" .config; then echo "  [OK] $2"; else echo "  [WARN] $2"; fi; }
echo "  --- BBR ---"
v 'CONFIG_TCP_CONG_BBR=y' 'BBR built-in'
v 'CONFIG_DEFAULT_BBR=y' 'BBR default'
v 'CONFIG_DEFAULT_TCP_CONG="bbr"' 'TCP cong = bbr'
echo "  --- Schedulers ---"
v 'CONFIG_NET_SCH_FQ=y' 'FQ built-in'
v 'CONFIG_NET_SCH_CAKE=m' 'CAKE module'
v 'CONFIG_DEFAULT_FQ=y' 'Default FQ'
echo "  --- L2TP/VPN ---"
v 'CONFIG_L2TP=m' 'L2TP'
v 'CONFIG_L2TP_IP=m' 'L2TP IP'
v 'CONFIG_L2TP_ETH=m' 'L2TP ETH'
v 'CONFIG_PPP=m' 'PPP'
v 'CONFIG_PPPOL2TP=m' 'PPPoL2TP'
v 'CONFIG_PPTP=m' 'PPTP'
v 'CONFIG_PPPOE=m' 'PPPoE'
v 'CONFIG_PPP_MPPE=m' 'MPPE enc'
v 'CONFIG_WIREGUARD=m' 'WireGuard'
v 'CONFIG_TUN=m' 'TUN/TAP'
v 'CONFIG_XFRM_ESP=m' 'XFRM ESP'
v 'CONFIG_INET_ESP=m' 'IPv4 ESP'
v 'CONFIG_INET6_ESP=m' 'IPv6 ESP'
v 'CONFIG_NET_KEY=m' 'NET_KEY'

mkdir -p "$WORKDIR/build-configs"
cp .config "$WORKDIR/build-configs/${KERNEL_ARCH}-${KERNEL_VERSION}.config"
cp .config "$WORKDIR/build-configs/${KERNEL_ARCH}.config"

echo "[6/8] Checking for debug..."
grep -q 'CONFIG_DEBUG_INFO=y' .config 2>/dev/null && {
    echo "ERROR: DEBUG_INFO enabled!"; exit 1; }
echo "  No debug info - OK"

echo "[7/8] Building kernel..."
if [ "$KERNEL_ARCH" = "arm64" ]; then
    BUILD_CMD="make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bindeb-pkg -j$(nproc) LOCALVERSION=$LOCALVERSION KDEB_COMPRESS=gzip skipdbg=true"
else
    BUILD_CMD="make bindeb-pkg -j$(nproc) LOCALVERSION=$LOCALVERSION KDEB_COMPRESS=gzip skipdbg=true"
fi
echo "  $BUILD_CMD"
eval "$BUILD_CMD" 2>&1 | tee "$WORKDIR/build-${KERNEL_ARCH}.log"

echo "[8/8] Verifying..."
DCNT=$(find "$WORKDIR" -maxdepth 2 \( -name '*-dbg*.deb' -o -name '*-dbgsym*.deb' \) 2>/dev/null | wc -l)
[ "$DCNT" -gt 0 ] && { echo "ERROR: debug debs found!"; exit 1; }

echo ""
echo "============================================"
echo " BUILD COMPLETE - $PROFILE_NAME"
echo "============================================"
echo "Kernel: $KERNEL_VERSION  Arch: $KERNEL_ARCH"
echo ""
find "$WORKDIR" -maxdepth 2 -name 'linux-*.deb' -print | sort
echo ""
echo "Config: $WORKDIR/build-configs/${KERNEL_ARCH}-${KERNEL_VERSION}.config"
echo "Log: $WORKDIR/build-${KERNEL_ARCH}.log"
