import tkinter as tk
from tkinter import messagebox, ttk
import math
import logging
import json
import os
from typing import Union, List, Dict
from datetime import datetime

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

HISTORY_FILE = 'history.json'
LOCALE_FILES = {
    'ru': 'locales/ru.json',
    'en': 'locales/en.json'
}
DEFAULT_LOCALE = 'en'

# Определение цветовых схем для светлой и тёмной тем
LIGHT_THEME = {
    "bg": "#ffffff",
    "fg": "#000000",
    "button_bg": "#e0e0e0",
    "button_fg": "#000000",
    "entry_bg": "#ffffff",
    "entry_fg": "#000000",
    "history_bg": "#f0f0f0",
    "history_fg": "#000000",
    "result_fg": "#0000ff"
}

# Улучшенная тёмная тема с повышенным контрастом
DARK_THEME = {
    "bg": "#1e1e1e",           # Очень тёмно-серый фон
    "fg": "#ffffff",           # Белый текст
    "button_bg": "#333333",    # Ещё более тёмно-серый фон кнопок
    "button_fg": "#ffffff",    # Белый текст на кнопках
    "entry_bg": "#2a2a2a",      # Тёмные поля ввода
    "entry_fg": "#ffffff",     # Белый текст в полях ввода
    "history_bg": "#2a2a2a",    # Тёмный фон истории
    "history_fg": "#ffffff",    # Белый текст истории
    "result_fg": "#00ff00"      # Ярко-зелёный текст результата
}

