import json
import os
import random
import paramiko
import telebot
import string
import textwrap
import logging
import time
import traceback
from dotenv import load_dotenv
from collections import defaultdict

# ---------------------- Загрузка Переменных Окружения ----------------------
load_dotenv()

# ---------------------- Настройка Логирования ----------------------
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[
        logging.FileHandler("bot.log"),
        logging.StreamHandler()
    ]
)

# ---------------------- Конфигурация и Данные Настроек ----------------------
TOKEN = os.getenv("BOT_TOKEN")  # Убедитесь, что у вас есть BOT_TOKEN в вашем .env файле
if not TOKEN:
    logging.error("Bot token is not set. Please set BOT_TOKEN in your environment variables.")
    raise ValueError("Bot token is not set. Please set BOT_TOKEN in your environment variables.")

bot = telebot.TeleBot(TOKEN)

# Загрузка конфигурации из config.json
try:
    with open("config.json", "r") as file:
        config = json.load(file)
    logging.info("Configuration loaded successfully.")
except FileNotFoundError:
    logging.error("config.json file not found.")
    raise

plan_names = list(config["plans"].keys())

# Файл базы данных
db_file = "db.json"

if not os.path.exists(db_file):
    with open(db_file, "w") as file:
        json.dump({"users": [], "server_counter": 0, "support_tickets": []}, file, indent=4)
    logging.info("Created new db.json file.")

with open(db_file, "r") as file:
    users_db = json.load(file)

# ---------------------- Структура для временного хранения данных о создаваемых серверах ----------------------
pending_server_creations = {}

# ---------------------- Функции Утилиты ----------------------

def load_users():
    global users_db
    try:
        with open(db_file, "r") as file:
            users_db = json.load(file)
        logging.info("User database loaded.")
    except FileNotFoundError:
        save_users()
        logging.info("User database not found. Created new db.json.")

def save_users():
    try:
        with open(db_file, "w") as file:
            json.dump(users_db, file, indent=4)
        logging.info("User database saved.")
    except Exception as e:
        logging.error(f"Failed to save user database: {e}")

def add_user(user_id, username):
    if user_id not in [user["id"] for user in users_db["users"]]:
        users_db["users"].append({
            "id": user_id,
            "username": username,
            "is_admin": False,
            "servers": [],
            "balance": 0
        })
        save_users()
        logging.info(f"Added new user: {user_id} ({username})")
        bot.send_message(user_id, "👋 Добро пожаловать! Используйте кнопки ниже для взаимодействия с ботом.")
        send_main_keyboard(user_id)

def set_admin(user_id, status=True):
    for user in users_db["users"]:
        if user["id"] == user_id:
            user["is_admin"] = status
            save_users()
            logging.info(f"Set admin status for user {user_id} to {status}")
            return True
    logging.warning(f"Attempted to set admin for non-existent user {user_id}")
    return False

def is_admin(user_id):
    admin_status = any(user["id"] == user_id and user.get("is_admin", False) for user in users_db["users"])
    logging.debug(f"User {user_id} admin status: {admin_status}")
    return admin_status

def get_unique_port():
    while True:
        port = random.randint(4000, 10000)
        existing_ports = config.get("rdp_ports", [])
        if port not in existing_ports:
            config["rdp_ports"].append(port)
            save_config()
            logging.debug(f"Assigned unique port: {port}")
            return port

def generate_random_password(length=12):
    characters = string.ascii_uppercase + '123456789'  # Разрешены: A-Z и 1-9
    if length < 2:
        raise ValueError("Password length must be at least 2 to include one letter and one digit.")
    password = [
        random.choice(string.ascii_uppercase),
        random.choice('123456789')
    ]
    password += random.choices(characters, k=length - 2)
    random.shuffle(password)
    generated_password = ''.join(password)
    logging.debug(f"Generated password: {generated_password}")
    return generated_password

def create_docker_compose(plan, server_id, version):
    plan_config = config["plans"][plan]
    rdp_port = get_unique_port()
    vnc_port = get_unique_port()
    password = generate_random_password()

    docker_compose_content = textwrap.dedent(f"""
    version: '3'
    services:
      windows:
        image: dockurr/windows
        container_name: windows{server_id}
        environment:
          VERSION: "{version}"
          DISK_SIZE: "{plan_config['DISK']}"
          RAM_SIZE: "{plan_config['RAM']}"
          CPU_CORES: "{plan_config['CPU']}"
          USERNAME: "admin"
          PASSWORD: "{password}"
        devices:
          - /dev/kvm
        cap_add:
          - NET_ADMIN
        ports:
          - "{vnc_port}:8006/tcp"
          - "{rdp_port}:3389/tcp"
          - "{rdp_port}:3389/udp"
        stop_grace_period: 2m
        restart: unless-stopped
    """)

    logging.debug(f"Generated docker-compose.yml for server {server_id}:\n{docker_compose_content}")
    return docker_compose_content, rdp_port, vnc_port, password

