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


# -*- coding: utf-8 -*-
# Файл: modules/project_edit.py

import tkinter as tk
from tkinter import Button, Radiobutton, Toplevel, messagebox, ttk

# Импортируем только функции и движок
from modules.database_module import engine, get_all_indicators_by_group, list_groups


class EditProjectWindow:
    """
    Класс окна редактирования проекта.
    Сессия SQLAlchemy открывается один раз при инициализации окна.
    """

    def __init__(self, manager, root_window=None):
        self.manager = manager  # <-- НОВОЕ: Сохраняем ссылку на менеджер
        self.root_window = root_window
        self.window = Toplevel(root_window)
        self.window.title("Редактирование проекта")
        self.window.geometry("600x500")
        self.window.protocol("WM_DELETE_WINDOW", self.on_close)

        # --- ИЗМЕНЕНИЕ: Убираем создание своего движка ---
        # self.engine = create_engine("sqlite:///database.db", echo=False)
        # self.Session = sessionmaker(bind=self.engine)

        # --- НОВОЕ: Используем сессию из основной базы (как было раньше) ---
        from modules.database_module import Session

        self.session = Session()
        # ------------------------------------

        self.group_names_to_codes = {}
        self._create_widgets()

        # Загружаем список проектов сразу
        self.update_project_list()

        # Если проекты есть, сразу грузим данные для первого из них
        if self.project_combobox["values"]:
            self._load_data()
        # Словарь для быстрого поиска: Название проекта -> Код проекта (ID)
        self.name_to_code_map = {}

    def _create_widgets(self):
        main_frame = tk.Frame(self.window)
        main_frame.pack(padx=10, pady=2, fill="both", expand=True)

        # 1. Выбор проекта
        tk.Label(main_frame, text="Выбрать проект:").pack(anchor="w")
        self.project_combobox = ttk.Combobox(main_frame, state="readonly")
        self.project_combobox.pack(fill="x", padx=15)
        self.project_combobox.bind("<<ComboboxSelected>>", self._load_data)

        # 2. Выбор Группы
        tk.Label(main_frame, text="Группа:").pack(anchor="w")
        self.group_combobox = ttk.Combobox(main_frame, state="readonly")
        self.group_combobox.bind("<<ComboboxSelected>>", self.update_indicators_list)
        self.group_combobox.pack(fill="x", padx=15)

        # 3. Выбор Показателя
        tk.Label(main_frame, text="Показатель:").pack(anchor="w")
        self.indicator_combobox = ttk.Combobox(main_frame, state="readonly")
        self.indicator_combobox.pack(fill="x", padx=15)

        # 4. Поле "Дата события"
        date_frame = tk.Frame(main_frame)
        date_frame.pack(fill="x", pady=3)
        tk.Label(date_frame, text="Дата события:").pack(side="left")
        self.date_entry = tk.Entry(date_frame, width=12)
        self.date_entry.insert(0, "2026-05-08")
        self.date_entry.pack(side="left", padx=(5, 0), ipadx=5)

        # 5. Радиокнопки "Направленность"
        direction_frame = tk.Frame(main_frame)
        direction_frame.pack(fill="x", pady=3, anchor="w")
        tk.Label(direction_frame, text="Направленность:").pack(side="left")

        radio_frame = tk.Frame(direction_frame)
        radio_frame.pack(side="left", padx=(5, 0))

        self.direction_var = tk.IntVar(value=1)

        Radiobutton(
            radio_frame, text="+ К цели", variable=self.direction_var, value=1
        ).pack(side="left", padx=(0, 10))

        Radiobutton(
            radio_frame, text="- От цели", variable=self.direction_var, value=-1
        ).pack(side="left", padx=(0, 10))

        # 6. Поле "Событие"
        tk.Label(main_frame, text="Событие:").pack(anchor="w")

        self.event_text = tk.Text(main_frame, height=4, wrap="word")
        self.event_text.pack(fill="x", pady=(2, 5), padx=15)

        # Кнопка "Вставить из буфера"
        btn_frame = tk.Frame(main_frame)
        btn_frame.pack(anchor="e", padx=15, pady=(0, 5))

        Button(
            btn_frame, text="Вставить", command=self.paste_from_clipboard, width=10
        ).pack()

        # Блок кнопок управления (Добавить, Удалить...)
        ctrl_btns = tk.Frame(main_frame)
        ctrl_btns.pack(fill="x", pady=(5, 3))  # Отступ сверху стал меньше

        Button(ctrl_btns, text="Добавить", width=15, command=self.add_entry).pack(
            side="left", padx=(0, 10)
        )

        Button(ctrl_btns, text="Удалить", width=15).pack(side="left", padx=(10, 10))

        Button(ctrl_btns, text="Печать в Word", width=15).pack(
            side="left", padx=(10, 10)
        )

        Button(ctrl_btns, text="В архив", width=15, command=self.archive_project).pack(
            side="left", padx=(10, 0)
        )

        # --- НОВЫЙ БЛОК: Список событий ---
        events_frame = tk.Frame(main_frame)
        events_frame.pack(
            fill="both", expand=True, pady=(5, 0), padx=15
        )  # Отступ сверху стал меньше

        tk.Label(events_frame, text="Список событий:").pack(anchor="w")

        # Создаем виджет для списка (Listbox) с полосой прокрутки (Scrollbar)
        self.events_listbox = tk.Listbox(events_frame, height=6)
        self.events_listbox.pack(side="left", fill="both", expand=True)

        scrollbar = tk.Scrollbar(
            events_frame, orient="vertical", command=self.events_listbox.yview
        )
        self.events_listbox.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side="right", fill="y")

    def update_project_list(self):
        """Обновляет список проектов."""

        projects = self.manager.list_projects()

        try:
            names = [p.name for p in projects]
            self.project_combobox["values"] = names
            return bool(names)  # Вернем True/False для удобства

        except Exception as e:
            print(f"Ошибка: {e}")
            return False

    def _load_data(self, event=None):
        """Загружает данные (группы и показатели) при выборе проекта."""

        # Получаем имя выбранного проекта
        project_name = self.project_combobox.get()

        # --- ОЧИЩАЕМ ВСЁ, ЕСЛИ ПРОЕКТ НЕ ВЫБРАН ---
        if not project_name:
            self.group_combobox.set("")
            self.indicator_combobox.set("")
            self.group_combobox["values"] = []
            self.indicator_combobox["values"] = []
            self.group_names_to_codes.clear()  # Очищаем словарь групп
            return

        print("[ДИАГНОСТИКА] --- ЗАГРУЗКА ДАННЫХ ПО ПРОЕКТУ ---")

        try:
            # Получаем список объектов проектов (нужно для словаря)
            project_objects = self.manager.list_projects()

            # --- СОЗДАЕМ СЛОВАРЬ: Название -> Код (ID) ---
            self.name_to_code_map.clear()  # Очищаем на всякий случай
            for p in project_objects:
                # Сохраняем соответствие: Название проекта -> его Код (ID)
                self.name_to_code_map[p.name] = p.id

            # --- ЗАГРУЗКА ДАННЫХ ИЗ ОСНОВНОЙ БАЗЫ (группы и показатели) ---
            groups = list_groups(session=self.session)

            if groups:
                group_names = [group.name for group in groups]
                self.group_combobox["values"] = group_names

                if group_names:
                    # Выбираем первую группу по умолчанию
                    first_group_name = group_names[0]
                    self.group_combobox.set(first_group_name)

                    # Обновляем словарь имен и кодов групп
                    self.group_names_to_codes.clear()
                    for group in groups:
                        self.group_names_to_codes[group.name] = group.code

                    # Загружаем показатели для первой группы
                    first_group_code = self.group_names_to_codes[first_group_name]
                    indicators = get_all_indicators_by_group(
                        first_group_code, session=self.session
                    )

                    if indicators:
                        indicator_names = [indicator.name for indicator in indicators]
                        self.indicator_combobox["values"] = indicator_names
                        if indicator_names:
                            self.indicator_combobox.set(indicator_names[0])

        except Exception as e:
            print(f"[ОШИБКА] Не удалось загрузить данные: {e}")

    def update_indicators_list(self, event=None):
        """Обновляет список показателей при выборе новой группы."""

        selected_group_name = self.group_combobox.get()

        # Если группа не выбрана, очищаем список показателей
        if not selected_group_name:
            self.indicator_combobox.set("")
            self.indicator_combobox["values"] = []
            return

        print(
            f"[ДИАГНОСТИКА] Пользователь выбрал группу: '{selected_group_name}'. Загружаем показатели..."
        )

        try:
            selected_group_code = self.group_names_to_codes.get(selected_group_name)
            if selected_group_code:
                indicators = get_all_indicators_by_group(
                    selected_group_code, session=self.session
                )

                if indicators:
                    indicator_names = [indicator.name for indicator in indicators]
                    self.indicator_combobox["values"] = indicator_names

                    if indicator_names:
                        # Сохраняем текущий выбор, если он есть в новом списке
                        current_val = self.indicator_combobox.get()
                        if current_val and current_val in indicator_names:
                            pass
                        else:
                            self.indicator_combobox.set(indicator_names[0])
                        print(
                            f"[ДИАГНОСТИКА] Список показателей обновлен: {indicator_names}"
                        )
                    else:
                        print("[ДИАГНОСТИКА] Показателей для этой группы нет.")
                else:
                    print("[ДИАГНОСТИКА] Показателей для этой группы нет.")

                # Очищаем поле и список, если показателей нет
                if not indicators or len(indicators) == 0:
                    self.indicator_combobox.set("")
                    self.indicator_combobox["values"] = []

            else:
                print("[ДИАГНОСТИКА] Код группы не найден в словаре.")
                self.indicator_combobox.set("")
                self.indicator_combobox["values"] = []

        except Exception as e:
            print(f"[ОШИБКА] Не удалось загрузить показатели: {e}")

    def add_entry(self):
        """Обработка нажатия кнопки 'Добавить'."""
        # --- ИМПОРТЫ ---
        import os
        import sqlite3

        # --- 1. ПРОВЕРКА ВХОДНЫХ ДАННЫХ ---
        project_name = self.project_combobox.get()
        if not project_name:
            messagebox.showwarning("Ошибка", "Выберите проект.")
            return

        group_name = self.group_combobox.get()
        if not group_name:
            messagebox.showwarning("Ошибка", "Выберите группу.")
            return

        indicator_name = self.indicator_combobox.get()
        if not indicator_name:
            messagebox.showwarning("Ошибка", "Выберите показатель.")
            return

        event_text_content = self.event_text.get("1.0", tk.END).strip()
        if not event_text_content:
            messagebox.showwarning("Ошибка", "Поле 'Событие' пустое.")
            return

        # --- 2. ПОДГОТОВКА ДАННЫХ ---
        from modules.database_module import Group, Indicator

        # Получаем данные ГРУППЫ из основной БД
        group_db = self.session.query(Group).filter_by(name=group_name).first()
        if not group_db:
            messagebox.showerror(
                "Критическая ошибка", "Группа не найдена в базе данных."
            )
            return

        group_kov = group_db.kov

        # Получаем код ПОКАЗАТЕЛЯ
        indicator_db = (
            self.session.query(Indicator).filter_by(name=indicator_name).first()
        )
        if not indicator_db:
            messagebox.showerror("Критическая ошибка", "Показатель не найден.")
            return

        indicator_code = indicator_db.code

        direction = self.direction_var.get()
        date_event = self.date_entry.get()

        # --- 3. ЗАПИСЬ В БАЗУ (внутри try/except) ---

        try:
            # Получаем список проектов от менеджера и ищем нужный объект
            project_objects = self.manager.list_projects()
            found_project = None
            for p in project_objects:
                if p.name == project_name:
                    found_project = p
                    break

            if not found_project:
                messagebox.showerror(
                    "Ошибка", f"Проект '{project_name}' не найден в менеджере."
                )
                return

            project_code = found_project.id

            # Строим путь к базе данных проекта по коду (ID)
            db_folder = os.path.join(os.getcwd(), "project_dbs")
            db_path = os.path.join(db_folder, f"{project_code}.db")

            if not os.path.exists(db_path):
                messagebox.showerror(
                    "Ошибка", f"База данных не найдена по пути: {db_path}"
                )
                return

            conn = sqlite3.connect(db_path)
            cursor = conn.cursor()

            # Подсчет количества событий для генерации кода (IV-CODE_Номер)
            cursor.execute(
                """
                    SELECT COUNT(*) FROM project_data
                    WHERE project_code = ? AND ivent_code LIKE ?
                """,
                (project_code, f"{indicator_code}_%"),
            )

            count = cursor.fetchone()[0]

            event_code = f"{indicator_code}_{count + 1}"

            cursor.execute(
                """
                    INSERT INTO project_data (
                        project_code, created_at, group_name, group_kov,
                        indicator_name, indicator_code, ivent_data, ivent_sign,
                        ivent_code, event_text_content
                    ) VALUES (?, datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    project_code,
                    group_name,
                    group_kov,
                    indicator_name,
                    indicator_code,
                    date_event,
                    direction,
                    event_code,
                    event_text_content,
                ),
            )

            conn.commit()

            # --- 4. ОБНОВЛЕНИЕ ИНТЕРФЕЙСА (только если запись в БД успешна) ---
            # Этот код находится ВНУТРИ блока try, поэтому выполнится только
            # при успехе.

            # Добавляем запись в список на экране
            self.events_listbox.insert(
                tk.END, f"{event_code}: {event_text_content[:40]}..."
            )

            # Очищаем поле события после добавления
            self.event_text.delete("1.0", tk.END)

            # Показываем сообщение об успехе
            messagebox.showinfo(
                "Успех", f"Данные добавлены.\nКод события: {event_code}"
            )

        except Exception as e:
            # Если что-то пошло не так (например, нет таблицы или ошибка записи),
            # мы выводим сообщение об ошибке и ВЫХОДИМ из метода.
            # Код обновления интерфейса выше не выполнится.
            messagebox.showerror("Ошибка БД", f"Не удалось записать данные: {e}")
            return  # <--- МАРКЕР: ЗДЕСЬ ПРОИСХОДИТ ВОЗВРАТ ПРИ ОШИБКЕ!

        finally:
            if "conn" in locals():
                conn.close()

    def paste_from_clipboard(self):
        """Вставляет текст из буфера обмена в поле события."""
        try:
            txt = self.window.clipboard_get()
            # Вставляем текст в текущую позицию курсора или в начало поля
            self.event_text.insert(tk.INSERT, txt)
        except Exception as e:
            pass

    def archive_project(self):
        """Обработка нажатия кнопки 'В архив'."""
        name = self.project_combobox.get()
        if not name:
            messagebox.showwarning("Ошибка", "Выберите проект.")
            return

        messagebox.showinfo("Успех", f"Проект '{name}' архивирован.")

    def on_close(self):
        """Корректно закрывает сессию базы данных при закрытии окна."""
        try:
            if hasattr(self, "session") and self.session is not None:
                self.session.close()
                print("[ДИАГНОСТИКА] Сессия базы данных закрыта.")

            # Это обязательно для модальных окон (grab_set), чтобы избежать
            # зависаний главного окна
            if hasattr(self.window, "destroy"):
                self.window.destroy()

        except Exception as e:
            print(f"[ДИАГНОСТИКА] Ошибка при закрытии сессии: {e}")