#!/bin/bash

# EoIPv6 over 6to4 Tunnel Bonding Manager
# Version: 2.0.0
# Compatible with: Ubuntu 20.04+, Debian 10+

set -e

# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color

# Configuration paths
CONFIG_DIR="/etc/tunnel_bonding"
CONFIG_FILE="${CONFIG_DIR}/config.json"
BACKUP_DIR="${CONFIG_DIR}/backup"
MANAGER_SCRIPT="${CONFIG_DIR}/tunnel_manager.sh"
SERVICE_FILE="/etc/systemd/system/tunnel-bonding.service"
LOG_FILE="/var/log/tunnel_bonding.log"

# Initialize logging
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "${LOG_FILE}"
}

# Print colored messages
print_msg() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

# Check if running as root
check_root() {
    if [[ $EUID -ne 0 ]]; then
        print_msg "$RED" "Error: This script must be run as root"
        exit 1
    fi
}

# Create necessary directories
init_directories() {
    mkdir -p "${CONFIG_DIR}"
    mkdir -p "${BACKUP_DIR}"
    touch "${LOG_FILE}"
    log "Initialized directories"
}

# Install prerequisites
install_prerequisites() {
    print_msg "$CYAN" "Installing prerequisites..."
    
    local packages=("iproute2" "bridge-utils" "ifenslave" "jq" "net-tools")
    local kernel_module="linux-modules-extra-$(uname -r)"
    
    # Check if kernel module package exists
    if apt-cache show "${kernel_module}" &>/dev/null; then
        packages+=("${kernel_module}")
    fi
    
    apt-get update -qq
    apt-get install -y -qq "${packages[@]}" >/dev/null 2>&1
    
    # Load bonding module
    modprobe bonding 2>/dev/null || true
    if ! grep -q "^bonding$" /etc/modules 2>/dev/null; then
        echo "bonding" >> /etc/modules
    fi
    
    print_msg "$GREEN" "Prerequisites installed successfully"
    log "Prerequisites installed"
}

# Validate IP address
validate_ip() {
    local ip=$1
    local valid_regex='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
    
    if [[ ! $ip =~ $valid_regex ]]; then
        return 1
    fi
    
    local IFS='.'
    local -a octets=($ip)
    for octet in "${octets[@]}"; do
        if ((octet > 255)); then
            return 1
        fi
    done
    
    return 0
}