class CalculatorApp:
    def __init__(self, root):
        self.root = root
        self.current_locale = 'ru'
        self.locale = self.load_locale(self.current_locale)
        self.history: List[Dict] = []
        self.current_theme = "Light"  # По умолчанию светлая тема
        self.style = ttk.Style()
        self.create_widgets()
        self.load_history()
        self.bind_shortcuts()
        self.apply_theme(self.current_theme)

    def load_locale(self, locale_code: str) -> dict:
        locale_file = LOCALE_FILES.get(locale_code, LOCALE_FILES[DEFAULT_LOCALE])
        try:
            with open(locale_file, 'r', encoding='utf-8-sig') as f:
                locale_data = json.load(f)
                logging.info(f"Загружена локализация: {locale_code}")
                return locale_data
        except FileNotFoundError:
            logging.error(f"Файл локализации не найден: {locale_file}. Используется {DEFAULT_LOCALE}.")
            if locale_code != DEFAULT_LOCALE:
                return self.load_locale(DEFAULT_LOCALE)
            return {}
        except json.JSONDecodeError as e:
            logging.error(f"Ошибка парсинга файла локализации: {locale_file} - {e}. Используется {DEFAULT_LOCALE}.")
            if locale_code != DEFAULT_LOCALE:
                return self.load_locale(DEFAULT_LOCALE)
            return {}

    def translate(self, key: str) -> str:
        return self.locale.get(key, key)

    # Арифметические операции
    def add(self, x: float, y: float) -> float:
        return x + y

    def subtract(self, x: float, y: float) -> float:
        return x - y

    def multiply(self, x: float, y: float) -> float:
        return x * y

    def divide(self, x: float, y: float) -> float:
        if y == 0:
            raise ValueError(self.translate("На ноль делить нельзя"))
        return x / y

    def power(self, x: float, y: float) -> float:
        return x ** y

    def sqrt(self, x: float) -> float:
        if x < 0:
            raise ValueError(self.translate("Нельзя вычислить квадратный корень из отрицательного числа"))
        return math.sqrt(x)

    def is_valid_number(self, entry: str) -> bool:
        try:
            float(entry)
            return True
        except ValueError:
            return False

    def save_history(self) -> None:
        try:
            with open(HISTORY_FILE, 'w', encoding='utf-8') as f:
                json.dump(self.history, f, ensure_ascii=False, indent=4)
            logging.info(self.translate("История операций сохранена"))
        except Exception as e:
            logging.error(f"Ошибка при сохранении истории: {e}")

    def load_history(self) -> None:
        if os.path.exists(HISTORY_FILE):
            try:
                with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
                    self.history = json.load(f)
                self.update_history_display()
                logging.info(self.translate("История операций загружена"))
            except Exception as e:
                logging.error(f"Ошибка при загрузке истории: {e}")
                self.history = []
        else:
            self.history = []

    def calculate(self, num1: float, num2: Union[float, None], operation: str) -> float:
        operations_map = {
            "add": self.add,
            "subtract": self.subtract,
            "multiply": self.multiply,
            "divide": self.divide,
            "power": self.power,
            "sqrt": self.sqrt
        }
        operation_func = operations_map.get(operation)
        if not operation_func:
            raise ValueError(self.translate("Неверная операция"))
        if operation == "sqrt":
            return operation_func(num1)
        return operation_func(num1, num2)

    def perform_operation(self, event: Union[tk.Event, None] = None) -> None:
        try:
            num1_str = self.entry1.get().strip()
            if not self.is_valid_number(num1_str):
                raise ValueError(self.translate("Первое число должно быть числом"))
            num1 = float(num1_str)

            operation_display = self.selected_operation.get()
            operation = self.display_operation_map.get(operation_display, None)
            if operation is None:
                raise ValueError(self.translate("Неверная операция"))

            if operation == "sqrt":
                result = self.calculate(num1, None, operation)
                num2 = None
                self.entry2.delete(0, tk.END)
                expression = f"√({num1})"
            else:
                num2_str = self.entry2.get().strip()
                if not self.is_valid_number(num2_str):
                    raise ValueError(self.translate("Второе число должно быть числом"))
                num2 = float(num2_str)
                result = self.calculate(num1, num2, operation)
                symbol = self.operation_symbol(operation)
                expression = f"{num1} {symbol} {num2}"

            self.result_label.config(text=f"{self.translate('Результат')}: {result}")

            # Форматирование записи для истории
            time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            history_entry = {
                "time": time_now,
                "operation": self.get_operation_name(operation),
                "expression": expression,
                "result": result
            }
            self.history.append(history_entry)
            display_entry = f"{history_entry['time']} - {history_entry['operation']}: {history_entry['expression']} = {history_entry['result']}"
            self.history_text.config(state=tk.NORMAL)
            self.history_text.insert(tk.END, display_entry + '\n')
            self.history_text.see(tk.END)
            self.history_text.config(state=tk.DISABLED)
            self.save_history()
            logging.info(f"{self.translate('Выполнена операция')}: {history_entry['operation']} {self.translate('с числами')} {num1} и {num2 if num2 is not None else ''}. {self.translate('Результат')}: {result}")
        except ValueError as e:
            messagebox.showerror(self.translate("Ошибка"), str(e))
            logging.error(f"{self.translate('Ошибка ввода данных')}: {str(e)}")
        except Exception as e:
            messagebox.showerror(self.translate("Ошибка"), self.translate("Неверный ввод данных"))
            logging.error(f"{self.translate('Непредвиденная ошибка')}: {str(e)}")

    def operation_symbol(self, operation: str) -> str:
        symbols = {
            "add": '+',
            "subtract": '-',
            "multiply": '*',
            "divide": '/',
            "power": '^',
        }
        return symbols.get(operation, '')

    def get_operation_name(self, operation: str) -> str:
        operation_names = {
            "add": self.translate("Сложение"),
            "subtract": self.translate("Вычитание"),
            "multiply": self.translate("Умножение"),
            "divide": self.translate("Деление"),
            "power": self.translate("Возведение в степень"),
            "sqrt": self.translate("Квадратный корень")
        }
        return operation_names.get(operation, operation)

    def clear_entries(self, event: Union[tk.Event, None] = None) -> None:
        self.entry1.delete(0, tk.END)
        self.entry2.delete(0, tk.END)
        self.result_label.config(text=f"{self.translate('Результат')}: ")

    def on_operation_change(self, event: Union[tk.Event, None] = None) -> None:
        if self.display_operation_map.get(self.selected_operation.get(), "") == "sqrt":
            self.entry2.config(state=tk.DISABLED)
        else:
            self.entry2.config(state=tk.NORMAL)

    def show_info(self, event: Union[tk.Event, None] = None) -> None:
        messagebox.showinfo(
            self.translate("О калькуляторе"),
            self.translate("Этот калькулятор выполнен на Python с использованием библиотеки Tkinter.")
        )

    def show_shortcuts(self, event: Union[tk.Event, None] = None) -> None:
        shortcuts_text = (
            f"{self.translate('Ctrl+C')}: {self.translate('Скопировать результат')}\n"
            f"{self.translate('Ctrl+I')}: {self.translate('О приложении')}\n"
            f"{self.translate('Ctrl+Q')}: {self.translate('Выход')}\n"
            f"{self.translate('Ctrl+K')}: {self.translate('Клавиатурные шорткаты')}"
        )
        messagebox.showinfo(
            self.translate("Клавиатурные шорткаты"),
            shortcuts_text
        )

    def exit_app(self, event: Union[tk.Event, None] = None) -> None:
        self.save_history()
        self.root.destroy()

    def copy_result(self, event: Union[tk.Event, None] = None) -> None:
        result_text = self.result_label.cget("text")
        if result_text != f"{self.translate('Результат')}: ":
            self.root.clipboard_clear()
            self.root.clipboard_append(result_text.split(": ", 1)[1])
            messagebox.showinfo(
                self.translate("Скопировано"),
                self.translate("Результат скопирован в буфер обмена.")
            )
        else:
            messagebox.showwarning(
                self.translate("Предупреждение"),
                self.translate("Нет результата для копирования.")
            )

    def switch_language(self) -> None:
        self.current_locale = 'en' if self.current_locale == 'ru' else 'ru'
        self.locale = self.load_locale(self.current_locale)
        self.update_ui_language()

    def update_ui_language(self) -> None:
        self.root.title(self.translate("Калькулятор"))
        self.update_labels()
        self.update_buttons()
        self.update_operation_menu()
        self.theme_label.config(text=self.translate("Тема") + ": ")

    def update_labels(self) -> None:
        labels = {
            "Первое число": self.label1,
            "Второе число": self.label2,
            "Операция": self.label3,
            "Результат": self.result_label,
            "История операций": self.label_history,
            "Тема": self.theme_label
        }

        for key, widget in labels.items():
            if widget == self.result_label:
                current_text = widget.cget("text").split(": ")[0]
                widget.config(text=f"{self.translate(key)}: ")
            else:
                widget.config(text=self.translate(key))

    def update_buttons(self) -> None:
        buttons = {
            "Вычислить": self.calculate_button,
            "Очистить": self.clear_button,
            "Скопировать результат": self.copy_button,
            "О приложении": self.info_button,
            "Клавиатурные шорткаты": self.shortcuts_button,
            "Выход": self.exit_button,
            "Переключить язык": self.language_button
        }

        for key, widget in buttons.items():
            widget.config(text=self.translate(key))

    def update_operation_display_map(self):
        self.operation_display_map = {
            "add": self.translate("Сложение"),
            "subtract": self.translate("Вычитание"),
            "multiply": self.translate("Умножение"),
            "divide": self.translate("Деление"),
            "power": self.translate("Возведение в степень"),
            "sqrt": self.translate("Квадратный корень")
        }
        self.display_operation_map = {v: k for k, v in self.operation_display_map.items()}

    def update_operation_menu(self) -> None:
        self.update_operation_display_map()
        self.operation_menu['values'] = list(self.operation_display_map.keys())
        # Установить текущую операцию, если она доступна в новом списке
        current_display = self.get_operation_name_map_reverse()
        if current_display in self.display_operation_map:
            self.selected_operation.set(current_display)
        else:
            self.selected_operation.set("Сложение")  # Устанавливаем "Сложение" как дефолтную операцию
            self.operation_menu.current(0)

    def get_operation_name_map_reverse(self):
        # Возвращает текущую операцию в виде ключа операции
        display_name = self.operation_menu.get()
        return self.display_operation_map.get(display_name, "")

    def switch_theme(self, theme: str) -> None:
        self.current_theme = theme
        self.apply_theme(theme)
        logging.info(f"{self.translate('Переключена тема на')}: {theme}")

    def apply_theme(self, theme: str) -> None:
        if theme == "Light":
            colors = LIGHT_THEME
        elif theme == "Dark":
            colors = DARK_THEME
        else:
            colors = LIGHT_THEME  # По умолчанию светлая тема

        # Обновление цветов фона основного окна
        self.root.configure(bg=colors["bg"])

        # Обновление цветов фона и текста для всех фреймов
        for frame in [self.input_frame, self.buttons_frame, self.history_frame, self.settings_frame]:
            frame.configure(style=f"{theme}.TFrame")

        # Настройка стилей для разных элементов интерфейса
        self.style.theme_use('xpnative')

        # Настройка фреймов
        self.style.configure(f"{theme}.TFrame", background=colors["bg"])

        # Настройка меток
        self.style.configure("TLabel", background=colors["bg"], foreground=colors["fg"])

        # Настройка кнопок
        self.style.configure("TButton", background=colors["button_bg"], foreground=colors["button_fg"])
        self.style.map("TButton",
                      background=[('active', colors["button_bg"])],
                      foreground=[('active', colors["button_fg"])])
    
        # Настройка полей ввода
        self.style.configure("TEntry", fieldbackground=colors["entry_bg"], foreground=colors["entry_fg"])

        # Настройка текстового поля истории
        self.history_text.config(bg=colors["history_bg"], fg=colors["history_fg"])

        # Настройка метки результата
        self.result_label.config(foreground=colors["result_fg"])

    def bind_shortcuts(self) -> None:
        # Привязки для английской раскладки
        self.root.bind_all('<Control-c>', self.copy_result)
        self.root.bind_all('<Control-C>', self.copy_result)
        self.root.bind_all('<Control-i>', self.show_info)
        self.root.bind_all('<Control-I>', self.show_info)
        self.root.bind_all('<Control-q>', self.exit_app)
        self.root.bind_all('<Control-Q>', self.exit_app)
        self.root.bind_all('<Control-k>', self.show_shortcuts)
        self.root.bind_all('<Control-K>', self.show_shortcuts)

        # Привязки для кириллической раскладки
        self.root.bind_all('<Control-с>', self.copy_result)  # Cyrillic 'с'
        self.root.bind_all('<Control-С>', self.copy_result)  # Cyrillic 'С'
        self.root.bind_all('<Control-и>', self.show_info)    # Cyrillic 'и'
        self.root.bind_all('<Control-И>', self.show_info)    # Cyrillic 'И'
        self.root.bind_all('<Control-я>', self.exit_app)     # Cyrillic 'я'
        self.root.bind_all('<Control-Я>', self.exit_app)     # Cyrillic 'Я'
        self.root.bind_all('<Control-к>', self.show_shortcuts)   # Cyrillic 'к'
        self.root.bind_all('<Control-К>', self.show_shortcuts)   # Cyrillic 'К'

        # Привязка клавиши Enter и Escape
        self.root.bind_all('<Return>', self.perform_operation)
        self.root.bind_all('<Escape>', self.clear_entries)

    def create_widgets(self):
        self.selected_operation = tk.StringVar()
        # Initialize operation_display_map
        self.update_operation_display_map()

        # Основные фреймы
        self.input_frame = ttk.Frame(self.root, padding="10")
        self.input_frame.grid(row=0, column=0, sticky='nsew')

        self.buttons_frame = ttk.Frame(self.root, padding="10")
        self.buttons_frame.grid(row=1, column=0, sticky='nsew')

        self.history_frame = ttk.Frame(self.root, padding="10")
        self.history_frame.grid(row=0, column=1, rowspan=3, sticky='nsew')

        self.settings_frame = ttk.Frame(self.root, padding="10")
        self.settings_frame.grid(row=2, column=0, sticky='nsew')

        self.root.columnconfigure(0, weight=3)
        self.root.columnconfigure(1, weight=2)
        self.root.rowconfigure(0, weight=1)
        self.root.rowconfigure(1, weight=2)
        self.root.rowconfigure(2, weight=1)

        # Ввод чисел и операция
        self.label1 = ttk.Label(self.input_frame, text=self.translate("Первое число"), font=("Arial", 12))
        self.label1.grid(row=0, column=0, sticky='w', pady=5)
        self.entry1 = ttk.Entry(self.input_frame, font=("Arial", 12))
        self.entry1.grid(row=0, column=1, sticky='ew', pady=5)

        self.label2 = ttk.Label(self.input_frame, text=self.translate("Второе число"), font=("Arial", 12))
        self.label2.grid(row=1, column=0, sticky='w', pady=5)
        self.entry2 = ttk.Entry(self.input_frame, font=("Arial", 12))
        self.entry2.grid(row=1, column=1, sticky='ew', pady=5)

        self.label3 = ttk.Label(self.input_frame, text=self.translate("Операция"), font=("Arial", 12))
        self.label3.grid(row=2, column=0, sticky='w', pady=5)

        # Operation Menu as Combobox
        self.operation_menu = ttk.Combobox(
            self.input_frame,
            textvariable=self.selected_operation,
            values=list(self.operation_display_map.keys()),
            state="readonly",
            font=("Arial", 12)
        )
        self.operation_menu.grid(row=2, column=1, sticky='ew', pady=5)
        self.operation_menu.current(0)
        self.operation_menu.bind("<<ComboboxSelected>>", self.on_operation_change)

        self.input_frame.columnconfigure(1, weight=1)

        # Цифровые и функциональные кнопки
        buttons = [
            ('7', lambda: self.append_number('7')),
            ('8', lambda: self.append_number('8')),
            ('9', lambda: self.append_number('9')),
            ('/', lambda: self.select_operation('divide')),
            ('4', lambda: self.append_number('4')),
            ('5', lambda: self.append_number('5')),
            ('6', lambda: self.append_number('6')),
            ('*', lambda: self.select_operation('multiply')),
            ('1', lambda: self.append_number('1')),
            ('2', lambda: self.append_number('2')),
            ('3', lambda: self.append_number('3')),
            ('-', lambda: self.select_operation('subtract')),
            ('0', lambda: self.append_number('0')),
            ('.', lambda: self.append_number('.')),
            ('√', lambda: self.select_operation('sqrt')),
            ('+', lambda: self.select_operation('add')),
            ('^', lambda: self.select_operation('power')),
            ('=', self.perform_operation),
            ('C', self.clear_entries)
        ]

        rows = 5
        cols = 4
        for index, (text, command) in enumerate(buttons):
            row = index // cols
            col = index % cols
            btn = ttk.Button(self.buttons_frame, text=text, command=command)
            btn.grid(row=row, column=col, padx=5, pady=5, sticky='nsew')

        for i in range(cols):
            self.buttons_frame.columnconfigure(i, weight=1)
        for i in range(rows):
            self.buttons_frame.rowconfigure(i, weight=1)

        # Результат
        self.result_label = ttk.Label(self.input_frame, text=f"{self.translate('Результат')}: ", font=("Arial", 12))
        self.result_label.grid(row=3, column=0, columnspan=2, sticky='w', pady=5)

        # Кнопки управления
        self.calculate_button = ttk.Button(self.settings_frame, text=self.translate("Вычислить"), command=self.perform_operation)
        self.calculate_button.grid(row=0, column=0, padx=5, pady=5, sticky='ew')

        self.clear_button = ttk.Button(self.settings_frame, text=self.translate("Очистить"), command=self.clear_entries)
        self.clear_button.grid(row=0, column=1, padx=5, pady=5, sticky='ew')

        self.copy_button = ttk.Button(self.settings_frame, text=self.translate("Скопировать результат"), command=self.copy_result)
        self.copy_button.grid(row=0, column=2, padx=5, pady=5, sticky='ew')

        self.info_button = ttk.Button(self.settings_frame, text=self.translate("О приложении"), command=self.show_info)
        self.info_button.grid(row=1, column=0, padx=5, pady=5, sticky='ew')

        self.shortcuts_button = ttk.Button(self.settings_frame, text=self.translate("Клавиатурные шорткаты"), command=self.show_shortcuts)
        self.shortcuts_button.grid(row=1, column=1, padx=5, pady=5, sticky='ew')

        self.exit_button = ttk.Button(self.settings_frame, text=self.translate("Выход"), command=self.exit_app)
        self.exit_button.grid(row=1, column=2, padx=5, pady=5, sticky='ew')

        self.language_button = ttk.Button(self.settings_frame, text=self.translate("Переключить язык"), command=self.switch_language)
        self.language_button.grid(row=2, column=0, columnspan=3, padx=5, pady=5, sticky='ew')

        for i in range(3):
            self.settings_frame.columnconfigure(i, weight=1)

        # История операций
        self.label_history = ttk.Label(self.history_frame, text=self.translate("История операций"), font=("Arial", 12))
        self.label_history.pack(anchor='w')
        self.history_text = tk.Text(
            self.history_frame,
            height=15,
            width=30,
            font=("Arial", 10),
            state=tk.DISABLED,
            bg=LIGHT_THEME["history_bg"],
            fg=LIGHT_THEME["history_fg"]
        )
        self.history_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.history_scroll = ttk.Scrollbar(self.history_frame, command=self.history_text.yview)
        self.history_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.history_text.config(yscrollcommand=self.history_scroll.set)

        # Темы оформления
        ttk.Label(self.settings_frame, text=self.translate("Тема") + ":", font=("Arial", 12)).grid(row=3, column=0, padx=5, pady=5, sticky='w')
        self.theme_menu = ttk.Combobox(self.settings_frame, values=["Light", "Dark"], state="readonly")
        self.theme_menu.set("Light")
        self.theme_menu.grid(row=3, column=1, padx=5, pady=5, sticky='ew')
        self.theme_menu.bind("<<ComboboxSelected>>", lambda event: self.switch_theme(self.theme_menu.get()))
        self.settings_frame.columnconfigure(1, weight=1)

    def append_number(self, number: str):
        focused_entry = self.root.focus_get()
        if isinstance(focused_entry, ttk.Entry):
            current_text = focused_entry.get()
            focused_entry.delete(0, tk.END)
            focused_entry.insert(0, current_text + number)
        else:
            self.entry1.insert(tk.END, number)

    def select_operation(self, operation_key: str):
        if operation_key in self.operation_display_map.values():
            self.selected_operation.set(operation_key)
            self.on_operation_change()

    def update_history_display(self):
        self.history_text.config(state=tk.NORMAL)
        self.history_text.delete('1.0', tk.END)
        for entry in self.history:
            display_entry = f"{entry['time']} - {entry['operation']}: {entry['expression']} = {entry['result']}"
            self.history_text.insert(tk.END, display_entry + '\n')
        self.history_text.see(tk.END)
        self.history_text.config(state=tk.DISABLED)

    def switch_theme(self, theme: str) -> None:
        self.current_theme = theme
        self.apply_theme(theme)
        logging.info(f"{self.translate('Переключена тема на')}: {theme}")

    def apply_theme(self, theme: str) -> None:
        if theme == "Light":
            colors = LIGHT_THEME
        elif theme == "Dark":
            colors = DARK_THEME
        else:
            colors = LIGHT_THEME  # По умолчанию светлая тема

        # Обновление цветов фона основного окна
        self.root.configure(bg=colors["bg"])

        # Обновление цветов фона и текста для всех фреймов
        for frame in [self.input_frame, self.buttons_frame, self.history_frame, self.settings_frame]:
            frame.configure(style=f"{theme}.TFrame")

        # Настройка стилей для разных элементов интерфейса
        self.style.theme_use('xpnative')

        # Настройка фреймов
        self.style.configure(f"{theme}.TFrame", background=colors["bg"])

        # Настройка меток
        self.style.configure("TLabel", background=colors["bg"], foreground=colors["fg"])

        # Настройка кнопок
        self.style.configure("TButton", background=colors["button_bg"], foreground=colors["button_fg"])
        self.style.map("TButton",
                      background=[('active', colors["button_bg"])],
                      foreground=[('active', colors["button_fg"])])
    
        # Настройка полей ввода
        self.style.configure("TEntry", fieldbackground=colors["entry_bg"], foreground=colors["entry_fg"])

        # Настройка текстового поля истории
        self.history_text.config(bg=colors["history_bg"], fg=colors["history_fg"])

        # Настройка метки результата
        self.result_label.config(foreground=colors["result_fg"])

    def bind_shortcuts(self) -> None:
        # Привязка для английской раскладки
        self.root.bind_all('<Control-c>', self.copy_result)
        self.root.bind_all('<Control-C>', self.copy_result)
        self.root.bind_all('<Control-i>', self.show_info)
        self.root.bind_all('<Control-I>', self.show_info)
        self.root.bind_all('<Control-q>', self.exit_app)
        self.root.bind_all('<Control-Q>', self.exit_app)
        self.root.bind_all('<Control-k>', self.show_shortcuts)
        self.root.bind_all('<Control-K>', self.show_shortcuts)

        # Привязка для кириллической раскладки
        self.root.bind_all('<Control-с>', self.copy_result)  # Cyrillic 'с'
        self.root.bind_all('<Control-С>', self.copy_result)  # Cyrillic 'С'
        self.root.bind_all('<Control-и>', self.show_info)    # Cyrillic 'и'
        self.root.bind_all('<Control-И>', self.show_info)    # Cyrillic 'И'
        self.root.bind_all('<Control-я>', self.exit_app)     # Cyrillic 'я'
        self.root.bind_all('<Control-Я>', self.exit_app)     # Cyrillic 'Я'
        self.root.bind_all('<Control-к>', self.show_shortcuts)   # Cyrillic 'к'
        self.root.bind_all('<Control-К>', self.show_shortcuts)   # Cyrillic 'К'

        # Привязка клавиши Enter и Escape
        self.root.bind_all('<Return>', self.perform_operation)
        self.root.bind_all('<Escape>', self.clear_entries)

    def create_widgets(self):
        # Этот метод уже реализован в `__init__` и включает создание всех фреймов и виджетов
        pass  # Можно оставить пустым или полностью удалить, если он дублирует `create_widgets`

    # В предыдущих версиях create_widgets был определён дважды и вызывался лишний раз
    # Убедитесь, что метод `create_widgets` определён только один раз в классе и содержит все необходимые виджеты

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("900x600")
    root.title("Калькулятор")
    app = CalculatorApp(root)
    root.mainloop()
ошибка 
D:\скрипты пайтон\калькулятор>python calculator.pyw
Traceback (most recent call last):
  File "D:\скрипты пайтон\калькулятор\calculator.pyw", line 655, in <module>
    app = CalculatorApp(root)
  File "D:\скрипты пайтон\калькулятор\calculator.pyw", line 60, in __init__
    self.bind_shortcuts()
  File "D:\скрипты пайтон\калькулятор\calculator.pyw", line 631, in bind_shortcuts
    self.root.bind_all('<Control-с>', self.copy_result)  # Cyrillic 'с'
  File "C:\Users\Я\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1399, in bind_all
    return self._bind(('bind', 'all'), sequence, func, add, 0)
  File "C:\Users\Я\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1339, in _bind
    self.tk.call(what + (sequence, cmd))
_tkinter.TclError: bad event type or keysym "с"