def save_docker_compose(ssh_client, docker_compose_content, server_id):
    try:
        sftp = ssh_client.open_sftp()
        folder_path = f"/root/windows{server_id}"
        try:
            sftp.mkdir(folder_path)
            logging.info(f"Created directory {folder_path} on server.")
        except IOError as e:
            if 'Failure' in str(e):
                logging.warning(f"Directory {folder_path} already exists.")
            else:
                logging.error(f"Failed to create directory {folder_path}: {e}")
                raise
        with sftp.open(f"{folder_path}/docker-compose.yml", "w") as file:
            file.write(docker_compose_content)
        sftp.close()
        logging.info(f"docker-compose.yml saved successfully for server {server_id}")
    except Exception as e:
        logging.error(f"Error saving docker-compose.yml: {e}")
        raise

def connect_via_ssh(server):
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh_client.connect(
            server["host"],
            username=server["username"],
            password=server["password"],
            timeout=10
        )
        logging.info(f"SSH connection established to {server['host']}")
    except Exception as e:
        logging.error(f"SSH connection failed to {server['host']}: {e}")
        return None
    return ssh_client

def execute_docker_command(server_id, command):
    server = config["servers"][0]
    ssh_client = connect_via_ssh(server)
    if ssh_client:
        try:
            full_command = f"docker {command} windows{server_id}"
            logging.info(f"Executing command: {full_command}")
            stdin, stdout, stderr = ssh_client.exec_command(full_command)
            exit_status = stdout.channel.recv_exit_status()
            output = stdout.read().decode().strip()
            error = stderr.read().decode().strip()
            if exit_status != 0:
                logging.error(f"Error executing '{full_command}': {error}")
                return False, error
            else:
                logging.info(f"Command '{full_command}' executed successfully: {output}")
                return True, output
        except Exception as e:
            logging.error(f"Error executing docker command '{command}' on server {server_id}: {e}")
            return False, str(e)
        finally:
            ssh_client.close()
            logging.info(f"SSH connection closed for server {server['host']}")
    else:
        logging.error(
            f"Cannot execute docker command '{command}' on server {server['host']} because SSH connection failed.")
        return False, "SSH connection failed."

def validate_integer(value):
    try:
        return int(value)
    except ValueError:
        return None

def validate_float(value):
    try:
        return float(value)
    except ValueError:
        return None

def validate_docker_compose(ssh_client, server_id):
    try:
        command = f"docker-compose -f /root/windows{server_id}/docker-compose.yml config"
        logging.info(f"Validating docker-compose.yml for server {server_id}")
        stdin, stdout, stderr = ssh_client.exec_command(command)
        exit_status = stdout.channel.recv_exit_status()
        if exit_status != 0:
            error = stderr.read().decode().strip()
            logging.error(f"Validation failed for server {server_id}: {error}")
            return False
        logging.info(f"Docker Compose file validated successfully for server {server_id}")
        return True
    except Exception as e:
        logging.error(f"Error validating docker-compose.yml for server {server_id}: {e}")
        return False

def save_config():
    try:
        with open("config.json", "w") as file:
            json.dump(config, file, indent=4)
        logging.info("Configuration saved.")
    except Exception as e:
        logging.error(f"Failed to save configuration: {e}")

def create_server_id():
    users_db["server_counter"] += 1
    save_users()
    return users_db["server_counter"]

# ---------------------- Ограничение по Частоте Запросов (Rate Limiting) ----------------------

user_command_times = defaultdict(list)
RATE_LIMIT = 5  # Количество команд
TIME_WINDOW = 60  # Время окна в секундах

def is_rate_limited(user_id):
    current_time = time.time()
    user_command_times[user_id] = [cmd_time for cmd_time in user_command_times[user_id] if
                                   current_time - cmd_time < TIME_WINDOW]
    if len(user_command_times[user_id]) >= RATE_LIMIT:
        return True
    user_command_times[user_id].append(current_time)
    return False

def rate_limit_decorator(handler):
    def wrapper(message, *args, **kwargs):
        if is_rate_limited(message.chat.id):
            bot.send_message(message.chat.id, "⚠️ Слишком много запросов. Пожалуйста, попробуйте позже.")
            logging.warning(f"User {message.chat.id} is rate limited.")
            return
        return handler(message, *args, **kwargs)

    return wrapper

# ---------------------- Функции Клавиатур ----------------------

def send_main_keyboard(chat_id):
    markup = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=3)
    # Добавляем только 3 нужных кнопки с эмодзи и красивым текстом
    markup.add(
        telebot.types.KeyboardButton("📂 Меню"),
        telebot.types.KeyboardButton("🛒 Товары"),
        telebot.types.KeyboardButton("📞 Поддержка")
    )
    bot.send_message(chat_id, "📚 **Главное меню:** Выберите опцию ниже.", reply_markup=markup, parse_mode="Markdown")

