Загрузка данных


# -*- coding: utf-8 -*-
# main.py

import os
import sys
import tkinter as tk
from tkinter import messagebox

from PIL import Image, ImageTk

from gui.group_manager import GroupManager
from gui.indicator_manager import IndicatorManager

# --- Импорты из папки gui ---
from gui.interface_expert import InterfaceExpert
from gui.project_manager import ProjectManager
from modules.adminmanager import DatabaseWindow, UsersWindow
from modules.Aggregate_Excel import AggregateFilesWindow

# --- Импорты из папки modules ---
from modules.Export_Excel import ExportMatrixWindow
from modules.login import ChangeRoleWindow, LoginWindow
from modules.project_archive import ArchiveWindow
from modules.project_create import CreateProjectWindow
from modules.project_event import ProjectEventWindow
from modules.database_module import initialize_database

# --- НОВЫЕ ИМПОРТЫ ПОСЛЕ РЕФАКТОРИНГА ---
from modules.project_list import ProjectListWindow

# --- Константы цветов ---
HEADER_FOOTER_BG_COLOR = "#007BFF"
BUTTON_TEXT_COLOR = "black"
ORANGE_TEXT_COLOR = "#FFA500"
COMMON_FRAME_BG_COLOR = "#EEEEEE"


class MainWindow(tk.Tk):

    def __init__(self):
        super().__init__()
        self.title("Название Вашего Приложения")
        self.geometry("600x357")
        self.resizable(False, False)
        self.configure(bg=COMMON_FRAME_BG_COLOR)

        self.current_user = {}
        self.project_manager = ProjectManager()
        self.is_master_db_active = tk.BooleanVar(value=False)

        # --- ОСНОВНОЙ КОНТЕЙНЕР ---
        # Используем grid для всего главного окна
        main_container = tk.Frame(self, bg=COMMON_FRAME_BG_COLOR)
        main_container.pack(fill="both", expand=True)

        # Настройка сетки: 5 строк и 2 колонки
        main_container.grid_columnconfigure(0, weight=1, uniform="col")  # Левая колонка
        main_container.grid_columnconfigure(
            1, weight=1, uniform="col"
        )  # Правая колонка

        main_container.grid_rowconfigure(0, minsize=5)  # Отступ сверху
        main_container.grid_rowconfigure(1, weight=1)  # Основное содержимое
        main_container.grid_rowconfigure(2, minsize=5)  # Отступ
        main_container.grid_rowconfigure(3, minsize=60)  # Блок с кнопками внизу
        main_container.grid_rowconfigure(4, minsize=20)  # Отступ перед футером

        # --- ЛЕВАЯ ПАНЕЛЬ (КНОПКИ МЕНЮ) ---
        left_frame = tk.Frame(main_container, bg=COMMON_FRAME_BG_COLOR)
        left_frame.grid(
            row=1, column=0, sticky="nsew"
        )  # sticky="nsew" - растягивать во все стороны

        buttons = [
            {"text": "Администратор", "command": lambda: self.open_admin_menu()},
            {"text": "Менеджер базы", "command": lambda: self.open_base_manager()},
            {"text": "Эксперты", "command": lambda: self.open_experts_menu()},
            {"text": "Проект", "command": lambda: self.create_file()},
            {
                "text": "Прогнозирование",
                "command": lambda: self.open_export_matrix_window(),
            },
        ]

        for btn_config in buttons:
            button = tk.Button(
                left_frame,
                text=btn_config["text"],
                fg=BUTTON_TEXT_COLOR,
                bg="#FFFFFF",
                width=15,
                height=2,
                relief="raised",
                borderwidth=5,
                font=("Helvetica", 13, "bold"),
                command=btn_config["command"],
            )
            button.pack(side="top", fill="x", expand=False, padx=5, pady=2)

        # --- ПРАВАЯ ПАНЕЛЬ (ИЗОБРАЖЕНИЕ) ---
        right_frame = tk.Frame(main_container, bg=COMMON_FRAME_BG_COLOR)
        right_frame.grid(row=1, column=1, sticky="nsew")

        # Фрейм для картинки с фиксированной высотой
        image_frame = tk.Frame(right_frame, bg=COMMON_FRAME_BG_COLOR, height=105)
        image_frame.pack(fill="x", expand=False)

        # Загрузка изображения
        current_dir = os.path.dirname(os.path.abspath(__file__))
        image_path = os.path.join(current_dir, "images", "Сова1.png")

        if hasattr(self, "load_and_resize_image"):
            self.tk_image = self.load_and_resize_image(image_path, 300, 170)
            if self.tk_image:
                self.label = tk.Label(image_frame, image=self.tk_image)
                self.label.image = self.tk_image
                self.label.pack(expand=True)

        # --- НИЖНИЙ БЛОК (КНОПКИ) ---
        small_button_frame = tk.Frame(main_container, bg=COMMON_FRAME_BG_COLOR)
        small_button_frame.grid(row=3, column=0, columnspan=2, sticky="ew")

        button1 = tk.Button(
            small_button_frame,
            text="Руководство",
            fg=BUTTON_TEXT_COLOR,
            bg="#FFFFFF",
            width=15,
            height=2,
            relief="raised",
            borderwidth=5,
            font=("Helvetica", 13, "bold"),
        )
        button1.pack(side="left", expand=True, padx=(50, 5), pady=(0, 5))

        button2 = tk.Button(
            small_button_frame,
            text="О программе",
            fg=BUTTON_TEXT_COLOR,
            bg="#FFFFFF",
            width=15,
            height=2,
            relief="raised",
            borderwidth=5,
            font=("Helvetica", 13, "bold"),
        )
        button2.pack(side="right", expand=True, padx=(5, 50), pady=(0, 5))

        # --- ФУТЕР (КОПИРАЙТ) ---
        footer_label = tk.Label(
            main_container,
            text="Copyright © Москва 2025 год",
            fg=ORANGE_TEXT_COLOR,
            bg=HEADER_FOOTER_BG_COLOR,
            anchor="w",
            font=("Helvetica", 11, "bold"),
            height=2,
        )
        footer_label.grid(row=4, column=0, columnspan=2, sticky="ew")

    # --- МЕТОДЫ ДЛЯ ОТКРЫТИЯ ОКОН ---

    def load_and_resize_image(self, path, max_width, max_height):
        """Загружает и изменяет размер изображения."""
        try:
            original_img = Image.open(path)
            ratio = min(
                max_width / original_img.width, max_height / original_img.height
            )
            resized_img = original_img.resize(
                (int(original_img.width * ratio), int(original_img.height * ratio)),
                resample=Image.LANCZOS,
            )
            return ImageTk.PhotoImage(resized_img)
        except FileNotFoundError:
            print(f"[WARNING] Изображение не найдено по пути: {path}")
            # Возвращаем None или заглушку, чтобы программа не упала
            return None

    def open_create_project_window(self):
        CreateProjectWindow(self.project_manager)

    def open_archive_window(self):
        ArchiveWindow()

    def open_group_manager(self):
        if self.current_user.get("role") != "Администратор":
            messagebox.showwarning(
                "Доступ запрещён", "Только администратор может управлять группами."
            )
            return
        group_manager = GroupManager(self)
        group_manager.grab_set()
        group_manager.wait_window()

    def open_indicator_manager(self):
        indicator_manager = IndicatorManager(self)

        self.update_idletasks()
        parent_x = self.winfo_x()
        parent_y = self.winfo_y()
        parent_w = self.winfo_width()
        parent_h = self.winfo_height()

        w = indicator_manager.winfo_width()
        h = indicator_manager.winfo_height()

        x = parent_x + (parent_w - w) // 2
        y = parent_y + (parent_h - h) // 2

        indicator_manager.geometry(f"+{x}+{y}")

        indicator_manager.grab_set()
        indicator_manager.wait_window()

    def open_admin_menu(self):
        admin_menu = tk.Menu(self, tearoff=0)
        admin_menu.add_command(label="Пользователи", command=self.open_users_window)
        admin_menu.add_command(label="Базы данных", command=self.open_database_window)

        root_x = self.winfo_rootx()
        root_y = self.winfo_rooty()

        menu_x = root_x + 100
        menu_y = root_y + 100

        admin_menu.post(menu_x, menu_y)

    def open_users_window(self):
        if self.current_user.get("role"):
            success = self.propose_admin_access()
            if success or self.current_user.get("role") == "Администратор":
                users_window = UsersWindow(self)
                users_window.grab_set()
                users_window.wait_window()
        else:
            messagebox.showwarning(
                "Ошибка", "Вы не авторизованы. Пожалуйста, выполните вход."
            )

    def open_database_window(self):
        if self.current_user.get("role"):
            success = self.propose_admin_access()
            if success or self.current_user.get("role") == "Администратор":
                db_window = DatabaseWindow(self)
                db_window.grab_set()
                db_window.wait_window()
        else:
            messagebox.showwarning(
                "Ошибка", "Вы не авторизованы. Пожалуйста, выполните вход."
            )

    def propose_admin_access(self):
        if self.current_user.get("role") != "Администратор":
            confirm_dialog = ChangeRoleWindow(self)
            confirm_dialog.grab_set()
            confirm_dialog.wait_window()

    def create_file(self):
        """Открывает меню управления проектами при нажатии кнопки 'Проект'."""
        project_menu = tk.Menu(self, tearoff=0)
        project_menu.add_command(
            label="Создать проект", command=self.open_create_project_window
        )
        project_menu.add_command(
            label="Редактировать проект", command=self.open_edit_project_window
        )
        project_menu.add_command(
            label="Архив проектов", command=self.open_archive_window
        )

        root_x = self.winfo_rootx()
        root_y = self.winfo_rooty()

        menu_x = root_x + 100  # Смещение по X от левого верхнего угла главного окна
        menu_y = root_y + 100  # Смещение по Y

        project_menu.post(menu_x, menu_y)

    def open_base_manager(self):
        base_menu = tk.Menu(self, tearoff=0)
        base_menu.add_command(label="Менеджер групп", command=self.open_group_manager)
        base_menu.add_command(
            label="Менеджер показателей", command=self.open_indicator_manager
        )

        root_x = self.winfo_rootx()
        root_y = self.winfo_rooty()

        menu_x = root_x + 100
        menu_y = root_y + 100

        base_menu.post(menu_x, menu_y)

    def open_experts_menu(self):
        experts_menu = tk.Menu(self, tearoff=0)
        experts_menu.add_command(
            label="Создание файла для эксперта", command=self.open_create_file_window
        )

        experts_menu.add_command(
            label="Агрегирование файлов", command=self.open_aggregation_window
        )

        root_x = self.winfo_rootx()
        root_y = self.winfo_rooty()

        menu_x = root_x + 100
        menu_y = root_y + 100

        experts_menu.post(menu_x, menu_y)

    def open_create_file_window(self):
        create_file_window = ExportMatrixWindow(self)

        self.update_idletasks()
        parent_x = self.winfo_x()
        parent_y = self.winfo_y()
        parent_w = self.winfo_width()
        parent_h = self.winfo_height()

        w = create_file_window.winfo_width()
        h = create_file_window.winfo_height()

        x = parent_x + (parent_w - w) // 2
        y = parent_y + (parent_h - h) // 2

        create_file_window.geometry(f"+{x}+{y}")

        create_file_window.grab_set()
        create_file_window.wait_window()

    def open_aggregation_window(self):
        aggregation_window = AggregateFilesWindow(self)

        self.update_idletasks()
        parent_x = self.winfo_x()
        parent_y = self.winfo_rooty()
        parent_w = self.winfo_width()
        parent_h = self.winfo_height()

        w = aggregation_window.winfo_width()
        h = aggregation_window.winfo_height()

        x = parent_x + (parent_w - w) // 2
        y = parent_y + (parent_h - h) // 2

        aggregation_window.geometry(f"+{x}+{y}")

        aggregation_window.grab_set()
        aggregation_window.wait_window()

    def on_exit(self):
        """Корректно закрывает приложение."""
        self.destroy()

    def start_login_process(self):
        login_window = LoginWindow(self)
        login_window.grab_set()
        login_window.wait_window()

        # --- ДОБАВЛЕНИЕ ТЕСТОВЫХ ПРОЕКТОВ ---
        try:
            from gui.project_manager import Project

            test_project_1_name = "Тестовый проект №1"
            test_project_2_name = "Тестовый проект №2"

            existing_names = [p.name for p in self.project_manager.list_projects()]

            # Добавляем проект 1, если его нет
            if test_project_1_name not in existing_names:
                new_project_1_obj = Project(name=test_project_1_name)
                new_project_1_obj.id = "TEST-001"
                self.project_manager.add_project(new_project_1_obj)

            # Добавляем проект 2, если его нет
            if test_project_2_name not in existing_names:
                new_project_2_obj = Project(name=test_project_2_name)
                new_project_2_obj.id = "TEST-002"
                self.project_manager.add_project(new_project_2_obj)

        except Exception as e:

            pass  # Лучше ничего не делать, если не удалось добавить тесты

    # --- РЕДАКТИРОВАНИЕ ПРОЕКТА ---
    def open_edit_project_window(self):
        """Открывает окно для выбора проекта."""
        # print("DEBUG: Открываем окно выбора проекта...")
        # Просто создаем окно выбора.
        # Нам не нужно его хранить или ждать.
        list_window = ProjectListWindow(self.project_manager, root_window=self)

        # Добавляем кнопку
        open_event_btn = tk.Button(
            list_window.window,
            text="Открыть ввод событий",
            width=25,
            bg="#4CAF50",
            fg="white",
            command=lambda: self.open_event_window_from_list(list_window),
        )
        open_event_btn.pack(pady=10)

    def return_from_event_window(self):
        """Этот метод вызывается из окна ProjectEventWindow при закрытии.
        Он показывает обратно скрытое окно со списками."""
        print("DEBUG: Пользователь возвращается в окно выбора параметров.")
        try:
            # Проверяем, что ссылка на окно со списками существует
            if hasattr(self, "list_window_instance"):
                # Снимаем блокировку с главного окна
                self.list_window_instance.window.grab_release()

                # Показываем окно со списками обратно
                self.list_window_instance.window.deiconify()

                # Принудительно выводим его на передний план
                self.list_window_instance.window.attributes("-topmost", 1)
                self.list_window_instance.window.after(
                    100,
                    lambda: self.list_window_instance.window.attributes("-topmost", 0),
                )

        except Exception as e:
            print(f"ОШИБКА при возврате в окно выбора: {e}")

    def open_event_window_from_list(self, list_window_instance):
        """Получает данные и открывает окно событий."""
        # --- НОВОЕ: Блокируем кнопку, чтобы избежать двойного клика ---
        try:
            # Получаем кнопку из переданного окна и блокируем ее
            # Предполагается, что кнопка имеет имя или мы ее находим
            # Если кнопка это переменная в list_window_instance, используй ее.
            # Если нет, найди ее по тексту или положи в переменную при создании.
            # Здесь я покажу универсальный способ через поиск по тексту.
            for widget in list_window_instance.window.winfo_children():
                if isinstance(
                    widget, tk.Button
                ) and "Открыть ввод событий" in widget.cget("text"):
                    widget.config(state="disabled")
                    break

            # Теперь основная логика
            selected_data = list_window_instance.get_selected_data()

            if not selected_data.get("project_name"):
                # Если кнопка заблокирована, вернем ей состояние NORMAL
                for widget in list_window_instance.window.winfo_children():
                    if isinstance(widget, tk.Button):
                        widget.config(state="normal")
                messagebox.showwarning("Ошибка", "Сначала выберите проект.")
                return

            # Уничтожаем окно выбора.
            list_window_instance.window.destroy()

            # Создаем окно событий.
            event_window = ProjectEventWindow(
                manager=list_window_instance.manager,
                selected_data=selected_data,
                root_window=self,
            )
            event_window.window.after(100, event_window.load_events)

        except Exception as e:
            pass  # Логирование убрано для чистоты

    def return_from_event_window(self):
        """Этот метод вызывается из окна ProjectEventWindow при закрытии."""
        try:
            if hasattr(self, "list_window_instance"):
                self.list_window_instance.window.grab_release()

            # --- НОВОЕ: Разблокируем кнопки в главном окне ---
            self.enable_all_buttons()

        except Exception as e:
            pass

    def show_selection_window(self):
        """Показывает окно выбора проектов обратно."""
        try:
            if (
                hasattr(self, "list_window_instance")
                and self.list_window_instance.window.winfo_exists()
            ):
                self.list_window_instance.window.deiconify()
            else:
                self.open_edit_project_window()
        except Exception as e:
            pass

    def enable_all_buttons(self):
        """Проходит по всем виджетам и включает все кнопки (снимает блокировку)."""
        for frame in self.winfo_children():
            for widget in frame.winfo_children():
                if isinstance(widget, tk.Button):
                    widget.config(state="normal")