# Read IPs from user
read_ips() {
    local prompt=$1
    local -a ips=()
    local ip
    
    print_msg "$CYAN" "$prompt"
    print_msg "$YELLOW" "Enter IPs one per line. Press Enter on empty line to finish:"
    
    while true; do
        read -r ip
        if [[ -z "$ip" ]]; then
            break
        fi
        
        if validate_ip "$ip"; then
            ips+=("$ip")
            print_msg "$GREEN" "  ✓ Added: $ip"
        else
            print_msg "$RED" "  ✗ Invalid IP format: $ip"
        fi
    done
    
    if [[ ${#ips[@]} -eq 0 ]]; then
        print_msg "$RED" "No valid IPs entered"
        return 1
    fi
    
    echo "${ips[@]}"
}

# Convert IPv4 to 6to4 address
ipv4_to_6to4() {
    local ip=$1
    local IFS='.'
    local -a octets=($ip)
    printf "2002:%02x%02x:%02x%02x::1" "${octets[0]}" "${octets[1]}" "${octets[2]}" "${octets[3]}"
}

# Clean up existing tunnels
cleanup_tunnels() {
    print_msg "$YELLOW" "Cleaning up existing tunnels..."
    
    # Stop service if exists
    if systemctl is-active --quiet tunnel-bonding 2>/dev/null; then
        systemctl stop tunnel-bonding 2>/dev/null || true
    fi
    
    # Delete bond interface
    ip link delete bond0 2>/dev/null || true
    
    # Delete EoIP interfaces
    for i in {1..100}; do
        ip link delete "eoip${i}" 2>/dev/null || true
    done
    
    # Delete 6to4 tunnels
    for i in {1..100}; do
        ip tunnel del "tun6to4_${i}" 2>/dev/null || true
    done
    
    # Clear any remaining tunnel interfaces
    ip -6 tunnel show 2>/dev/null | grep -E "tun6to4_" | awk '{print $1}' | cut -d: -f1 | while read -r tun; do
        ip tunnel del "$tun" 2>/dev/null || true
    done
    
    print_msg "$GREEN" "Cleanup completed"
    log "Cleaned up existing tunnels"
}

# Save configuration
save_config() {
    local server_type=$1
    shift
    local iran_ips=($1)
    shift
    local foreign_ips=($@)
    
    # Backup existing config
    if [[ -f "$CONFIG_FILE" ]]; then
        cp "$CONFIG_FILE" "${BACKUP_DIR}/config_$(date +%Y%m%d_%H%M%S).json"
    fi
    
    # Create JSON configuration
    cat > "$CONFIG_FILE" << EOF
{
    "server_type": "${server_type}",
    "iran_ips": [$(printf '"%s",' "${iran_ips[@]}" | sed 's/,$//') ],
    "foreign_ips": [$(printf '"%s",' "${foreign_ips[@]}" | sed 's/,$//') ],
    "created": "$(date -Iseconds)",
    "version": "2.0.0"
}
EOF
    
    log "Configuration saved"
}

# Load configuration
load_config() {
    if [[ ! -f "$CONFIG_FILE" ]]; then
        return 1
    fi
    
    if ! command -v jq &>/dev/null; then
        apt-get install -y -qq jq >/dev/null 2>&1
    fi
    
    echo "$(jq -r . "$CONFIG_FILE" 2>/dev/null)"
}

# Create tunnel manager script
create_manager_script() {
    local server_type=$1
    local iran_ips=($2)
    shift 2
    local foreign_ips=($@)
    
    cat > "$MANAGER_SCRIPT" << 'SCRIPT_START'
#!/bin/bash

# Load configuration
CONFIG_FILE="/etc/tunnel_bonding/config.json"
LOG_FILE="/var/log/tunnel_bonding.log"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "${LOG_FILE}"
}

# Parse configuration
SERVER_TYPE=$(jq -r '.server_type' "$CONFIG_FILE")
IRAN_IPS=($(jq -r '.iran_ips[]' "$CONFIG_FILE"))
FOREIGN_IPS=($(jq -r '.foreign_ips[]' "$CONFIG_FILE"))

# Enable forwarding
sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null 2>&1
sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1

# Make persistent
sed -i 's/#net.ipv6.conf.all.forwarding=1/net.ipv6.conf.all.forwarding=1/' /etc/sysctl.conf 2>/dev/null || \
    echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf 2>/dev/null || \
    echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

# Create bonding interface
ip link add bond0 type bond mode balance-rr miimon 100 2>/dev/null || true
ip link set bond0 up

if [[ "$SERVER_TYPE" == "iran" ]]; then
    BOND_IP="10.0.0.1/24"
else
    BOND_IP="10.0.0.2/24"
fi

ip addr add ${BOND_IP} dev bond0 2>/dev/null || true

# Function to convert IPv4 to 6to4
ipv4_to_6to4() {
    local ip=$1
    local IFS='.'
    local -a octets=($ip)
    printf "2002:%02x%02x:%02x%02x::1" "${octets[0]}" "${octets[1]}" "${octets[2]}" "${octets[3]}"
}

# Create tunnels
TUNNEL_COUNT=${#IRAN_IPS[@]}

for ((i=0; i<$TUNNEL_COUNT; i++)); do
    TUNNEL_NUM=$((i+1))
    
    if [[ "$SERVER_TYPE" == "iran" ]]; then
        LOCAL_IP=${IRAN_IPS[$i]}
        REMOTE_IP=${FOREIGN_IPS[$i]}
    else
        LOCAL_IP=${FOREIGN_IPS[$i]}
        REMOTE_IP=${IRAN_IPS[$i]}
    fi
    
    # Calculate 6to4 addresses
    LOCAL_6TO4=$(ipv4_to_6to4 "$LOCAL_IP")
    REMOTE_6TO4=$(ipv4_to_6to4 "$REMOTE_IP")
    
    # Create 6to4 tunnel
    ip tunnel add "tun6to4_${TUNNEL_NUM}" mode sit remote any local "${LOCAL_IP}" 2>/dev/null || true
    ip link set "tun6to4_${TUNNEL_NUM}" up
    ip -6 addr add "${LOCAL_6TO4}/64" dev "tun6to4_${TUNNEL_NUM}" 2>/dev/null || true
    ip -6 route add "${REMOTE_6TO4}/128" dev "tun6to4_${TUNNEL_NUM}" 2>/dev/null || true
    
    # Create EoIPv6 tunnel
    ip link add "eoip${TUNNEL_NUM}" type ip6gretap \
        local "${LOCAL_6TO4}" remote "${REMOTE_6TO4}" \
        dev "tun6to4_${TUNNEL_NUM}" 2>/dev/null || true
    
    ip link set "eoip${TUNNEL_NUM}" up
    ip link set "eoip${TUNNEL_NUM}" master bond0
    ip link set "eoip${TUNNEL_NUM}" mtu 1480
    
    log "Tunnel ${TUNNEL_NUM} created: ${LOCAL_IP} <-> ${REMOTE_IP}"
done

# Configure NAT
iptables -t nat -A POSTROUTING -o bond0 -j MASQUERADE 2>/dev/null || true

if [[ "$SERVER_TYPE" == "iran" ]]; then
    # Iran server specific routing
    iptables -t nat -A PREROUTING -p tcp --dport 22 -j DNAT --to-destination ${IRAN_IPS[0]} 2>/dev/null || true
    iptables -t nat -A PREROUTING -j DNAT --to-destination 10.0.0.2 2>/dev/null || true
fi

log "All ${TUNNEL_COUNT} tunnels created and bonded successfully"
SCRIPT_START

    chmod +x "$MANAGER_SCRIPT"
    log "Manager script created"
}

# Create systemd service
create_systemd_service() {
    cat > "$SERVICE_FILE" << 'SERVICE_END'
[Unit]
Description=EoIPv6 Tunnel Bonding Service
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash /etc/tunnel_bonding/tunnel_manager.sh
ExecReload=/bin/bash -c 'systemctl stop tunnel-bonding && systemctl start tunnel-bonding'
ExecStop=/bin/bash -c 'ip link delete bond0 2>/dev/null; for i in $(seq 1 100); do ip link delete eoip$i 2>/dev/null; ip tunnel del tun6to4_$i 2>/dev/null; done; exit 0'
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
SERVICE_END

    systemctl daemon-reload
    systemctl enable tunnel-bonding.service 2>/dev/null
    log "Systemd service created"
}

# Start tunnel service
start_tunnels() {
    print_msg "$CYAN" "Starting tunnel service..."
    systemctl restart tunnel-bonding.service
    
    sleep 3
    
    if systemctl is-active --quiet tunnel-bonding.service; then
        print_msg "$GREEN" "✓ Tunnel service started successfully"
        log "Tunnel service started"
        return 0
    else
        print_msg "$RED" "✗ Failed to start tunnel service"
        log "Failed to start tunnel service"
        return 1
    fi
}

# Show tunnel status
show_status() {
    echo ""
    print_msg "$MAGENTA" "═══════════════════════════════════════════════════"
    print_msg "$WHITE" "           TUNNEL BONDING STATUS REPORT"
    print_msg "$MAGENTA" "═══════════════════════════════════════════════════"
    echo ""
    
    # Service status
    print_msg "$CYAN" "▶ Service Status:"
    if systemctl is-active --quiet tunnel-bonding 2>/dev/null; then
        print_msg "$GREEN" "  ✓ tunnel-bonding.service is running"
    else
        print_msg "$RED" "  ✗ tunnel-bonding.service is not running"
    fi
    echo ""
    
    # Configuration
    if [[ -f "$CONFIG_FILE" ]]; then
        print_msg "$CYAN" "▶ Configuration:"
        local server_type=$(jq -r '.server_type' "$CONFIG_FILE" 2>/dev/null)
        local created=$(jq -r '.created' "$CONFIG_FILE" 2>/dev/null)
        print_msg "$WHITE" "  Server Type: ${server_type^^}"
        print_msg "$WHITE" "  Created: $created"
        
        local iran_count=$(jq -r '.iran_ips | length' "$CONFIG_FILE" 2>/dev/null)
        local foreign_count=$(jq -r '.foreign_ips | length' "$CONFIG_FILE" 2>/dev/null)
        print_msg "$WHITE" "  Iran IPs: $iran_count"
        print_msg "$WHITE" "  Foreign IPs: $foreign_count"
    else
        print_msg "$YELLOW" "▶ No configuration found"
    fi
    echo ""
    
    # Bond interface
    print_msg "$CYAN" "▶ Bond Interface:"
    if ip link show bond0 &>/dev/null; then
        local bond_state=$(ip link show bond0 | grep -o "state [A-Z]*" | cut -d' ' -f2)
        local bond_ip=$(ip addr show bond0 | grep "inet " | awk '{print $2}')
        print_msg "$WHITE" "  State: $bond_state"
        print_msg "$WHITE" "  IP: $bond_ip"
        
        if [[ -f /proc/net/bonding/bond0 ]]; then
            local slave_count=$(grep -c "Slave Interface" /proc/net/bonding/bond0 2>/dev/null || echo "0")
            print_msg "$WHITE" "  Active Slaves: $slave_count"
        fi
    else
        print_msg "$RED" "  ✗ Bond interface not found"
    fi
    echo ""
    
    # Tunnel status
    print_msg "$CYAN" "▶ Active Tunnels:"
    local tunnel_count=0
    for i in {1..100}; do
        if ip link show "eoip${i}" &>/dev/null; then
            local state=$(ip link show "eoip${i}" | grep -o "state [A-Z]*" | cut -d' ' -f2)
            if [[ "$state" == "UP" ]]; then
                print_msg "$GREEN" "  ✓ Tunnel ${i}: $state"
            else
                print_msg "$YELLOW" "  ⚠ Tunnel ${i}: $state"
            fi
            ((tunnel_count++))
        fi
    done
    
    if [[ $tunnel_count -eq 0 ]]; then
        print_msg "$RED" "  ✗ No active tunnels found"
    else
        print_msg "$WHITE" "  Total Active: $tunnel_count tunnels"
    fi
    echo ""
    
    # Network statistics
    if ip link show bond0 &>/dev/null; then
        print_msg "$CYAN" "▶ Network Statistics:"
        local stats=$(ip -s link show bond0 | awk '/RX:/{getline; printf "RX: %s bytes, %s packets", $1, $2} /TX:/{getline; printf " | TX: %s bytes, %s packets", $1, $2}')
        print_msg "$WHITE" "  $stats"
    fi
    
    echo ""
    print_msg "$MAGENTA" "═══════════════════════════════════════════════════"
}

# Setup new tunnel
setup_new_tunnel() {
    print_msg "$CYAN" "\n▶ Select Server Type:"
    print_msg "$WHITE" "  1) Iran Server"
    print_msg "$WHITE" "  2) Foreign Server"
    
    local server_type
    while true; do
        read -p "Enter choice [1-2]: " choice
        case $choice in
            1)
                server_type="iran"
                print_msg "$GREEN" "Selected: Iran Server"
                break
                ;;
            2)
                server_type="foreign"
                print_msg "$GREEN" "Selected: Foreign Server"
                break
                ;;
            *)
                print_msg "$RED" "Invalid choice. Please enter 1 or 2"
                ;;
        esac
    done
    
    # Read Iran IPs
    echo ""
    local iran_ips_str=$(read_ips "Enter Iran Server IPs:")
    if [[ -z "$iran_ips_str" ]]; then
        return 1
    fi
    local iran_ips=($iran_ips_str)
    
    # Read Foreign IPs
    echo ""
    local foreign_ips_str=$(read_ips "Enter Foreign Server IPs:")
    if [[ -z "$foreign_ips_str" ]]; then
        return 1
    fi
    local foreign_ips=($foreign_ips_str)
    
    # Validate IP count
    if [[ ${#iran_ips[@]} -ne ${#foreign_ips[@]} ]]; then
        print_msg "$RED" "Error: Number of Iran IPs (${#iran_ips[@]}) must match Foreign IPs (${#foreign_ips[@]})"
        return 1
    fi
    
    print_msg "$GREEN" "\n✓ Configuration validated:"
    print_msg "$WHITE" "  - Server Type: ${server_type^^}"
    print_msg "$WHITE" "  - Iran IPs: ${#iran_ips[@]}"
    print_msg "$WHITE" "  - Foreign IPs: ${#foreign_ips[@]}"
    
    # Cleanup and setup
    cleanup_tunnels
    save_config "$server_type" "${iran_ips[*]}" "${foreign_ips[@]}"
    create_manager_script "$server_type" "${iran_ips[*]}" "${foreign_ips[@]}"
    create_systemd_service
    start_tunnels
    
    print_msg "$GREEN" "\n✓ Tunnel setup completed successfully!"
}

# Edit existing configuration
edit_configuration() {
    local config=$(load_config)
    if [[ -z "$config" ]]; then
        print_msg "$RED" "No existing configuration found"
        return 1
    fi
    
    local current_server_type=$(echo "$config" | jq -r '.server_type')
    local current_iran_ips=($(echo "$config" | jq -r '.iran_ips[]'))
    local current_foreign_ips=($(echo "$config" | jq -r '.foreign_ips[]'))
    
    print_msg "$CYAN" "\n▶ Current Configuration:"
    print_msg "$WHITE" "  Server Type: ${current_server_type^^}"
    print_msg "$WHITE" "  Iran IPs: ${#current_iran_ips[@]} configured"
    for ip in "${current_iran_ips[@]}"; do
        print_msg "$YELLOW" "    - $ip"
    done
    print_msg "$WHITE" "  Foreign IPs: ${#current_foreign_ips[@]} configured"
    for ip in "${current_foreign_ips[@]}"; do
        print_msg "$YELLOW" "    - $ip"
    done
    
    # Ask what to change
    echo ""
    print_msg "$CYAN" "▶ What would you like to change?"
    print_msg "$WHITE" "  1) Change Server Type"
    print_msg "$WHITE" "  2) Change Iran IPs"
    print_msg "$WHITE" "  3) Change Foreign IPs"
    print_msg "$WHITE" "  4) Change Both Iran and Foreign IPs"
    print_msg "$WHITE" "  5) Cancel"
    
    read -p "Enter choice [1-5]: " edit_choice
    
    local new_server_type="$current_server_type"
    local new_iran_ips=("${current_iran_ips[@]}")
    local new_foreign_ips=("${current_foreign_ips[@]}")
    
    case $edit_choice in
        1)
            if [[ "$current_server_type" == "iran" ]]; then
                new_server_type="foreign"
            else
                new_server_type="iran"
            fi
            print_msg "$GREEN" "Server type changed to: ${new_server_type^^}"
            ;;
        2)
            echo ""
            local iran_ips_str=$(read_ips "Enter new Iran Server IPs:")
            if [[ -z "$iran_ips_str" ]]; then
                return 1
            fi
            new_iran_ips=($iran_ips_str)
            ;;
        3)
            echo ""
            local foreign_ips_str=$(read_ips "Enter new Foreign Server IPs:")
            if [[ -z "$foreign_ips_str" ]]; then
                return 1
            fi
            new_foreign_ips=($foreign_ips_str)
            ;;
        4)
            echo ""
            local iran_ips_str=$(read_ips "Enter new Iran Server IPs:")
            if [[ -z "$iran_ips_str" ]]; then
                return 1
            fi
            new_iran_ips=($iran_ips_str)
            
            echo ""
            local foreign_ips_str=$(read_ips "Enter new Foreign Server IPs:")
            if [[ -z "$foreign_ips_str" ]]; then
                return 1
            fi
            new_foreign_ips=($foreign_ips_str)
            ;;
        5)
            print_msg "$YELLOW" "Edit cancelled"
            return 0
            ;;
        *)
            print_msg "$RED" "Invalid choice"
            return 1
            ;;
    esac
    
    # Validate new configuration
    if [[ ${#new_iran_ips[@]} -ne ${#new_foreign_ips[@]} ]]; then
        print_msg "$RED" "Error: Number of Iran IPs (${#new_iran_ips[@]}) must match Foreign IPs (${#new_foreign_ips[@]})"
        return 1
    fi
    
    print_msg "$GREEN" "\n✓ New configuration validated:"
    print_msg "$WHITE" "  - Server Type: ${new_server_type^^}"
    print_msg "$WHITE" "  - Iran IPs: ${#new_iran_ips[@]}"
    print_msg "$WHITE" "  - Foreign IPs: ${#new_foreign_ips[@]}"
    
    # Apply changes
    print_msg "$CYAN" "\nApplying changes..."
    cleanup_tunnels
    save_config "$new_server_type" "${new_iran_ips[*]}" "${new_foreign_ips[@]}"
    create_manager_script "$new_server_type" "${new_iran_ips[*]}" "${new_foreign_ips[@]}"
    create_systemd_service
    start_tunnels
    
    print_msg "$GREEN" "\n✓ Configuration updated successfully!"
}

# Remove all tunnels
remove_all_tunnels() {
    print_msg "$YELLOW" "\n⚠ This will remove all tunnel configurations and services."
    read -p "Are you sure? [y/N]: " confirm
    
    if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
        print_msg "$YELLOW" "Operation cancelled"
        return 0
    fi
    
    print_msg "$CYAN" "Removing all tunnels and configurations..."
    
    # Stop and disable service
    systemctl stop tunnel-bonding 2>/dev/null || true
    systemctl disable tunnel-bonding 2>/dev/null || true
    
    # Cleanup tunnels
    cleanup_tunnels
    
    # Remove files
    rm -f "$SERVICE_FILE"
    rm -f "$MANAGER_SCRIPT"
    rm -f "$CONFIG_FILE"
    
    systemctl daemon-reload
    
    print_msg "$GREEN" "✓ All tunnels and configurations removed"
    log "All tunnels and configurations removed"
}

# Main menu
show_menu() {
    echo ""
    print_msg "$MAGENTA" "╔═══════════════════════════════════════════════════╗"
    print_msg "$MAGENTA" "║     EoIPv6 over 6to4 Tunnel Bonding Manager      ║"
    print_msg "$MAGENTA" "║                   Version 2.0.0                   ║"
    print_msg "$MAGENTA" "╚═══════════════════════════════════════════════════╝"
    echo ""
    print_msg "$CYAN" "Main Menu:"
    print_msg "$WHITE" "  1) Setup New Tunnel Configuration"
    print_msg "$WHITE" "  2) Edit Existing Configuration"
    print_msg "$WHITE" "  3) Show Current Status"
    print_msg "$WHITE" "  4) Remove All Tunnels"
    print_msg "$WHITE" "  5) Exit"
    echo ""
}

# Main function
main() {
    check_root
    init_directories
    
    # Check if first run
    if [[ ! -f "$CONFIG_FILE" ]] && [[ ! -f "$SERVICE_FILE" ]]; then
        print_msg "$CYAN" "First run detected. Installing prerequisites..."
        install_prerequisites
    fi
    
    while true; do
        show_menu
        read -p "Enter your choice [1-5]: " choice
        
        case $choice in
            1)
                setup_new_tunnel
                ;;
            2)
                edit_configuration
                ;;
            3)
                show_status
                ;;
            4)
                remove_all_tunnels
                ;;
            5)
                print_msg "$GREEN" "Goodbye!"
                exit 0
                ;;
            *)
                print_msg "$RED" "Invalid option. Please try again."
                ;;
        esac
        
        echo ""
        read -p "Press Enter to continue..."
    done
}

# Run main function
main "$@"