# ---------------------- Обработчики Команд Бота ----------------------

@bot.message_handler(commands=["start"])
def start_command(message):
    add_user(message.chat.id, message.from_user.username)
    logging.info(f"User {message.chat.id} started the bot.")
    send_main_keyboard(message.chat.id)  # Отправка основной клавиатуры при старте

@bot.message_handler(commands=["help"])
def help_command(message):
    help_text = (
        "🛠️ **Доступные команды:**\n"
        "🛒 *Товары* - Приобрести сервер\n"
        "📂 *Меню* - Управление вашими серверами\n"
        "💰 *Профиль* - Просмотр вашего профиля\n"
        "📞 *Поддержка* - Связаться со службой поддержки\n"
        "🔧 *Админ-панель* - Доступна только администраторам"
    )
    bot.send_message(message.chat.id, help_text, parse_mode="Markdown")
    send_main_keyboard(message.chat.id)  # Возвращаем основную клавиатуру после помощи
    logging.info(f"Sent help to user {message.chat.id}.")

@bot.message_handler(commands=["profile"])
@rate_limit_decorator
def profile_command(message):
    user = next((user for user in users_db["users"] if user["id"] == message.chat.id), None)
    if user:
        server_count = len(user["servers"])
        servers = "\n".join([f"• {server['name']} (Статус: {server['status']})" for server in user["servers"]]) or "Нет серверов"
        profile_text = (
            f"🧑‍💼 **Ваш профиль:**\n\n"
            f"✅ *ID пользователя:* `{user['id']}`\n"
            f"💵 *Баланс:* `${user['balance']}`\n"
            f"🖥️ *Серверов:* {server_count}\n\n"
            f"**Список серверов:**\n{servers}"
        )
        bot.send_message(message.chat.id, profile_text, parse_mode="Markdown")
        logging.info(f"Displayed profile for user {message.chat.id}.")
    else:
        bot.send_message(message.chat.id, "🚫 Пользователь не найден.")
        logging.warning(f"Profile check for non-existent user {message.chat.id}.")

@bot.message_handler(commands=["balance"])
def balance_redirect(message):
    bot.send_message(message.chat.id, "⚠️ Эта команда была заменена на /profile. Используйте /profile для просмотра вашего баланса.")
    logging.info(f"User {message.chat.id} attempted to use deprecated /balance command.")

@bot.message_handler(commands=["shop"])
@rate_limit_decorator
def shop_command(message):
    user = next((user for user in users_db["users"] if user["id"] == message.chat.id), None)
    if not user:
        add_user(message.chat.id, message.from_user.username)
        user = next((user for user in users_db["users"] if user["id"] == message.chat.id), None)

    markup = telebot.types.InlineKeyboardMarkup(row_width=1)
    for plan in plan_names:
        markup.add(telebot.types.InlineKeyboardButton(text=f"{plan.capitalize()} 🛡️", callback_data=f"plan:{plan}"))
    bot.send_message(message.chat.id, "🛒 **Выберите тарифный план:**", reply_markup=markup, parse_mode="Markdown")
    logging.info(f"Displayed shop to user {message.chat.id}.")

@bot.message_handler(commands=["admin"])
def admin_command(message):
    if is_admin(message.chat.id):
        admin_text = (
            "🔧 **Админ-панель:**\n"
            "👥 /setadmin [ID] - Назначить пользователя админом\n"
            "🚫 /removeadmin [ID] - Удалить пользователя из админов\n"
            "📋 /listusers - Показать всех пользователей\n"
            "💸 /givebalance [ID] [AMOUNT] - Выдать баланс пользователю\n"
            "📨 /viewtickets - Просмотреть все запросы в поддержку"
        )
        bot.send_message(message.chat.id, admin_text, parse_mode="Markdown")
        logging.info(f"Displayed admin panel to admin user {message.chat.id}.")
    else:
        bot.send_message(message.chat.id, "🚫 У вас нет доступа к админ-панели")
        logging.warning(f"User {message.chat.id} attempted to access admin panel without permissions.")

# Обработчик текстовых команд из клавиатуры
@bot.message_handler(func=lambda message: message.text in ["📂 Меню", "🛒 Товары", "📞 Поддержка"])
def handle_text_commands(message):
    command = message.text
    if command == "📂 Меню":
        menu_command(message)
    elif command == "🛒 Товары":
        shop_command(message)
    elif command == "📞 Поддержка":
        support_command(message)
    else:
        bot.send_message(message.chat.id, "❓ Неизвестная команда. Используйте кнопки ниже для взаимодействия с ботом.")
        logging.warning(f"User {message.chat.id} sent an unknown command: {message.text}")

