#!/bin/bash
# =============================================================================
# WireGuard Manager - Универсальный скрипт управления WireGuard
# Поддерживает: Ubuntu/Debian, CentOS/RHEL, Arch Linux
# =============================================================================

set -euo pipefail

# --- Цвета ---
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'

# --- Конфиги ---
WG_DIR="/etc/wireguard"
CLIENTS_DIR="/etc/wireguard/clients"
WG_BACKUP_DIR="/etc/wireguard/backups"

# =============================================================================
# Утилиты
# =============================================================================

log_info()    { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn()    { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error()   { echo -e "${RED}[ERROR]${NC} $*" >&2; }
log_success() { echo -e "${GREEN}[✓]${NC} $*"; }

check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "Скрипт должен запускаться от root (sudo $0)"
        exit 1
    fi
}

detect_os() {
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        OS_ID="${ID}"
        OS_LIKE="${ID_LIKE:-}"
    else
        log_error "Не удалось определить ОС"
        exit 1
    fi
}


install_wireguard() {
    detect_os
    log_info "Обнаружена ОС: $OS_ID"

    if command -v wg &>/dev/null; then
        log_success "WireGuard уже установлен"
        return
    fi

    log_info "Устанавливаю WireGuard..."

    case "$OS_ID" in
        ubuntu|debian|linuxmint)
            apt-get update -qq
            apt-get install -y wireguard wireguard-tools qrencode iptables
            ;;
        centos|rhel|fedora|rocky|almalinux)
            if command -v dnf &>/dev/null; then
                dnf install -y epel-release 2>/dev/null || true
                dnf install -y wireguard-tools qrencode iptables
            else
                yum install -y epel-release 2>/dev/null || true
                yum install -y wireguard-tools qrencode iptables
            fi
            ;;
        arch|manjaro)
            pacman -Sy --noconfirm wireguard-tools qrencode iptables
            ;;
        *)
            if echo "$OS_LIKE" | grep -q "debian"; then
                apt-get update -qq
                apt-get install -y wireguard wireguard-tools qrencode iptables
            elif echo "$OS_LIKE" | grep -q "rhel\|fedora"; then
                dnf install -y wireguard-tools qrencode iptables 2>/dev/null || \
                yum install -y wireguard-tools qrencode iptables
            else
                log_error "Неподдерживаемая ОС: $OS_ID"
                log_warn "Установите wireguard-tools и qrencode вручную"
                exit 1
            fi
            ;;
    esac

    log_success "WireGuard успешно установлен"
}

press_enter() {
    echo ""
    read -rp "Нажмите Enter для продолжения..."
}

confirm() {
    local prompt="${1:-Продолжить?}"
    read -rp "${prompt} [y/N]: " answer
    [[ "${answer,,}" == "y" ]]
}

# =============================================================================
# Работа с конфигурациями серверов
# =============================================================================

