import logging
import os
from typing import List, Tuple, Dict
from telebot import TeleBot
from telebot.types import Message, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton, \
    CallbackQuery
from dotenv import load_dotenv

from models import gpt4o, gemma_model

# Загрузка переменных окружения
load_dotenv()
TELEGRAM_API_TOKEN = os.getenv('TELEGRAM_API_TOKEN')

if TELEGRAM_API_TOKEN is None:
    raise ValueError("Переменная окружения TELEGRAM_API_TOKEN не установлена")

# Инициализация бота
bot = TeleBot(TELEGRAM_API_TOKEN)

# Настройка клиентов моделей
model_clients = {
    'model1': gpt4o.ModelClient(),  # Замените на реальные экземпляры
    'model2': gemma_model.ModelClient(),
    # Добавьте больше моделей по необходимости
}

# Настройка логирования
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

# Словари для отслеживания истории пользователей и выбранных моделей
user_histories: Dict[int, List[Tuple[str, str]]] = {}
user_selected_models: Dict[int, str] = {}


def log_error(action: str, error: Exception) -> None:
    """Логирует сообщения об ошибках для конкретных действий."""
    logging.error(f"Ошибка во время {action}: {error}")


def get_main_keyboard() -> ReplyKeyboardMarkup:
    """Создает основную клавиатуру с кнопками."""
    markup = ReplyKeyboardMarkup(resize_keyboard=True)
    clear_button = KeyboardButton("Очистить историю")
    change_model_button = KeyboardButton("Сменить модель")
    markup.add(clear_button, change_model_button)
    return markup


def get_model_selection_markup() -> InlineKeyboardMarkup:
    """Создает инлайн-клавиатуру для выбора моделей."""
    markup = InlineKeyboardMarkup()
    for model in model_clients.keys():
        markup.add(InlineKeyboardButton(model, callback_data=f"select_{model}"))
    return markup


def escape_markdown_v2(text: str) -> str:
    """
    Экранирует специальные символы в тексте для использования с Markdown V2 в Telegram.
    """
    escape_chars = r'\_*[]()~>#+-=|{}.!'
    return ''.join(['\\' + char if char in escape_chars else char for char in text])


@bot.message_handler(commands=['start'])
def handle_start(message: Message) -> None:
    """Обрабатывает команду /start для начала диалога."""
    try:
        bot.reply_to(message, 'Привет! Я ваш чат-бот. Начните говорить со мной!', reply_markup=get_main_keyboard())
    except Exception as e:
        log_error("обработки команды /start", e)


@bot.message_handler(func=lambda message: message.text == "Очистить историю")
def handle_clear_history(message: Message) -> None:
    """Обрабатывает нажатие кнопки "Очистить историю"."""
    user_id = message.from_user.id
    try:
        user_histories.pop(user_id, None)
        bot.reply_to(message, 'Ваша история была очищена!', reply_markup=get_main_keyboard())
    except Exception as e:
        bot.reply_to(message, "Произошла ошибка при очистке истории.")
        log_error("обработки кнопки очистки истории", e)


@bot.message_handler(func=lambda message: message.text == "Сменить модель")
def handle_change_model(message: Message) -> None:
    """Обрабатывает нажатие кнопки "Сменить модель"."""
    try:
        bot.reply_to(message, 'Выберите модель:', reply_markup=get_model_selection_markup())
    except Exception as e:
        log_error("обработки кнопки смены модели", e)


@bot.callback_query_handler(func=lambda call: call.data.startswith("select_"))
def callback_select_model(call: CallbackQuery) -> None:
    """Обрабатывает выбор модели через инлайн-клавиатуру."""
    user_id = call.from_user.id
    selected_model = call.data.split("_")[1]  # Извлекаем выбранную модель из колбэк-данных
    if selected_model in model_clients:
        user_selected_models[user_id] = selected_model
        bot.answer_callback_query(call.id, f"Вы выбрали модель: {selected_model}.")
        bot.send_message(user_id, f"Модель изменена на {selected_model}.", reply_markup=get_main_keyboard())
    else:
        bot.answer_callback_query(call.id, "Указанная модель недоступна.")


@bot.message_handler(func=lambda _: True)
def handle_chat(message: Message) -> None:
    """Обрабатывает входящие сообщения и генерирует ответы с использованием выбранной пользователем модели."""
    user_id = message.from_user.id
    user_message = message.text
    history = user_histories.setdefault(user_id, [])
    sys_prompt = "Ваш системный запрос здесь"

    selected_model_key = user_selected_models.get(user_id, 'model1')  # По умолчанию 'model1'

    try:
        model_client = model_clients[selected_model_key]
        response = model_client.get_response(user_message, history, sys_prompt)
        history.append((user_message, response))
        bot.reply_to(message, escape_markdown_v2(response), reply_markup=get_main_keyboard(), parse_mode='MarkdownV2')
    except Exception as e:
        bot.reply_to(message, "Произошла ошибка. Пожалуйста, попробуйте снова.")
        log_error("обработки входящего сообщения", e)


def main() -> None:
    """Основная функция для начала опроса."""
    try:
        bot.polling(none_stop=True)
    except Exception as e:
        log_error("опрашивания бота", e)


if __name__ == '__main__':
    main()


Как указать вместо model1, model2 - GPT4o и Gemma2