@bot.message_handler(commands=["menu"])
@rate_limit_decorator
def menu_command(message):
    user = next((user for user in users_db["users"] if user["id"] == message.chat.id), None)
    if user:
        if user["servers"]:
            # Создаем клавиатуру с серверами пользователя
            markup = telebot.types.InlineKeyboardMarkup(row_width=1)
            for server in user["servers"]:
                markup.add(
                    telebot.types.InlineKeyboardButton(
                        text=f"{server['name']} 🖥️",
                        callback_data=f"select_server:{server['id']}"
                    )
                )
            bot.send_message(message.chat.id, "📂 **Выберите сервер для управления:**", reply_markup=markup, parse_mode="Markdown")
            logging.info(f"Displayed server list to user {message.chat.id} for selection.")
        else:
            bot.send_message(message.chat.id, "🚫 У вас нет серверов.")
            logging.info(f"User {message.chat.id} has no servers.")
    else:
        bot.send_message(message.chat.id, "🚫 У вас нет доступа.")
        logging.warning(f"Non-existent user {message.chat.id} attempted to access menu.")

# ---------------------- Обработчики Callback Query ----------------------

@bot.callback_query_handler(func=lambda call: call.data.startswith("plan:"))
def plan_callback(call):
    plan = call.data.split(":")[1]

    user = next((user for user in users_db["users"] if user["id"] == call.message.chat.id), None)
    if user and user["balance"] >= config["plans"][plan]["price"]:
        # Перейти к выбору версии ОС
        versions = ["2022", "2025", "2019", "win10", "win11"]
        version_markup = telebot.types.InlineKeyboardMarkup(row_width=1)

        for version in versions:
            if version.startswith("win"):
                version_display = f"Windows {version[-2:].upper()}"
            else:
                version_display = f"Windows Server {version}"
            version_markup.add(
                telebot.types.InlineKeyboardButton(
                    text=f"{version_display} 🖥️",
                    callback_data=f"version:{version}:plan:{plan}"
                )
            )

        bot.send_message(call.message.chat.id, "📋 **Выберите версию Windows:**", reply_markup=version_markup,
                         parse_mode="Markdown")
        logging.info(f"User {call.message.chat.id} selected plan '{plan}'. Prompting for OS version.")
    else:
        bot.send_message(call.message.chat.id, "🚫 У вас недостаточно средств для покупки этого тарифа.")
        logging.warning(f"User {call.message.chat.id} has insufficient balance for plan '{plan}'.")
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("select_server:"))
def select_server_callback(call):
    user_id = call.message.chat.id
    server_id = validate_integer(call.data.split(":")[1])

    if server_id is None:
        bot.send_message(user_id, "⚠️ Некорректный ID сервера.")
        logging.error(f"Invalid server ID in select_server callback: {call.data}")
        bot.answer_callback_query(call.id)
        return

    user = next((user for user in users_db["users"] if user["id"] == user_id), None)
    if not user:
        bot.send_message(user_id, "🚫 Пользователь не найден.")
        logging.error(f"User {user_id} not found in select_server_callback.")
        bot.answer_callback_query(call.id)
        return

    server = next((s for s in user["servers"] if s["id"] == server_id), None)
    if not server:
        bot.send_message(user_id, "🚫 Сервер не найден.")
        logging.error(f"Server ID {server_id} not found for user {user_id} in select_server_callback.")
        bot.answer_callback_query(call.id)
        return

    # Создаем inline клавиатуру для управления выбранным сервером
    control_markup = telebot.types.InlineKeyboardMarkup(row_width=2)
    control_markup.add(
        telebot.types.InlineKeyboardButton(text="▶️ Запустить", callback_data=f"start:{server_id}"),
        telebot.types.InlineKeyboardButton(text="🛑 Остановить", callback_data=f"stop:{server_id}"),
        telebot.types.InlineKeyboardButton(text="🔄 Перезапустить", callback_data=f"restart:{server_id}"),
        telebot.types.InlineKeyboardButton(text="❌ Удалить", callback_data=f"delete:{server_id}")
    )

    # Отправляем детали сервера и кнопки управления
    server_details = compose_server_details(server)
    bot.send_message(
        user_id,
        server_details,
        reply_markup=control_markup,
        parse_mode="Markdown"
    )
    logging.info(f"Displayed control options for server '{server['name']}' (ID: {server_id}) to user {user_id}.")
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("version:"))
def version_callback(call):
    data_parts = call.data.split(":")
    if len(data_parts) != 4:
        bot.send_message(call.message.chat.id, "⚠️ Некорректные данные. Попробуйте снова.")
        logging.error(f"Incorrect version callback data from user {call.message.chat.id}: {call.data}")
        bot.answer_callback_query(call.id)
        return

    _, version, _, plan = data_parts

    supported_versions = ["2022", "2025", "2019", "win10", "win11"]  # Ensure '2025' is included

    if version not in supported_versions:
        bot.send_message(call.message.chat.id, "🚫 Выбранная версия Windows не поддерживается.")
        logging.warning(f"User {call.message.chat.id} selected unsupported version '{version}'.")
        bot.answer_callback_query(call.id)
        return

    # Сохранить данные о создаваемом сервере в временном хранилище
    pending_server_creations[call.message.chat.id] = {'plan': plan, 'version': version}

    # Запросить у пользователя имя сервера
    bot.send_message(call.message.chat.id, "📝 **Введите имя для вашего сервера:**", parse_mode="Markdown")
    bot.register_next_step_handler(call.message, handle_server_name)

    logging.info(
        f"User {call.message.chat.id} selected version '{version}' for plan '{plan}'. Prompting for server name.")
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("start:") or
                                              call.data.startswith("stop:") or
                                              call.data.startswith("restart:") or
                                              call.data.startswith("delete:"))