list_configs() {
    ls "${WG_DIR}"/*.conf 2>/dev/null | xargs -I{} basename {} .conf || true
}

select_config() {
    local configs
    mapfile -t configs < <(list_configs)

    if [[ ${#configs[@]} -eq 0 ]]; then
        log_warn "Нет доступных конфигураций"
        return 1
    fi

    echo ""
    echo -e "${CYAN}Доступные конфигурации:${NC}"
    local i=1
    for cfg in "${configs[@]}"; do
        local status=""
        if systemctl is-active --quiet "wg-quick@${cfg}" 2>/dev/null; then
            status="${GREEN}[активна]${NC}"
        else
            status="${RED}[остановлена]${NC}"
        fi
        echo -e "  ${BOLD}$i)${NC} ${cfg} ${status}"
        ((i++))
    done

    echo ""
    read -rp "Выберите конфигурацию [1-${#configs[@]}]: " choice

    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#configs[@]} )); then
        log_error "Неверный выбор"
        return 1
    fi

    SELECTED_CONFIG="${configs[$((choice-1))]}"
    SELECTED_CONFIG_FILE="${WG_DIR}/${SELECTED_CONFIG}.conf"
    return 0
}

# Применяет конфиг к активному интерфейсу без wg-quick strip (он плодит .bak файлы).
# Используем wg addconf с временным файлом только новых [Peer] блоков.
wg_apply_config() {
    local iface="$1"
    local config_file="$2"

    if ! systemctl is-active --quiet "wg-quick@${iface}" 2>/dev/null; then
        return 0
    fi

    # Извлекаем только [Peer] блоки из конфига и передаём через wg addconf
    # wg setconf заменяет всё целиком — безопаснее чем syncconf для нашего случая
    local tmp_conf
    tmp_conf=$(mktemp /tmp/wg_peers_XXXXXX.conf)
    # Вырезаем все блоки [Peer] (всё что после первого [Peer])
    awk '/^\[Peer\]/{found=1} found{print}' "$config_file"         | grep -v "^# PEER_NAME" > "$tmp_conf"

    # Применяем через wg setconf (полная замена, без .bak)
    local iface_section
    iface_section=$(mktemp /tmp/wg_iface_XXXXXX.conf)
    awk '/^\[Interface\]/{found=1} /^\[Peer\]/{found=0} found{print}' "$config_file"         | grep -v "^#" | grep -v "^$" > "$iface_section"

    # Объединяем Interface (без комментариев) + все Peer блоки
    cat "$iface_section" "$tmp_conf" > /tmp/wg_full_apply.conf
    rm -f "$tmp_conf" "$iface_section"

    if wg setconf "$iface" /tmp/wg_full_apply.conf 2>/dev/null; then
        rm -f /tmp/wg_full_apply.conf
        log_success "Конфигурация применена (wg setconf)"
    else
        rm -f /tmp/wg_full_apply.conf
        systemctl restart "wg-quick@${iface}" 2>/dev/null
        log_success "Интерфейс перезапущен"
    fi
}

get_used_ports() {
    # Порты из всех существующих конфигов WireGuard
    grep -h "^ListenPort" "${WG_DIR}"/*.conf 2>/dev/null | awk '{print $3}' || true
}

get_used_subnets() {
    # Подсети из всех конфигов + все активные маршруты
    grep -h "^Address" "${WG_DIR}"/*.conf 2>/dev/null | grep -oP '\d+\.\d+\.\d+\.\d+/\d+' || true
    ip route show 2>/dev/null | grep -v default | awk '{print $1}' | grep -v via || true
}

suggest_free_port() {
    local used_ports
    mapfile -t used_ports < <(get_used_ports)
    local candidate=51820
    while printf '%s\n' "${used_ports[@]}" | grep -q "^${candidate}$"; do
        ((candidate++))
    done
    echo "$candidate"
}

suggest_free_subnet() {
    local used_subnets
    mapfile -t used_subnets < <(get_used_subnets)
    for third in $(seq 0 255); do
        local candidate="10.${third}.0.0/24"
        local candidate_base="10.${third}.0"
        local conflict=0
        for used in "${used_subnets[@]}"; do
            local used_base="${used%.*}"
            used_base="${used_base%/*}"
            if [[ "$used_base" == "$candidate_base" ]] || \
               ip route show 2>/dev/null | grep -q "^${candidate_base}"; then
                conflict=1
                break
            fi
        done
        if [[ $conflict -eq 0 ]]; then
            echo "10.${third}.0.1/24"
            return
        fi
    done
    echo "10.99.0.1/24"
}

create_server_config() {
    echo ""
    echo -e "${BOLD}=== Создание новой серверной конфигурации ===${NC}"
    echo ""

    read -rp "Имя интерфейса (например wg0): " iface
    iface="${iface:-wg0}"

    if [[ -f "${WG_DIR}/${iface}.conf" ]]; then
        log_warn "Конфигурация ${iface}.conf уже существует!"
        confirm "Перезаписать?" || return
    fi

    # Предлагаем свободный порт
    local free_port
    free_port=$(suggest_free_port)
    local used_ports_list
    used_ports_list=$(get_used_ports | tr '\n' ' ')
    [[ -n "$used_ports_list" ]] && log_warn "Уже используются порты: ${used_ports_list}"

    read -rp "Порт сервера [${free_port}]: " port
    port="${port:-$free_port}"

    # Проверяем выбранный порт
    if get_used_ports | grep -q "^${port}$"; then
        log_error "Порт ${port} уже занят другим WireGuard интерфейсом!"
        log_warn "Свободный порт: $(suggest_free_port)"
        press_enter
        return
    fi

    # Предлагаем свободную подсеть
    local free_subnet
    free_subnet=$(suggest_free_subnet)
    local used_subnets_list
    used_subnets_list=$(get_used_subnets | tr '\n' ' ')
    [[ -n "$used_subnets_list" ]] && log_warn "Уже используются подсети: ${used_subnets_list}"

    read -rp "Подсеть сервера [${free_subnet}]: " subnet
    subnet="${subnet:-$free_subnet}"

    # Проверяем конфликт подсети
    local subnet_base="${subnet%.*}"
    subnet_base="${subnet_base%/*}"
    if ip route show 2>/dev/null | grep -q "^${subnet_base}"; then
        log_error "Подсеть ${subnet} конфликтует с существующим маршрутом!"
        log_warn "Рекомендуемая свободная подсеть: ${free_subnet}"
        press_enter
        return
    fi

    local server_ip="${subnet%/*}"

    read -rp "Публичный IP или домен сервера: " server_endpoint
    if [[ -z "$server_endpoint" ]]; then
        server_endpoint=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || echo "")
        if [[ -n "$server_endpoint" ]]; then
            log_info "Определён внешний IP: $server_endpoint"
        else
            log_error "Не удалось определить внешний IP. Введите вручную."
            return 1
        fi
    fi

    # Автоопределение основного сетевого интерфейса
    local default_iface
    default_iface=$(ip route show default 2>/dev/null | awk '/default/ {print $5}' | head -1)
    default_iface="${default_iface:-eth0}"
    read -rp "Сетевой интерфейс для NAT [${default_iface}]: " net_iface
    net_iface="${net_iface:-$default_iface}"

    log_info "Генерирую ключи сервера..."
    local privkey pubkey
    privkey=$(wg genkey)
    pubkey=$(echo "$privkey" | wg pubkey)

    mkdir -p "${WG_DIR}"
    chmod 700 "${WG_DIR}"

    cat > "${WG_DIR}/${iface}.conf" <<EOF
[Interface]
Address = ${subnet}
ListenPort = ${port}
PrivateKey = ${privkey}

# PostUp и PostDown закомментированы — настройте файрвол вручную
# Примеры:
#   iptables:  PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ${net_iface} -j MASQUERADE
#              PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ${net_iface} -j MASQUERADE
#   nftables:  PostUp = nft add rule inet filter forward iifname "%i" accept; nft add rule ip nat postrouting oifname "${net_iface}" masquerade
#              PostDown = nft delete rule inet filter forward iifname "%i" accept
#   ufw:       PostUp = ufw route allow in on %i out on ${net_iface}; iptables -t nat -A POSTROUTING -o ${net_iface} -j MASQUERADE
#              PostDown = ufw route delete allow in on %i out on ${net_iface}; iptables -t nat -D POSTROUTING -o ${net_iface} -j MASQUERADE

# SERVER_ENDPOINT=${server_endpoint}
# SERVER_PORT=${port}
# SERVER_SUBNET=${subnet%.*}
# NET_IFACE=${net_iface}
EOF

    chmod 600 "${WG_DIR}/${iface}.conf"

    # Сохраняем публичный ключ
    echo "$pubkey" > "${WG_DIR}/${iface}.pub"

    # Включаем IP forwarding
    if ! grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf; then
        echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
        sysctl -p -q
    fi

    log_success "Конфигурация ${iface}.conf создана"
    echo -e "  Публичный ключ сервера: ${CYAN}${pubkey}${NC}"
    echo ""
    log_warn "Не забудьте:"
    log_warn "  1. Открыть порт ${port}/udp в вашем файрволе"
    log_warn "  2. Раскомментировать и настроить PostUp/PostDown в ${WG_DIR}/${iface}.conf"

    if confirm "Запустить интерфейс сейчас?"; then
        systemctl enable "wg-quick@${iface}" --quiet
        systemctl start "wg-quick@${iface}"
        log_success "Интерфейс ${iface} запущен"
    fi
}


show_server_config() {
    select_config || { press_enter; return; }

    echo ""
    echo -e "${CYAN}=== Конфигурация: ${SELECTED_CONFIG} ===${NC}"
    echo ""
    cat "${SELECTED_CONFIG_FILE}"

    echo ""
    echo -e "${CYAN}=== Статус WireGuard ===${NC}"
    wg show "${SELECTED_CONFIG}" 2>/dev/null || log_warn "Интерфейс не активен"

    press_enter
}

delete_server_config() {
    select_config || { press_enter; return; }

    echo ""
    log_warn "Вы собираетесь удалить конфигурацию: ${SELECTED_CONFIG}"

    confirm "Вы уверены?" || return

    # Останавливаем если запущен
    if systemctl is-active --quiet "wg-quick@${SELECTED_CONFIG}" 2>/dev/null; then
        systemctl stop "wg-quick@${SELECTED_CONFIG}"
        systemctl disable "wg-quick@${SELECTED_CONFIG}" --quiet 2>/dev/null || true
    fi

    rm -f "${SELECTED_CONFIG_FILE}"
    rm -f "${WG_DIR}/${SELECTED_CONFIG}.pub"

    # Удаляем клиентские конфиги для этого сервера
    if [[ -d "${CLIENTS_DIR}/${SELECTED_CONFIG}" ]]; then
        if confirm "Удалить также клиентские конфиги для ${SELECTED_CONFIG}?"; then
            rm -rf "${CLIENTS_DIR}/${SELECTED_CONFIG}"
        fi
    fi

    log_success "Конфигурация ${SELECTED_CONFIG} удалена"
    press_enter
}

start_stop_config() {
    select_config || { press_enter; return; }

    echo ""
    if systemctl is-active --quiet "wg-quick@${SELECTED_CONFIG}" 2>/dev/null; then
        if confirm "Остановить ${SELECTED_CONFIG}?"; then
            systemctl stop "wg-quick@${SELECTED_CONFIG}"
            log_success "Интерфейс ${SELECTED_CONFIG} остановлен"
        fi
    else
        if confirm "Запустить ${SELECTED_CONFIG}?"; then
            systemctl enable "wg-quick@${SELECTED_CONFIG}" --quiet
            systemctl start "wg-quick@${SELECTED_CONFIG}"
            log_success "Интерфейс ${SELECTED_CONFIG} запущен"
        fi
    fi

    press_enter
}

# =============================================================================
# Работа с пирами (peers) на сервере
# =============================================================================

get_next_ip() {
    local config_file="$1"
    local subnet server_ip
    # Получаем IP сервера (без маски)
    server_ip=$(grep "^Address" "$config_file" | grep -oP '\d+\.\d+\.\d+\.\d+' | head -1)
    # Базовая часть подсети (первые три октета)
    local base="${server_ip%.*}"

    local used_ips
    mapfile -t used_ips < <(grep "AllowedIPs" "$config_file" | grep -oP '\d+\.\d+\.\d+\.\d+' || true)
    used_ips+=("$server_ip")

    for i in $(seq 2 254); do
        local candidate="${base}.${i}"
        if ! printf '%s\n' "${used_ips[@]}" | grep -q "^${candidate}$"; then
            echo "$candidate"
            return
        fi
    done

    log_error "Нет свободных IP в подсети"
    return 1
}

add_peer() {
    select_config || { press_enter; return; }

    echo ""
    echo -e "${BOLD}=== Добавление нового пира ===${NC}"

    read -rp "Имя пира/клиента: " peer_name
    if [[ -z "$peer_name" ]]; then
        log_error "Имя не может быть пустым"
        press_enter
        return
    fi

    # Проверяем дубликат
    if grep -q "# PEER_NAME=${peer_name}" "${SELECTED_CONFIG_FILE}" 2>/dev/null; then
        log_warn "Пир '${peer_name}' уже существует в ${SELECTED_CONFIG}"
        press_enter
        return
    fi

    local next_ip
    next_ip=$(get_next_ip "${SELECTED_CONFIG_FILE}") || { press_enter; return; }

    read -rp "IP для клиента [${next_ip}]: " client_ip
    client_ip="${client_ip:-$next_ip}"

    log_info "Генерирую ключи клиента..."
    local client_privkey client_pubkey preshared_key
    client_privkey=$(wg genkey)
    client_pubkey=$(echo "$client_privkey" | wg pubkey)
    preshared_key=$(wg genpsk)

    # Добавляем peer в серверный конфиг
    cat >> "${SELECTED_CONFIG_FILE}" <<EOF

[Peer]
# PEER_NAME=${peer_name}
PublicKey = ${client_pubkey}
PresharedKey = ${preshared_key}
AllowedIPs = ${client_ip}/32
EOF

    log_success "Пир '${peer_name}' добавлен в ${SELECTED_CONFIG}"

    # Применяем конфиг если интерфейс активен
    wg_apply_config "${SELECTED_CONFIG}" "${SELECTED_CONFIG_FILE}"

    # Сохраняем данные клиента для создания конфига
    mkdir -p "${CLIENTS_DIR}/${SELECTED_CONFIG}"
    cat > "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.key" <<EOF
PRIVATE_KEY=${client_privkey}
PUBLIC_KEY=${client_pubkey}
PRESHARED_KEY=${preshared_key}
CLIENT_IP=${client_ip}
EOF
    chmod 600 "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.key"

    if confirm "Создать клиентский конфиг сейчас?"; then
        create_client_config_for "${SELECTED_CONFIG}" "${SELECTED_CONFIG_FILE}" "${peer_name}"
    fi

    press_enter
}

bulk_add_peers() {
    select_config || { press_enter; return; }

    echo ""
    echo -e "${BOLD}=== Массовое создание пиров по префиксу ===${NC}"
    echo ""

    # Показываем существующие пиры с этим префиксом для наглядности
    read -rp "Префикс имён (например client): " prefix
    if [[ -z "$prefix" ]]; then
        log_error "Префикс не может быть пустым"
        press_enter
        return
    fi

    # Находим уже существующие номера с этим префиксом
    local existing_nums=()
    while IFS= read -r line; do
        local num="${line#"${prefix}"}"
        if [[ "$num" =~ ^[0-9]+$ ]]; then
            existing_nums+=("$num")
        fi
    done < <(grep "# PEER_NAME=${prefix}" "${SELECTED_CONFIG_FILE}" | sed "s/# PEER_NAME=//" || true)

    if [[ ${#existing_nums[@]} -gt 0 ]]; then
        log_info "Уже существуют: $(printf "${prefix}%s " "${existing_nums[@]}")"
    fi

    read -rp "Сколько клиентов создать: " count
    if ! [[ "$count" =~ ^[0-9]+$ ]] || (( count < 1 || count > 253 )); then
        log_error "Введите число от 1 до 253"
        press_enter
        return
    fi

    # Определяем начальный номер — следующий после максимального существующего
    local start_num=1
    if [[ ${#existing_nums[@]} -gt 0 ]]; then
        local max_num=0
        for n in "${existing_nums[@]}"; do
            [[ $n -gt $max_num ]] && max_num=$n || true
        done
        start_num=$(( max_num + 1 ))
    fi

    # Проверяем что имена не пересекаются (могут быть "дыры" в нумерации)
    local names_to_create=()
    local num=$start_num
    local found=0
    while (( found < count )); do
        local candidate_name="${prefix}${num}"
        if ! grep -q "# PEER_NAME=${candidate_name}" "${SELECTED_CONFIG_FILE}" 2>/dev/null; then
            names_to_create+=("$candidate_name")
            (( found++ )) || true
        fi
        (( num++ )) || true
        # Защита от бесконечного цикла
        [[ $num -gt 9999 ]] && break || true
    done

    echo ""
    log_info "Будут созданы пиры: ${names_to_create[*]}"

    read -rp "DNS для клиентов [1.1.1.1, 8.8.8.8]: " bulk_dns
    bulk_dns="${bulk_dns:-1.1.1.1, 8.8.8.8}"

    read -rp "AllowedIPs [0.0.0.0/0, ::/0]: " bulk_allowed
    bulk_allowed="${bulk_allowed:-0.0.0.0/0, ::/0}"

    local make_configs
    confirm "Создать клиентские .conf и QR для всех?" && make_configs=1 || make_configs=0

    echo ""
    confirm "Начать создание ${#names_to_create[@]} пиров?" || { press_enter; return; }

    echo ""
    local created=0
    for peer_name in "${names_to_create[@]}"; do
        local next_ip
        next_ip=$(get_next_ip "${SELECTED_CONFIG_FILE}") || {
            log_error "Закончились свободные IP в подсети"
            break
        }

        local client_privkey client_pubkey preshared_key
        client_privkey=$(wg genkey)
        client_pubkey=$(echo "$client_privkey" | wg pubkey)
        preshared_key=$(wg genpsk)

        # Добавляем peer в серверный конфиг
        cat >> "${SELECTED_CONFIG_FILE}" <<EOF

[Peer]
# PEER_NAME=${peer_name}
PublicKey = ${client_pubkey}
PresharedKey = ${preshared_key}
AllowedIPs = ${next_ip}/32
EOF

        # Сохраняем ключи
        mkdir -p "${CLIENTS_DIR}/${SELECTED_CONFIG}"
        cat > "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.key" <<EOF
PRIVATE_KEY=${client_privkey}
PUBLIC_KEY=${client_pubkey}
PRESHARED_KEY=${preshared_key}
CLIENT_IP=${next_ip}
EOF
        chmod 600 "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.key"

        # Создаём клиентский конфиг если нужно
        if [[ $make_configs -eq 1 ]]; then
            _bulk_create_client_conf "${SELECTED_CONFIG}" "${SELECTED_CONFIG_FILE}"                 "${peer_name}" "${bulk_dns}" "${bulk_allowed}"
        fi

        log_success "  ${peer_name} → ${next_ip}"
        (( created++ )) || true
    done

    echo ""
    log_success "Создано пиров: ${created}"

    # Применяем конфиг если интерфейс активен
    wg_apply_config "${SELECTED_CONFIG}" "${SELECTED_CONFIG_FILE}"

    [[ $make_configs -eq 1 ]] &&         log_info "Конфиги сохранены в: ${CLIENTS_DIR}/${SELECTED_CONFIG}/"

    press_enter
}

# Внутренняя функция создания клиентского конфига без интерактива (для bulk)
_bulk_create_client_conf() {
    local iface="$1" iface_file="$2" peer_name="$3" dns="$4" allowed="$5"

    local key_file="${CLIENTS_DIR}/${iface}/${peer_name}.key"
    local PRIVATE_KEY CLIENT_IP PRESHARED_KEY
    PRIVATE_KEY=$(grep "^PRIVATE_KEY=" "$key_file" | cut -d= -f2-)
    PRESHARED_KEY=$(grep "^PRESHARED_KEY=" "$key_file" | cut -d= -f2-)
    CLIENT_IP=$(grep "^CLIENT_IP=" "$key_file" | cut -d= -f2-)

    local server_endpoint server_port server_pubkey
    server_endpoint=$(grep "# SERVER_ENDPOINT=" "$iface_file" | cut -d= -f2-)
    server_port=$(grep "# SERVER_PORT=" "$iface_file" | cut -d= -f2-)

    if [[ -f "${WG_DIR}/${iface}.pub" ]]; then
        server_pubkey=$(cat "${WG_DIR}/${iface}.pub")
    else
        local srv_privkey
        srv_privkey=$(grep "^PrivateKey" "$iface_file" | awk '{print $3}')
        server_pubkey=$(echo "$srv_privkey" | wg pubkey 2>/dev/null || echo "")
        [[ -n "$server_pubkey" ]] && echo "$server_pubkey" > "${WG_DIR}/${iface}.pub"
    fi

    [[ -z "$server_port" ]] && server_port=$(grep "^ListenPort" "$iface_file" | awk '{print $3}')
    server_port="${server_port:-51820}"

    local client_conf="${CLIENTS_DIR}/${iface}/${peer_name}.conf"
    cat > "$client_conf" <<EOF
[Interface]
PrivateKey = ${PRIVATE_KEY}
Address = ${CLIENT_IP}/32
DNS = ${dns}

[Peer]
PublicKey = ${server_pubkey}
PresharedKey = ${PRESHARED_KEY}
Endpoint = ${server_endpoint}:${server_port}
AllowedIPs = ${allowed}
PersistentKeepalive = 25
EOF
    chmod 600 "$client_conf"

    if command -v qrencode &>/dev/null; then
        qrencode -o "${CLIENTS_DIR}/${iface}/${peer_name}.png" < "$client_conf" 2>/dev/null || true
    fi
}


list_peers() {
    select_config || { press_enter; return; }

    echo ""
    echo -e "${CYAN}=== Пиры в конфигурации: ${SELECTED_CONFIG} ===${NC}"
    echo ""

    local peers
    mapfile -t peers < <(grep "# PEER_NAME=" "${SELECTED_CONFIG_FILE}" | sed 's/# PEER_NAME=//' || true)

    if [[ ${#peers[@]} -eq 0 ]]; then
        log_warn "Нет добавленных пиров"
    else
        local i=1
        for p in "${peers[@]}"; do
            local pub_key ip
            # Получаем IP этого пира (следующий AllowedIPs после его PublicKey)
            ip=$(awk "/# PEER_NAME=${p}/{found=1} found && /AllowedIPs/{print \$3; exit}" "${SELECTED_CONFIG_FILE}" | grep -oP '\d+\.\d+\.\d+\.\d+' || echo "?")
            echo -e "  ${BOLD}$i)${NC} ${p} ${CYAN}${ip}${NC}"
            ((i++))
        done
    fi

    echo ""
    if systemctl is-active --quiet "wg-quick@${SELECTED_CONFIG}" 2>/dev/null; then
        echo -e "${CYAN}--- Активные соединения ---${NC}"
        wg show "${SELECTED_CONFIG}" 2>/dev/null || true
    fi

    press_enter
}

remove_peer() {
    select_config || { press_enter; return; }

    local peers
    mapfile -t peers < <(grep "# PEER_NAME=" "${SELECTED_CONFIG_FILE}" | sed 's/# PEER_NAME=//' || true)

    if [[ ${#peers[@]} -eq 0 ]]; then
        log_warn "Нет пиров для удаления"
        press_enter
        return
    fi

    echo ""
    echo -e "${CYAN}Пиры в ${SELECTED_CONFIG}:${NC}"
    local i=1
    for p in "${peers[@]}"; do
        echo -e "  ${BOLD}$i)${NC} ${p}"
        ((i++))
    done

    echo ""
    read -rp "Выберите пир для удаления [1-${#peers[@]}]: " choice

    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#peers[@]} )); then
        log_error "Неверный выбор"
        press_enter
        return
    fi

    local peer_name="${peers[$((choice-1))]}"
    confirm "Удалить пир '${peer_name}'?" || { press_enter; return; }

    # Удаляем блок [Peer] для данного пира из конфига
    # Используем Python для надёжного парсинга
    python3 - "${SELECTED_CONFIG_FILE}" "${peer_name}" <<'PYEOF'
import sys, re

config_file = sys.argv[1]
peer_name = sys.argv[2]

with open(config_file, 'r') as f:
    content = f.read()

# Разбиваем на блоки
blocks = re.split(r'\n(?=\[)', content)
new_blocks = []
for block in blocks:
    if block.strip().startswith('[Peer]') and f'# PEER_NAME={peer_name}' in block:
        continue  # пропускаем этот блок
    new_blocks.append(block)

with open(config_file, 'w') as f:
    f.write('\n'.join(new_blocks).rstrip('\n') + '\n')

print(f"Пир '{peer_name}' удалён из конфига")
PYEOF

    # Удаляем peer с активного интерфейса
    if systemctl is-active --quiet "wg-quick@${SELECTED_CONFIG}" 2>/dev/null; then
        local pub_key
        pub_key=$(cat "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.key" 2>/dev/null | grep PUBLIC_KEY | cut -d= -f2 || true)
        if [[ -n "$pub_key" ]]; then
            wg set "${SELECTED_CONFIG}" peer "$pub_key" remove 2>/dev/null || true
            log_success "Пир удалён с активного интерфейса"
        fi
    fi

    # Удаляем клиентские файлы
    rm -f "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.key"
    rm -f "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.conf"
    rm -f "${CLIENTS_DIR}/${SELECTED_CONFIG}/${peer_name}.png"

    log_success "Пир '${peer_name}' удалён"
    press_enter
}

# =============================================================================
# Клиентские конфиги
# =============================================================================

create_client_config_for() {
    local iface="$1"
    local iface_file="$2"
    local peer_name="$3"

    local key_file="${CLIENTS_DIR}/${iface}/${peer_name}.key"
    if [[ ! -f "$key_file" ]]; then
        log_error "Файл ключей для '${peer_name}' не найден: $key_file"
        return 1
    fi

    # Явный парсинг файла ключей (source может не работать без export)
    local PRIVATE_KEY PUBLIC_KEY PRESHARED_KEY CLIENT_IP
    PRIVATE_KEY=$(grep "^PRIVATE_KEY=" "$key_file" | cut -d= -f2-)
    PUBLIC_KEY=$(grep "^PUBLIC_KEY=" "$key_file" | cut -d= -f2-)
    PRESHARED_KEY=$(grep "^PRESHARED_KEY=" "$key_file" | cut -d= -f2-)
    CLIENT_IP=$(grep "^CLIENT_IP=" "$key_file" | cut -d= -f2-)

    if [[ -z "$PRIVATE_KEY" || -z "$CLIENT_IP" ]]; then
        log_error "Файл ключей повреждён или неполный: $key_file"
        return 1
    fi

    # Получаем данные сервера из конфига
    local server_endpoint server_port server_pubkey
    server_endpoint=$(grep "# SERVER_ENDPOINT=" "$iface_file" | cut -d= -f2-)
    server_port=$(grep "# SERVER_PORT=" "$iface_file" | cut -d= -f2-)

    # Публичный ключ сервера: из .pub файла, или из приватного ключа в конфиге
    if [[ -f "${WG_DIR}/${iface}.pub" ]]; then
        server_pubkey=$(cat "${WG_DIR}/${iface}.pub")
    else
        local srv_privkey
        srv_privkey=$(grep "^PrivateKey" "$iface_file" | awk '{print $3}')
        server_pubkey=$(echo "$srv_privkey" | wg pubkey 2>/dev/null || "")
        # Сохраняем на будущее
        [[ -n "$server_pubkey" ]] && echo "$server_pubkey" > "${WG_DIR}/${iface}.pub"
    fi

    if [[ -z "$server_pubkey" ]]; then
        log_error "Не удалось получить публичный ключ сервера"
        return 1
    fi

    if [[ -z "$server_endpoint" ]]; then
        read -rp "Публичный IP или домен сервера: " server_endpoint
    fi
    if [[ -z "$server_port" ]]; then
        server_port=$(grep "^ListenPort" "$iface_file" | awk '{print $3}')
        server_port="${server_port:-51820}"
    fi

    echo ""
    read -rp "DNS для клиента [1.1.1.1, 8.8.8.8]: " dns
    dns="${dns:-1.1.1.1, 8.8.8.8}"

    read -rp "AllowedIPs (маршруты) [0.0.0.0/0 = весь трафик]: " allowed
    allowed="${allowed:-0.0.0.0/0, ::/0}"

    local client_conf="${CLIENTS_DIR}/${iface}/${peer_name}.conf"

    cat > "$client_conf" <<EOF
[Interface]
PrivateKey = ${PRIVATE_KEY}
Address = ${CLIENT_IP}/32
DNS = ${dns}

[Peer]
PublicKey = ${server_pubkey}
PresharedKey = ${PRESHARED_KEY}
Endpoint = ${server_endpoint}:${server_port}
AllowedIPs = ${allowed}
PersistentKeepalive = 25
EOF

    chmod 600 "$client_conf"
    log_success "Клиентский конфиг сохранён: $client_conf"

    # QR-код
    if command -v qrencode &>/dev/null; then
        local qr_file="${CLIENTS_DIR}/${iface}/${peer_name}.png"
        qrencode -o "$qr_file" < "$client_conf"
        log_success "QR-код сохранён: $qr_file"

        if confirm "Показать QR-код в терминале?"; then
            qrencode -t ansiutf8 < "$client_conf"
        fi
    else
        log_warn "qrencode не установлен, QR-код не создан"
    fi

    echo ""
    echo -e "${CYAN}=== Содержимое клиентского конфига ===${NC}"
    cat "$client_conf"
}

create_client_config_menu() {
    select_config || { press_enter; return; }

    local peers
    mapfile -t peers < <(grep "# PEER_NAME=" "${SELECTED_CONFIG_FILE}" | sed 's/# PEER_NAME=//' || true)

    if [[ ${#peers[@]} -eq 0 ]]; then
        log_warn "Нет пиров. Сначала добавьте пир."
        press_enter
        return
    fi

    echo ""
    echo -e "${CYAN}Выберите пир для создания конфига:${NC}"
    local i=1
    for p in "${peers[@]}"; do
        echo -e "  ${BOLD}$i)${NC} ${p}"
        ((i++))
    done

    echo ""
    read -rp "Выбор [1-${#peers[@]}]: " choice

    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#peers[@]} )); then
        log_error "Неверный выбор"
        press_enter
        return
    fi

    local peer_name="${peers[$((choice-1))]}"
    create_client_config_for "${SELECTED_CONFIG}" "${SELECTED_CONFIG_FILE}" "${peer_name}"
    press_enter
}

show_client_configs() {
    select_config || { press_enter; return; }

    local client_dir="${CLIENTS_DIR}/${SELECTED_CONFIG}"
    if [[ ! -d "$client_dir" ]]; then
        log_warn "Нет клиентских конфигов для ${SELECTED_CONFIG}"
        press_enter
        return
    fi

    echo ""
    echo -e "${CYAN}Клиентские конфиги для ${SELECTED_CONFIG}:${NC}"
    echo ""

    local confs
    mapfile -t confs < <(ls "${client_dir}"/*.conf 2>/dev/null | xargs -I{} basename {} .conf || true)

    if [[ ${#confs[@]} -eq 0 ]]; then
        log_warn "Нет конфигов"
        press_enter
        return
    fi

    local i=1
    for c in "${confs[@]}"; do
        local has_qr=""
        [[ -f "${client_dir}/${c}.png" ]] && has_qr="${GREEN}[QR]${NC}"
        echo -e "  ${BOLD}$i)${NC} ${c} ${has_qr}"
        ((i++))
    done

    echo ""
    read -rp "Показать конфиг / QR? (номер или 0 для отмены): " choice

    if [[ "$choice" == "0" ]] || [[ -z "$choice" ]]; then
        return
    fi

    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#confs[@]} )); then
        log_error "Неверный выбор"
        press_enter
        return
    fi

    local peer_name="${confs[$((choice-1))]}"
    local conf_file="${client_dir}/${peer_name}.conf"

    echo ""
    echo -e "${CYAN}=== ${peer_name}.conf ===${NC}"
    cat "$conf_file"

    if [[ -f "${client_dir}/${peer_name}.png" ]] && command -v qrencode &>/dev/null; then
        echo ""
        if confirm "Показать QR-код?"; then
            qrencode -t ansiutf8 < "$conf_file"
        fi
    fi

    press_enter
}

# =============================================================================
# Меню
# =============================================================================

menu_server() {
    while true; do
        clear
        echo -e "${BOLD}${BLUE}╔══════════════════════════════════════╗${NC}"
        echo -e "${BOLD}${BLUE}║   WireGuard - Серверные конфиги      ║${NC}"
        echo -e "${BOLD}${BLUE}╚══════════════════════════════════════╝${NC}"
        echo ""
        echo -e "  ${BOLD}1)${NC} Создать новую конфигурацию"
        echo -e "  ${BOLD}2)${NC} Показать конфигурацию"
        echo -e "  ${BOLD}3)${NC} Запустить / Остановить"
        echo -e "  ${BOLD}4)${NC} Удалить конфигурацию"
        echo -e "  ${BOLD}0)${NC} Назад"
        echo ""
        read -rp "Выбор: " opt

        case "$opt" in
            1) create_server_config ;;
            2) show_server_config ;;
            3) start_stop_config ;;
            4) delete_server_config ;;
            0) return ;;
            *) log_warn "Неверный выбор" ; sleep 1 ;;
        esac
    done
}

menu_peers() {
    while true; do
        clear
        echo -e "${BOLD}${BLUE}╔══════════════════════════════════════╗${NC}"
        echo -e "${BOLD}${BLUE}║   WireGuard - Управление пирами      ║${NC}"
        echo -e "${BOLD}${BLUE}╚══════════════════════════════════════╝${NC}"
        echo ""
        echo -e "  ${BOLD}1)${NC} Добавить пир"
        echo -e "  ${BOLD}2)${NC} Добавить пиры по префиксу (массово)"
        echo -e "  ${BOLD}3)${NC} Список пиров"
        echo -e "  ${BOLD}4)${NC} Удалить пир"
        echo -e "  ${BOLD}0)${NC} Назад"
        echo ""
        read -rp "Выбор: " opt

        case "$opt" in
            1) add_peer ;;
            2) bulk_add_peers ;;
            3) list_peers ;;
            4) remove_peer ;;
            0) return ;;
            *) log_warn "Неверный выбор" ; sleep 1 ;;
        esac
    done
}

menu_clients() {
    while true; do
        clear
        echo -e "${BOLD}${BLUE}╔══════════════════════════════════════╗${NC}"
        echo -e "${BOLD}${BLUE}║   WireGuard - Клиентские конфиги     ║${NC}"
        echo -e "${BOLD}${BLUE}╚══════════════════════════════════════╝${NC}"
        echo ""
        echo -e "  ${BOLD}1)${NC} Создать / Пересоздать конфиг клиента"
        echo -e "  ${BOLD}2)${NC} Показать конфиги и QR-коды"
        echo -e "  ${BOLD}0)${NC} Назад"
        echo ""
        read -rp "Выбор: " opt

        case "$opt" in
            1) create_client_config_menu ;;
            2) show_client_configs ;;
            0) return ;;
            *) log_warn "Неверный выбор" ; sleep 1 ;;
        esac
    done
}

menu_main() {
    while true; do
        clear
        echo -e "${BOLD}${CYAN}"
        echo "  ██╗    ██╗██╗██████╗ ███████╗ ██████╗ ██╗   ██╗ █████╗ ██████╗ ██████╗ "
        echo "  ██║    ██║██║██╔══██╗██╔════╝██╔════╝ ██║   ██║██╔══██╗██╔══██╗██╔══██╗"
        echo "  ██║ █╗ ██║██║██████╔╝█████╗  ██║  ███╗██║   ██║███████║██████╔╝██║  ██║"
        echo "  ██║███╗██║██║██╔══██╗██╔══╝  ██║   ██║██║   ██║██╔══██║██╔══██╗██║  ██║"
        echo "  ╚███╔███╔╝██║██║  ██║███████╗╚██████╔╝╚██████╔╝██║  ██║██║  ██║██████╔╝"
        echo "   ╚══╝╚══╝ ╚═╝╚═╝  ╚═╝╚══════╝ ╚═════╝  ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═════╝ "
        echo -e "${NC}"
        echo -e "${BOLD}               Manager v1.0 | $(date '+%Y-%m-%d %H:%M')${NC}"
        echo ""
        echo -e "  ${BOLD}1)${NC} 🖥️  Серверные конфигурации"
        echo -e "  ${BOLD}2)${NC} 👥  Управление пирами"
        echo -e "  ${BOLD}3)${NC} 📱  Клиентские конфиги и QR"
        echo -e "  ${BOLD}4)${NC} ⚙️  Установить WireGuard"
        echo -e "  ${BOLD}5)${NC} 📊  Статус всех интерфейсов"
        echo -e "  ${BOLD}0)${NC} ❌  Выход"
        echo ""
        read -rp "Выбор: " opt

        case "$opt" in
            1) menu_server ;;
            2) menu_peers ;;
            3) menu_clients ;;
            4) install_wireguard ; press_enter ;;
            5) show_all_status ;;
            0) echo -e "\n${GREEN}До свидания!${NC}\n" ; exit 0 ;;
            *) log_warn "Неверный выбор" ; sleep 1 ;;
        esac
    done
}

show_all_status() {
    clear
    echo -e "${BOLD}${CYAN}=== Статус WireGuard интерфейсов ===${NC}"
    echo ""

    local configs
    mapfile -t configs < <(list_configs)

    if [[ ${#configs[@]} -eq 0 ]]; then
        log_warn "Нет конфигураций"
    else
        for cfg in "${configs[@]}"; do
            local status_color status_text
            if systemctl is-active --quiet "wg-quick@${cfg}" 2>/dev/null; then
                status_color=$GREEN; status_text="АКТИВЕН"
            else
                status_color=$RED; status_text="ОСТАНОВЛЕН"
            fi
            echo -e "${BOLD}[${cfg}]${NC} - ${status_color}${status_text}${NC}"

            if systemctl is-active --quiet "wg-quick@${cfg}" 2>/dev/null; then
                wg show "$cfg" 2>/dev/null | sed 's/^/  /' || true
            fi
            echo ""
        done
    fi

    press_enter
}

# =============================================================================
# Точка входа
# =============================================================================

main() {
    check_root
    mkdir -p "${CLIENTS_DIR}"
    mkdir -p "${WG_BACKUP_DIR}"
    # Перемещаем старые .bak файлы если они уже есть
    find "${WG_DIR}" -maxdepth 1 -name "*.bak.*" -exec mv {} "${WG_BACKUP_DIR}/" \; 2>/dev/null || true
    menu_main
}

main "$@"