if __name__ == "__main__":
    try:
        import sys
        import os

        # --- 1. ВРЕМЕННО ОТКЛЮЧАЕМ ВЫВОД В КОНСОЛЬ ---
        # Сохраняем текущий stdout, чтобы вернуть его потом
        old_stdout = sys.stdout
        old_stderr = sys.stderr  # Иногда ошибки пишутся сюда

        # Открываем "пустое" устройство (NUL для Windows)
        devnull = open(os.devnull, "w")

        # Подменяем стандартные потоки вывода на "пустоту"
        sys.stdout = devnull
        sys.stderr = devnull

        # --- 2. ЗАПУСКАЕМ ПРИЛОЖЕНИЕ ---
        # Весь код, который создает папки и подключается к БД,
        # выполнится в полной тишине.
        app = MainWindow()

        # --- 3. ВОЗВРАЩАЕМ ВЫВОД ОБРАТНО ---
        # Теперь, когда все "шумные" объекты созданы, возвращаем вывод.
        sys.stdout = old_stdout
        sys.stderr = old_stderr
        devnull.close()

        # --- 4. ПРОДОЛЖАЕМ НОРМАЛЬНЫЙ ЗАПУСК ---
        app.withdraw()  # Скрываем окно на время логина

        login_window = LoginWindow(app)

        # --- 5. ОКОНЧАТЕЛЬНО ОТКЛЮЧАЕМ ЛОГИ (если они есть) ---
        import logging

        logging.disable(logging.INFO)

        app.mainloop()

    except Exception as e:
        # В блоке except вывод уже должен быть включен, чтобы мы увидели ошибку
        print(f"Возникла критическая ошибка при запуске приложения: {e}")

# Главный запуск программы
if __name__ == "__main__":
    # Первым делом создаём структуру базы данных
    initialize_database()