def server_control_callback(call):
    user_id = call.message.chat.id
    user = next((user for user in users_db["users"] if user["id"] == user_id), None)
    if not user:
        bot.send_message(user_id, "🚫 Пользователь не найден.")
        logging.error(f"User {user_id} not found while controlling server.")
        bot.answer_callback_query(call.id)
        return

    parts = call.data.split(":")
    action = parts[0]
    server_id = validate_integer(parts[1])

    if server_id is None:
        bot.send_message(user_id, "⚠️ Некорректный ID сервера.")
        logging.error(f"Invalid server ID in callback: {call.data}")
        bot.answer_callback_query(call.id)
        return

    server = next((s for s in user["servers"] if s["id"] == server_id), None)
    if not server:
        bot.send_message(user_id, "🚫 Сервер не найден.")
        logging.error(f"Server ID {server_id} not found for user {user_id}.")
        bot.answer_callback_query(call.id)
        return

    if action == "delete":
        # Подтвердить удаление
        markup = telebot.types.InlineKeyboardMarkup(row_width=2)
        markup.add(
            telebot.types.InlineKeyboardButton(text="✅ Да", callback_data=f"confirm_delete:{server_id}"),
            telebot.types.InlineKeyboardButton(text="❌ Нет", callback_data="cancel_delete")
        )
        bot.send_message(
            user_id,
            f"❗ Вы уверены, что хотите удалить сервер '{server['name']}'?",
            reply_markup=markup
        )
        logging.info(f"User {user_id} initiated delete for server '{server['name']}' (ID: {server_id}).")
        bot.answer_callback_query(call.id, "⚠️ Подтвердите удаление сервера.")
        return

    # Выполнить действие (start, stop, restart)
    success, response = execute_docker_command(server_id, action)
    if success:
        # Обновить статус сервера
        if action == "start":
            server["status"] = "Запущен"
        elif action == "stop":
            server["status"] = "Остановлен"
        elif action == "restart":
            server["status"] = "Перезапущен"
        save_users()

        # Отправить подтверждение
        status_text = server["status"]
        bot.send_message(user_id, f"✅ Сервер '{server['name']}' успешно {status_text.lower()}.")

        logging.info(f"User {user_id} {action}ed server '{server['name']}' (ID: {server_id}).")

        # Обновить сообщение с деталями сервера
        update_server_message(user_id, server)
    else:
        # Отправить ошибку
        bot.send_message(user_id, f"⚠️ Не удалось {action}ить сервер '{server['name']}'.\nОшибка: {response}")
        logging.error(f"Failed to {action} server '{server['name']}' (ID: {server_id}) for user {user_id}: {response}")

    bot.answer_callback_query(call.id, f"✅ Сервер '{server['name']}' {action}ирован.")

@bot.callback_query_handler(func=lambda call: call.data.startswith("confirm_delete:") or
                                              call.data == "cancel_delete")
def delete_server_confirm_callback(call):
    user_id = call.message.chat.id
    user = next((user for user in users_db["users"] if user["id"] == user_id), None)
    if not user:
        bot.send_message(user_id, "🚫 Пользователь не найден.")
        logging.error(f"User {user_id} not found while confirming delete.")
        bot.answer_callback_query(call.id)
        return

    if call.data.startswith("confirm_delete:"):
        server_id = validate_integer(call.data.split(":")[1])
        if server_id is None:
            bot.send_message(user_id, "⚠️ Некорректный ID сервера.")
            logging.error(f"Invalid server ID in confirm_delete callback: {call.data}")
            bot.answer_callback_query(call.id)
            return

        server = next((s for s in user["servers"] if s["id"] == server_id), None)
        if not server:
            bot.send_message(user_id, "🚫 Сервер не найден.")
            logging.error(f"Server ID {server_id} not found for user {user_id}.")
            bot.answer_callback_query(call.id)
            return

        # Остановить Docker контейнер
        success_stop, response_stop = execute_docker_command(server_id, "stop")
        if not success_stop:
            bot.send_message(user_id,
                             f"⚠️ Не удалось остановить сервер '{server['name']}'.\nОшибка: {response_stop}")
            logging.error(f"Failed to stop server '{server['name']}' (ID: {server_id}) for user {user_id}: {response_stop}")
            bot.answer_callback_query(call.id)
            return

        # Удалить Docker контейнер
        success_rm, response_rm = execute_docker_command(server_id, "rm")
        if not success_rm:
            bot.send_message(user_id,
                             f"⚠️ Не удалось удалить сервер '{server['name']}'.\nОшибка: {response_rm}")
            logging.error(f"Failed to remove server '{server['name']}' (ID: {server_id}) for user {user_id}: {response_rm}")
            bot.answer_callback_query(call.id)
            return

        # Удалить файлы docker-compose.yml с сервера
        server_info = config["servers"][0]
        ssh_client = connect_via_ssh(server_info)
        if ssh_client:
            try:
                command = f"rm -rf /root/windows{server_id}"
                logging.info(f"Removing server files for server '{server['name']}' (ID: {server_id}): {command}")
                stdin, stdout, stderr = ssh_client.exec_command(command)
                exit_status = stdout.channel.recv_exit_status()
                if exit_status == 0:
                    logging.info(f"Removed server files for server '{server['name']}' (ID: {server_id}).")
                else:
                    error = stderr.read().decode().strip()
                    logging.error(
                        f"Failed to remove server files for server '{server['name']}' (ID: {server_id}): {error}")
            except Exception as e:
                logging.error(f"Failed to remove server files for server '{server['name']}' (ID: {server_id}): {e}")
            finally:
                ssh_client.close()
        else:
            logging.error(
                f"SSH connection failed while deleting server files for server '{server['name']}' (ID: {server_id}).")

        # Удалить сервер из списка пользователя
        user["servers"] = [s for s in user["servers"] if s["id"] != server_id]
        save_users()

        # Удалить сообщение с сервера
        try:
            bot.delete_message(chat_id=user_id, message_id=server["control_message_id"])
            logging.info(f"Deleted control message for server '{server['name']}' (ID: {server_id}) for user {user_id}.")
        except Exception as e:
            logging.error(f"Failed to delete message for server '{server['name']}' (ID: {server_id}): {e}")

        # Отправить подтверждение
        bot.send_message(user_id, f"✅ Сервер '{server['name']}' успешно удален.")
        logging.info(f"Server '{server['name']}' (ID: {server_id}) deleted for user {user_id}.")

    elif call.data == "cancel_delete":
        bot.send_message(user_id, "❌ Удаление сервера отменено.")
        logging.info(f"User {user_id} canceled server deletion.")

    bot.answer_callback_query(call.id)

# ---------------------- Дополнительные Обработчики и Функции ----------------------

def handle_server_name(message):
    user_id = message.chat.id

    # Проверить, есть ли у пользователя незавершённый процесс создания сервера
    if user_id not in pending_server_creations:
        bot.send_message(user_id, "⚠️ В процессе создания сервера произошла ошибка. Пожалуйста, попробуйте снова.")
        logging.error(f"User {user_id} tried to set server name without pending creation.")
        return

    plan = pending_server_creations[user_id]['plan']
    version = pending_server_creations[user_id]['version']
    server_name = message.text.strip()

    # Валидация имени сервера
    if not server_name:
        bot.send_message(user_id, "⚠️ Имя сервера не может быть пустым. Пожалуйста, попробуйте снова.")
        logging.warning(f"User {user_id} provided empty server name.")
        return

    # Проверка уникальности имени сервера для данного пользователя
    user = next((user for user in users_db["users"] if user["id"] == user_id), None)
    if user:
        if any(s['name'] == server_name for s in user['servers']):
            bot.send_message(user_id, "⚠️ Сервер с таким именем уже существует. Пожалуйста, выберите другое имя.")
            logging.warning(f"User {user_id} attempted to use duplicate server name '{server_name}'.")
            return
    else:
        bot.send_message(user_id, "🚫 Пользователь не найден.")
        logging.error(f"User {user_id} not found while setting server name.")
        return

    # Удалить запись из временного хранилища
    del pending_server_creations[user_id]

    # Продолжить процесс создания сервера с использованием заданного имени
    try:
        server_id = create_server_id()
        docker_compose_content, rdp_port, vnc_port, password = create_docker_compose(plan, server_id, version)
    except Exception as e:
        bot.send_message(user_id, "⚠️ Ошибка при создании конфигурации сервера.")
        logging.error(f"Failed to create docker compose for server {server_id} and user {user_id}: {e}")
        logging.error(traceback.format_exc())
        return

    server_info = config["servers"][0]
    ssh_client = connect_via_ssh(server_info)
    if not ssh_client:
        bot.send_message(user_id, "🚫 Не удалось подключиться к серверу. Пожалуйста, попробуйте позже.")
        logging.error(f"SSH connection failed while creating server {server_id} for user {user_id}.")
        return

    try:
        save_docker_compose(ssh_client, docker_compose_content, server_id)
    except Exception as e:
        bot.send_message(user_id, "⚠️ Не удалось сохранить конфигурацию сервера.")
        logging.error(f"Failed to save docker-compose.yml for server {server_id}: {e}")
        logging.error(traceback.format_exc())
        ssh_client.close()
        return

    # Проверка корректности docker-compose.yml
    if not validate_docker_compose(ssh_client, server_id):
        bot.send_message(user_id, "⚠️ Некорректный docker-compose.yml. Пожалуйста, свяжитесь с поддержкой.")
        ssh_client.close()
        return

    # Запуск Docker контейнера
    try:
        logging.info(f"Starting Docker container for server {server_id}.")
        stdin, stdout, stderr = ssh_client.exec_command(f"cd /root/windows{server_id} && docker-compose up -d")
        exit_status = stdout.channel.recv_exit_status()
        output = stdout.read().decode().strip()
        error = stderr.read().decode().strip()
        if exit_status != 0:
            logging.error(f"Failed to start Docker container for server {server_id}: {error}")
            bot.send_message(user_id, "⚠️ Не удалось запустить контейнер. Пожалуйста, попробуйте позже.")
            ssh_client.close()
            return
        logging.info(f"Docker container for server {server_id} started successfully: {output}")
    except Exception as e:
        logging.error(f"Error starting Docker container for server {server_id}: {e}")
        logging.error(traceback.format_exc())
        bot.send_message(user_id, "⚠️ Не удалось запустить контейнер. Пожалуйста, попробуйте позже.")
        ssh_client.close()
        return

    ssh_client.close()

    # Обновление данных пользователя
    server_entry = {
        "id": server_id,
        "name": server_name,  # Добавлено имя сервера
        "plan": plan,
        "status": "Запущен",
        "rdp_port": rdp_port,
        "vnc_port": vnc_port,
        "username": "admin",
        "password": password,
        "control_message_id": None  # Будет обновлено после отправки сообщения
    }
    if user:
        user["servers"].append(server_entry)
    else:
        bot.send_message(user_id, "🚫 Пользователь не найден.")
        logging.error(f"User {user_id} not found while updating server {server_id}.")
        return

    # Списание стоимости сервера из баланса пользователя
    user["balance"] -= config["plans"][plan]["price"]
    save_users()

    # Формирование сообщения с деталями сервера
    server_details = (
        f"✅ **Сервер создан!**\n\n"
        f"**🖥️ Имя сервера:** `{server_name}`\n"
        f"👤 **Пользователь:** `admin`\n"
        f"🌐 **RDP IP:** {server_info['host']}:{rdp_port}\n"
        f"🌐 **VNC:** [http://{server_info['host']}:{vnc_port}](http://{server_info['host']}:{vnc_port})\n"
        f"🔑 **Пароль:** `{password}`\n\n"
        f"**🛡️ Характеристики:**\n"
        f"- **Диск:** {config['plans'][plan]['DISK']}\n"
        f"- **ОЗУ:** {config['plans'][plan]['RAM']}\n"
        f"- **CPU:** {config['plans'][plan]['CPU']} ядра\n"
        f"- **Цена:** `${config['plans'][plan]['price']}`"
    )

    # Создать inline клавиатуру для управления сервером
    control_markup = telebot.types.InlineKeyboardMarkup(row_width=4)
    control_markup.add(
        telebot.types.InlineKeyboardButton(text="▶️ Запустить", callback_data=f"start:{server_id}"),
        telebot.types.InlineKeyboardButton(text="🛑 Остановить", callback_data=f"stop:{server_id}"),
        telebot.types.InlineKeyboardButton(text="🔄 Перезапустить", callback_data=f"restart:{server_id}"),
        telebot.types.InlineKeyboardButton(text="❌ Удалить", callback_data=f"delete:{server_id}")
    )

    # Отправить сообщение с деталями сервера и контролами
    sent_message = bot.send_message(
        user_id,
        server_details,
        reply_markup=control_markup,
        parse_mode="Markdown"
    )
    logging.info(f"Server {server_id} created for user {user_id} with name '{server_name}'.")

    # Сохранить message_id в сервере для дальнейшего обновления
    server_entry["control_message_id"] = sent_message.message_id
    save_users()

def send_server_control_message(chat_id, server):
    # Проверить, есть ли уже сообщение для сервера
    if server.get("control_message_id"):
        try:
            bot.edit_message_text(
                chat_id=chat_id,
                message_id=server["control_message_id"],
                text=compose_server_details(server),
                reply_markup=compose_server_markup(server),
                parse_mode="Markdown"
            )
            logging.info(
                f"Updated control message for server '{server['name']}' (ID: {server['id']}) for user {chat_id}.")
        except Exception as e:
            logging.error(f"Failed to edit message for server {server['id']}: {e}")
    else:
        # Отправить новое сообщение для сервера
        sent_message = bot.send_message(
            chat_id,
            compose_server_details(server),
            reply_markup=compose_server_markup(server),
            parse_mode="Markdown"
        )
        logging.info(f"Sent control message for server '{server['name']}' (ID: {server['id']}) to user {chat_id}.")

        # Сохранить message_id
        server["control_message_id"] = sent_message.message_id
        save_users()

def compose_server_details(server):
    server_info = config["servers"][0]
    plan_info = config["plans"][server['plan']]
    status_text = server["status"]

    return (
        f"🔧 **Управление сервером '{server['name']}':**\n\n"
        f"**🛡️ Тарифный план:** {server['plan'].capitalize()}\n\n"
        f"**🛠️ Характеристики:**\n"
        f"- **Диск:** {plan_info['DISK']}\n"
        f"- **ОЗУ:** {plan_info['RAM']}\n"
        f"- **CPU:** {plan_info['CPU']} ядра\n"
        f"- **Цена:** `${plan_info['price']}`\n\n"
        f"**📌 Статус:** {status_text}\n"
        f"**🌐 RDP IP:** {server_info['host']}:{server['rdp_port']}\n"
        f"**🌐 VNC:** [http://{server_info['host']}:{server['vnc_port']}](http://{server_info['host']}:{server['vnc_port']})\n"
        f"👤 **Пользователь:** `admin`\n"
        f"🔑 **Пароль:** `{server['password']}`"
    )

def compose_server_markup(server):
    return telebot.types.InlineKeyboardMarkup(row_width=4).add(
        telebot.types.InlineKeyboardButton(text="▶️ Запустить", callback_data=f"start:{server['id']}"),
        telebot.types.InlineKeyboardButton(text="🛑 Остановить", callback_data=f"stop:{server['id']}"),
        telebot.types.InlineKeyboardButton(text="🔄 Перезапустить", callback_data=f"restart:{server['id']}"),
        telebot.types.InlineKeyboardButton(text="❌ Удалить", callback_data=f"delete:{server['id']}")
    )

def update_server_message(chat_id, server):
    if server.get("control_message_id"):
        try:
            bot.edit_message_text(
                chat_id=chat_id,
                message_id=server["control_message_id"],
                text=compose_server_details(server),
                reply_markup=compose_server_markup(server),
                parse_mode="Markdown"
            )
            logging.info(f"Updated server details for '{server['name']}' (ID: {server['id']}) for user {chat_id}.")
        except Exception as e:
            logging.error(f"Failed to edit message for server {server['id']}: {e}")
    else:
        send_server_control_message(chat_id, server)

# ---------------------- Функция Поддержки (Support Command) ----------------------
@bot.message_handler(commands=["support"])
@rate_limit_decorator
def support_command(message):
    # Пример простой поддержки: отправка контакта или инструкции
    support_text = (
        "📞 **Служба поддержки:**\n\n"
        "Если у вас возникли вопросы или проблемы, свяжитесь с нами по следующим каналам:\n"
        "✉️ *Email:* support@example.com\n"
        "📱 *Телефон:* +1234567890\n\n"
        "Вы также можете описать вашу проблему здесь, и мы ответим вам в ближайшее время."
    )
    bot.send_message(message.chat.id, support_text, parse_mode="Markdown")
    send_main_keyboard(message.chat.id)
    logging.info(f"Sent support information to user {message.chat.id}.")

# ---------------------- Обработчик Неизвестных Команд ----------------------

@bot.message_handler(func=lambda message: True)
def unknown_command(message):
    bot.send_message(message.chat.id, "❓ Неизвестная команда. Используйте кнопки ниже для взаимодействия с ботом.")
    logging.warning(f"User {message.chat.id} sent an unknown command: {message.text}")

# ---------------------- Функция Главного Запуска Бота ----------------------

def main():
    load_users()
    logging.info("Bot is polling...")
    while True:
        try:
            bot.polling(none_stop=True)
        except Exception as e:
            logging.error(f"Bot polling failed: {e}")
            logging.error(traceback.format_exc())
            time.sleep(15)  # Wait before restarting polling

if __name__ == "__main__":
    main()

Перепиши этот биллинг бота в веб версию.