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


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

import os
import sqlite3
import tkinter as tk
from tkinter import Button, Radiobutton, Toplevel, messagebox, ttk
from tkinter import messagebox
from modules.project_database import TABLE_PROJECT_DATA


class ProjectEventWindow:
    """
    Окно для работы с событиями: ввод текста, добавление в список,
    управление записями и печать.
    Принимает данные (код проекта) из окна выбора параметров.
    """

    def __init__(self, manager, selected_data, root_window=None):
        """
        :param selected_data: Словарь с данными из ProjectListWindow.
                             Должен содержать 'project_code', 'group_code', 'indicator_name', 'indicator_code'.
        """
        self.manager = manager
        self.root_window = root_window

        # --- ВАЛИДАЦИЯ ВХОДНЫХ ДАННЫХ ---
        project_code_from_data = selected_data.get("project_code")
        group_code_from_data = selected_data.get("group_code")
        indicator_name_from_data = selected_data.get("indicator_name")
        indicator_code_from_data = selected_data.get("indicator_code")

        if not all(
            [
                project_code_from_data,
                group_code_from_data,
                indicator_name_from_data,
                indicator_code_from_data,
            ]
        ):
            messagebox.showerror(
                "Ошибка данных",
                "Не удалось получить данные проекта, группы или показателя.",
            )
            return

        self.selected_data = selected_data
        self.project_code = project_code_from_data
        self.group_code = group_code_from_data
        self.indicator_name = indicator_name_from_data
        self.indicator_code = indicator_code_from_data

        # --- СОЗДАНИЕ ОКНА ---
        # Используем Toplevel, если есть родительское окно, иначе создаем
        # главное
        if root_window:
            self.window = Toplevel(root_window)
        else:
            self.window = tk.Tk()

        project_name = self.selected_data.get("project_name", "Неизвестный проект")
        self.window.title(f"Ввод событий - {project_name}")
        self.window.geometry("750x600")

        # --- КОРРЕКТНОЕ ЗАКРЫТИЕ ОКНА ---
        # Эта строка связывает системную кнопку закрытия (крестик) с нашим
        # методом
        self.window.protocol("WM_DELETE_WINDOW", self.on_close)

        # Создаем виджеты после того, как окно успешно создано и настроено
        self._create_widgets()

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

        # --- Блок информации о контексте (Группа и Показатель) ---
        info_frame = tk.Frame(main_frame)
        info_frame.pack(fill="x", pady=5, padx=15)
        context_text = f"Группа: {self.group_code}, Показатель: {self.indicator_name}"
        tk.Label(info_frame, text=context_text, font=("Arial", 10)).pack(anchor="w")

        # --- Блок ввода данных ---
        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)

        # Радиокнопки "Направленность"
        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))

        # Поле "Событие"
        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)

        # --- Блок кнопок управления ---
        ctrl_btns = tk.Frame(main_frame)
        ctrl_btns.pack(fill="x", pady=(0, 5), padx=15)

        Button(
            ctrl_btns, text="Вставить", command=self.paste_from_clipboard, width=15
        ).pack(side="left", padx=(0, 10))

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

        # Кнопка "Удалить"
        Button(
            ctrl_btns, text="Удалить", width=15, command=self.delete_selected_event
        ).pack(side="left", padx=(10, 10))

        # Кнопка "Печать в Word" (пока заглушка)
        Button(
            ctrl_btns, text="Печать в Word", width=15, command=self.print_to_word
        ).pack(side="left", padx=(10, 10))

        # --- Блок списка событий (ТАБЛИЦА) ---
        events_main_frame = tk.Frame(main_frame)
        events_main_frame.pack(fill="both", expand=True, pady=(5, 0), padx=15)

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

        # 1. Определяем колонки
        columns = ("code", "date", "direction", "event")

        # 2. Создаем Treeview (таблицу)
        self.events_tree = ttk.Treeview(
            events_main_frame, columns=columns, show="headings", height=10
        )

        # --- НОВАЯ СТРОКА: Привязка двойного клика ---
        # Привязываем событие к конкретным элементам (строкам) таблицы.
        # 'i' означает item (элемент/строка).
        self.events_tree.bind("<Double-1>", self.on_edit_event)

        # 3. Задаем ширину и заголовки (РЕШЕНИЕ ПРОБЛЕМЫ РАЗМЕРОВ)
        self.events_tree.column("code", width=80, anchor="center", minwidth=60)
        self.events_tree.column("date", width=90, anchor="center", minwidth=70)
        self.events_tree.column("direction", width=45, anchor="center", minwidth=30)
        self.events_tree.column("event", anchor="w", minwidth=200, stretch=tk.YES)

        self.events_tree.heading("code", text="Код")
        self.events_tree.heading("date", text="Дата")
        self.events_tree.heading("direction", text="+/-")
        self.events_tree.heading("event", text="Событие")

        # 4. Добавляем скроллбар и связываем его с таблицей
        scrollbar = tk.Scrollbar(
            events_main_frame, orient="vertical", command=self.events_tree.yview
        )

        self.events_tree.configure(yscrollcommand=scrollbar.set)

        # 5. Размещаем таблицу и скроллбар на экране
        self.events_tree.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # --- Блок поиска по коду ---
        code_search_frame = tk.Frame(main_frame)
        code_search_frame.pack(fill="x", pady=(5, 0), padx=15)

        tk.Label(code_search_frame, text="Код события:").grid(
            row=0, column=0, sticky="e", padx=5
        )

        self.code_entry = tk.Entry(code_search_frame, width=15)
        self.code_entry.grid(row=0, column=1, sticky="we", padx=5, pady=5)

        search_btn = Button(
            code_search_frame, text="Поиск", command=self.search_by_code
        )
        search_btn.grid(row=0, column=2, sticky="we", padx=5, pady=5)

    def add_entry(self):
        """Обработка нажатия кнопки 'Добавить'."""
        # 1. Получаем данные из полей ввода
        event_text_content = self.event_text.get("1.0", tk.END).strip()
        date_event = self.date_entry.get()
        direction = self.direction_var.get()

        # Проверяем, что поле "Событие" не пустое
        if not event_text_content:
            messagebox.showwarning("Ошибка", "Поле 'Событие' пустое.")
            return

        # 2. Подготавливаем путь к базе данных
        db_folder = os.path.join(os.getcwd(), "project_dbs")
        db_path = os.path.join(db_folder, f"{self.project_code}.db")

        try:
            # Создаем папку, если ее нет, и подключаемся к БД
            os.makedirs(db_folder, exist_ok=True)
            conn = sqlite3.connect(db_path)
            cursor = conn.cursor()

            # Проверяем и исправляем названия колонок, если нужно (для
            # совместимости)
            cursor.execute(f"PRAGMA table_info({TABLE_PROJECT_DATA})")
            columns = [column[1] for column in cursor.fetchall()]

            data_column = "event_data" if "event_data" in columns else "ivent_data"
            code_column = "event_code" if "event_code" in columns else "ivent_code"
            sign_column = "event_sign" if "event_sign" in columns else "ivent_sign"

            # 3. Генерируем новый код события
            cursor.execute(
                f"""
                SELECT COUNT(*)
                FROM {TABLE_PROJECT_DATA}
                WHERE project_code = ? AND indicator_code = ?
            """,
                (self.project_code, self.indicator_code),
            )

            count = cursor.fetchone()[0]
            event_sequence_number = count + 1
            event_code = f"{self.indicator_code}_{event_sequence_number}"

            # 4. Вставляем данные в базу
            cursor.execute(
                f"""
                INSERT INTO {TABLE_PROJECT_DATA} (
                    project_code, group_code, indicator_name, indicator_code,
                    {data_column}, {sign_column}, event_text_content, {code_column}, created_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
            """,
                (
                    self.project_code,
                    self.group_code,
                    self.indicator_name,
                    self.indicator_code,
                    date_event,
                    direction,
                    event_text_content,
                    event_code,
                ),
            )

            # 5. Сохраняем изменения в файл базы данных
            conn.commit()

            # 6. Обновляем интерфейс (ТАБЛИЦУ), если запись прошла успешно
            if conn.total_changes > 0:
                # Определяем текст для колонки "Направленность"
                direction_str = "+" if direction == 1 else "-"

                # Вставляем новую строку в таблицу Treeview
                self.events_tree.insert(
                    "",
                    "end",
                    values=(
                        event_code,  # Код
                        date_event,  # Дата
                        direction_str,  # Направленность
                        event_text_content,  # Текст события
                    ),
                )

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

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

                # Делаем окно "всегда поверх", чтобы оно не уходило на задний
                # план
                self.window.attributes("-topmost", 1)
                self.window.after(100, lambda: self.window.attributes("-topmost", 0))

        except Exception as e:
            messagebox.showerror("Ошибка БД", f"Не удалось записать данные: {e}")
        finally:
            # Гарантируем, что соединение с базой данных будет закрыто
            if "conn" in locals():
                conn.close()

    def load_events(self):
        """Загружает события из базы данных и отображает их в таблице."""
        try:
            for item in self.events_tree.get_children():
                self.events_tree.delete(item)

            db_path = os.path.join("project_dbs", f"{self.project_code}.db")

            if not os.path.exists(db_path):
                return

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

            cursor.execute(
                f"""
                    SELECT event_code, event_data, event_sign, event_text_content
                    FROM {TABLE_PROJECT_DATA}
                    WHERE project_code = ? AND group_code = ? AND indicator_name = ?
                    ORDER BY id DESC
                """,
                (self.project_code, self.group_code, self.indicator_name),
            )

            rows = cursor.fetchall()

            if not rows:
                return

            for row in rows:
                if row and len(row) >= 4:
                    event_code, event_date, sign_val, text_content = row
                    direction_str = "+" if sign_val == 1 else "-"
                    self.events_tree.insert(
                        "",
                        "end",
                        values=(event_code, event_date, direction_str, text_content),
                    )

        except Exception as e:
            pass  # Или можно логировать ошибку в файл, если нужно
        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):
        """Обработка нажатия кнопки 'В архив'."""
        messagebox.showinfo(
            "Успех", f"Проект с кодом '{self.project_code}' архивирован."
        )

    def on_close(self):
        """Закрытие окна ввода событий и возврат в окно выбора."""
        try:
            # Если у нас есть родительское окно (окно выбора) и у него есть
            # метод возврата
            if self.root_window and hasattr(self.root_window, "show_selection_window"):
                # Вызываем метод возврата в главном окне
                self.root_window.show_selection_window()

            # Затем уничтожаем текущее окно ввода событий
            self.window.destroy()

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

    def search_by_code(self):
        """
        Выполняет поиск события по коду.
        Если событие найдено:
        1.  Находит его группу и показатель.
        2.  Меняет текущую группу/показатель в окне.
        3.  Загружет список событий для новой группы.
        4.  Выделяет найденную строку.
        """
        entered_code = self.code_entry.get().strip()

        if not entered_code:
            messagebox.showwarning("Поиск", "Введите код события.")
            return

        db_path = os.path.join("project_dbs", f"{self.project_code}.db")

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

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

            # Запрос: ищем событие по коду и получаем его группу и показатель
            cursor.execute(
                f"""
                    SELECT group_code, indicator_name, indicator_code 
                    FROM {TABLE_PROJECT_DATA}
                    WHERE project_code = ? AND event_code = ?
                    LIMIT 1
                """,
                (self.project_code, entered_code),
            )

            result = cursor.fetchone()

            if not result:
                messagebox.showinfo("Поиск", "Событие с таким кодом не найдено.")
                return

            # Получаем данные о том, где лежит это событие
            found_group_code = result[0]
            found_indicator_name = result[1]
            found_indicator_code = result[2]

            # Проверяем, не находимся ли мы уже в нужной группе/показателе
            current_context = (self.group_code, self.indicator_name)
            new_context = (found_group_code, found_indicator_name)

            # Обновляем контекст окна ТОЛЬКО если он изменился
            if current_context != new_context:
                self.group_code = found_group_code
                self.indicator_name = found_indicator_name
                self.indicator_code = found_indicator_code

                # Обновляем текст в информационном блоке вверху окна
                context_text = (
                    f"Группа: {self.group_code}, Показатель: {self.indicator_name}"
                )
                # Находим виджет Label по его тексту в дочерних окнах и меняем текст
                for child in self.window.winfo_children():
                    if isinstance(child, tk.Frame):  # Ищем фрейм info_frame
                        for label in child.winfo_children():
                            if isinstance(label, tk.Label):
                                label.config(text=context_text)
                                break

                # Перезагружаем список событий для новой группы/показателя,
                # чтобы таблица обновилась
                self.load_events()

            # Теперь ищем строку с кодом в уже загруженной таблице
            self._select_row_by_event_code(entered_code)

        except Exception as e:
            messagebox.showerror("Ошибка БД", f"Ошибка при поиске: {e}")
        finally:
            if "conn" in locals():
                conn.close()

    def _select_row_by_event_code(self, event_code_to_find):
        """
        Вспомогательный метод.
        Выделяет строку с указанным кодом в таблице и прокручивает к ней.
        """
        children = self.events_tree.get_children()

        if not children:
            return

        # Снимаем выделение со всех строк (если было)
        self.events_tree.selection_remove(children)

        # Ищем нужную строку по значению в первом столбце (Код)
        for item in children:
            code_val = self.events_tree.item(item, "values")[0]
            if code_val == event_code_to_find:
                # Выделяем строку (стандартный синий цвет Windows)
                self.events_tree.selection_add(item)

                # Прокручиваем таблицу так, чтобы строка стала видимой
                self.events_tree.see(item)

                # Устанавливаем фокус на таблицу (для стандартной подсветки)
                self.events_tree.focus(item)

                # Выходим из цикла, так как нашли нужное событие
                break

    def on_edit_event(self, event):
        """Обработчик двойного клика по строке в таблице."""
        selected_item = self.events_tree.selection()
        if not selected_item:
            return

        item_values = self.events_tree.item(selected_item[0], "values")
        if not item_values:
            return

        event_code = item_values[0]
        date_val = item_values[1]
        direction_val = 1 if item_values[2] == "+" else -1
        event_text = item_values[3]

        # --- ИЗМЕНЕНИЕ ЗДЕСЬ ---
        # Передаем 'self' как parent_window
        edit_window = EventEditWindow(
            self,  # <-- Вот эта строчка! Передаем текущий объект как родителя
            event_code,
            date_val,
            direction_val,
            event_text,
        )

    def update_event_in_db(self, event_code, new_date, new_direction, new_event_text):
        """Обновляет событие в базе данных и таблице."""
        db_path = os.path.join("project_dbs", f"{self.project_code}.db")

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

            cursor.execute(
                f"""
             UPDATE {TABLE_PROJECT_DATA}
             SET event_data = ?, event_sign = ?, event_text_content = ?
             WHERE event_code = ? AND project_code = ? AND indicator_code = ?
            """,
                (
                    new_date,
                    new_direction,
                    new_event_text,
                    event_code,
                    self.project_code,
                    self.indicator_code,
                ),
            )

            conn.commit()

            if conn.total_changes > 0:
                selected_item = self.events_tree.selection()
                if selected_item:
                    direction_str = "+" if new_direction == 1 else "-"
                    self.events_tree.item(
                        selected_item[0],
                        values=(event_code, new_date, direction_str, new_event_text),
                    )
                    messagebox.showinfo("Успех", "Изменения сохранены.")
            else:
                messagebox.showwarning("Нет изменений", "Данные не были изменены в БД.")

        except Exception as e:
            messagebox.showerror("Ошибка БД", f"Не удалось обновить данные: {e}")
        finally:
            if "conn" in locals():
                conn.close()

    def on_right_click(self, event):
        """Обработчик правого клика по строке в таблице."""
        # Находим элемент под курсором
        item = self.events_tree.identify_row(event.y)
        if not item:
            return

        # Выделяем строку (если не выделена)
        if item not in self.events_tree.selection():
            self.events_tree.selection_set(item)

        # Создаём контекстное меню с пунктом "Удалить"
        menu = tk.Menu(self.window, tearoff=0)
        menu.add_command(label="Удалить", command=lambda: self.delete_selected_event())

        try:
            menu.tk_popup(event.x_root, event.y_root)
        finally:
            menu.grab_release()

    def delete_selected_event(self):
        """Удаляет выбранное событие из таблицы и базы данных."""
        selected_items = self.events_tree.selection()
        if not selected_items:
            return

        item_values = self.events_tree.item(selected_items[0], "values")
        if not item_values:
            return

        event_code_to_delete = item_values[0]

        # Убрал messagebox.askyesno для полной автоматизации (если это тест)
        # Если нужно оставить диалог, верни код с messagebox обратно.

        db_path = os.path.join("project_dbs", f"{self.project_code}.db")

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

            cursor.execute(
                f"""
                DELETE FROM {TABLE_PROJECT_DATA}
                WHERE event_code = ? AND project_code = ? AND indicator_code = ?
            """,
                (event_code_to_delete, self.project_code, self.indicator_code),
            )

            conn.commit()

            if conn.total_changes > 0:
                for item in selected_items:
                    self.events_tree.delete(item)
                self.recalculate_event_codes()

        except Exception as e:
            pass  # Или логирование ошибки
        finally:
            if "conn" in locals():
                conn.close()

    def print_to_word(self):
        """Создаёт и сохраняет файлы Excel и Word с отчётом."""
        # --- 1. ПОДГОТОВИТЕЛЬНЫЙ ЭТАП ---
        import os
        from datetime import datetime

        current_dir = os.path.dirname(os.path.abspath(__file__))
        project_root_dir = os.path.abspath(os.path.join(current_dir, ".."))
        projects_dir = os.path.join(project_root_dir, "projects")
        os.makedirs(projects_dir, exist_ok=True)

        now = datetime.now()
        timestamp = now.strftime("%Y%m%d_%H%M")
        file_base_name = f"{self.project_code}_{timestamp}"

        docx_file_name = f"{file_base_name}.docx"
        docx_file_path = os.path.join(projects_dir, docx_file_name)

        xlsx_file_name = f"{file_base_name}.xlsx"
        xlsx_file_path = os.path.join(projects_dir, xlsx_file_name)

        # --- 2. ПОЛУЧЕНИЕ ДАННЫХ ИЗ БАЗЫ (ГЛАВНОЕ ИЗМЕНЕНИЕ) ---
        # Получаем данные для шапки (Название, Описание и т.д.)
        project_name = "Неизвестный проект"
        project_description = ""
        date_opened = "[Дата открытия]"
        responsible = "[ФИО]"

        try:
            import sqlite3
            from tkinter import messagebox
            import pandas as pd

            db_path = os.path.join("project_dbs", f"{self.project_code}.db")

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

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

            # Запрос для получения данных проекта для шапки
            try:
                cursor.execute(
                    f"""
                        SELECT name, description, date_opened, responsible 
                        FROM {TABLE_PROJECT_DATA}
                        WHERE project_code = ?
                        LIMIT 1
                    """,
                    (self.project_code,),
                )
                header_row = cursor.fetchone()

                # Если данные проекта найдены, используем их
                if header_row:
                    project_name, project_description, date_opened, responsible = (
                        header_row
                    )

                # Запрос для получения списка событий (как и раньше)
                cursor.execute(
                    f"""
                        SELECT event_code, event_data, event_text_content
                        FROM {TABLE_PROJECT_DATA}
                        WHERE project_code = ?
                        ORDER BY group_code, indicator_code, event_data ASC
                    """,
                    (self.project_code,),
                )
                rows = cursor.fetchall()

            except Exception as sql_error:
                messagebox.showerror(
                    "Ошибка SQL", f"Ошибка при выполнении запроса к базе: {sql_error}"
                )
                return
            finally:
                conn.close()

            if not rows:
                messagebox.showwarning("Печать", "Нет событий для печати.")
                return

            valid_rows_data = []
            for row in rows:
                event_code, date_str, event_text = row
                if event_text and date_str:
                    valid_rows_data.append((event_code, date_str, event_text))

            if not valid_rows_data:
                messagebox.showwarning(
                    "Печать",
                    "Нет валидных событий для печати (отсутствует дата или текст).",
                )
                return

        except Exception as e:
            messagebox.showerror(
                "Ошибка подготовки", f"Не удалось подготовить данные: {e}"
            )
            return

        # Флаги для финального сообщения
        word_success = False
        excel_success = False

        # --- 3. СОЗДАНИЕ ВОРД ФАЙЛА ---
        try:
            from docx import Document
            from docx.shared import Pt, Cm, Inches, RGBColor
            from docx.enum.text import WD_ALIGN_PARAGRAPH
            from docx.enum.section import WD_ORIENT
            from docx.enum.table import WD_TABLE_ALIGNMENT, WD_TABLE_DIRECTION

            doc = Document()

            # Настройка страницы (Альбомная)
            section = doc.sections[0]
            section.orientation = WD_ORIENT.LANDSCAPE
            section.page_width = Cm(29.7)
            section.page_height = Cm(21.0)
            section.top_margin = Cm(2)
            section.bottom_margin = Cm(2)
            section.left_margin = Cm(3)
            section.right_margin = Cm(1.5)

            # Настройка шрифта
            style = doc.styles["Normal"]
            font = style.font
            font.name = "Times New Roman"
            font.size = Pt(12)

            # Шапка документа (ИСПРАВЛЕННАЯ ШАПКА)
            doc.add_paragraph("Отчёт о событиях").alignment = WD_ALIGN_PARAGRAPH.CENTER

            # Используем переменные с данными из базы
            doc.add_paragraph(f"Название проекта: {project_name}")
            doc.add_paragraph(f"Код проекта: {self.project_code}")

            # Добавляем Описание (которого раньше не было)
            doc.add_paragraph(f"Описание: {project_description}")

            doc.add_paragraph(f"Дата открытия: {date_opened}")
            doc.add_paragraph(f"Ответственный: {responsible}")
            doc.add_paragraph("")

            # --- СОЗДАНИЕ И ОФОРМЛЕНИЕ ТАБЛИЦЫ ---
            # 1. Создаем таблицу (4 столбца, 1 строка для заголовка)
            table = doc.add_table(rows=1, cols=4, style="Table Grid")
            table.alignment = WD_TABLE_ALIGNMENT.CENTER  # Исправлено на константу
            table.allow_autofit = False
            table.columns[0].width = Cm(2.0)
            table.columns[1].width = Cm(2.5)
            table.columns[2].width = Cm(2.5)
            table.columns[3].width = Inches(7.0)
            table.columns[3].width_rule = 3

            # --- ЗАПОЛНЕНИЕ ТАБЛИЦЫ ---
            hdr_cells = table.rows[0].cells
            hdr_cells[0].text = "№ п/п"
            hdr_cells[1].text = "Код"
            hdr_cells[2].text = "Дата"
            hdr_cells[3].text = "Событие"

            # Выравнивание и жирный шрифт для заголовка + ЗАЛИВКА СЕРЫМ ЦВЕТОМ
            for cell in hdr_cells:
                p = cell.paragraphs[0]
                p.alignment = WD_ALIGN_PARAGRAPH.CENTER
                run = p.runs[0]
                run.font.bold = True

                try:
                    # Пробуем новый метод (для новых версий)
                    shading_elm = cell._tc.get_or_add_tcPr().get_or_add_shd()
                    shading_elm.fill = "D9D9D9"
                    shading_elm.val = "clear"
                except AttributeError:
                    # Если не получилось (для старых версий), используем XML
                    from docx.oxml.ns import nsdecls
                    from docx.oxml import parse_xml

                    cell._tc.get_or_add_tcPr().append(
                        parse_xml(f'<w:shd {nsdecls("w")} w:fill="D9D9D9"/>')
                    )

            for i, (code, date, text) in enumerate(valid_rows_data, start=1):
                cells = table.add_row().cells
                cells[0].text = str(i)
                cells[1].text = code
                cells[2].text = date
                cells[3].text = text

                for j in range(3):
                    cells[j].paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
                cells[3].paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT

            doc.save(docx_file_path)
            word_success = True

        except Exception as e:
            messagebox.showerror("Ошибка Word", f"Не удалось создать Word-файл: {e}")
            word_success = False

        # --- 4. СОЗДАНИЕ ЭКСЕЛЬ ФАЙЛА ---
        try:
            data_for_excel = [
                {"№ п/п": i, "Код": code, "Дата": date, "Событие": text}
                for i, (code, date, text) in enumerate(valid_rows_data, start=1)
            ]

            df = pd.DataFrame(data_for_excel)

            df.to_excel(xlsx_file_path, index=False)

            excel_success = True

        except Exception as e:
            messagebox.showerror("Ошибка Excel", f"Не удалось создать Excel-файл: {e}")
        else:
            pass

        # --- 5. ФИНАЛЬНОЕ СООБЩЕНИЕ ПОЛЬЗОВАТЕЛЮ ---
        if word_success or excel_success:
            message_text = "Файлы успешно сохранены в папку 'projects':\n"
            if word_success:
                message_text += f"- {docx_file_name}\n"
            if excel_success:
                message_text += f"- {xlsx_file_name}\n"

            messagebox.showinfo("Успех", message_text.strip())


class EventEditWindow:
    """
    Окно для редактирования выбранного события.
    """

    def __init__(self, parent_window, event_code, date_val, direction_val, event_text):
        # Сохраняем ссылку на родительское окно и данные
        self.parent_window = parent_window
        self.event_code = event_code
        self.date_val = date_val
        self.direction_val = direction_val

        # Создаем само окно
        self.window = tk.Toplevel(parent_window.window)
        self.window.title("Редактирование событий")
        self.window.geometry("450x220")
        self.window.transient(parent_window.window)
        self.window.grab_set()

        # --- 1. СОЗДАНИЕ ФРЕЙМА И НАСТРОЙКА СЕТКИ ---
        # Используем один фрейм с сеткой для всех элементов.
        # Это самый надежный способ избежать проблем с .pack() и .grid().
        frame = tk.Frame(self.window, padx=10, pady=10)
        frame.pack(fill="both", expand=True)

        # Настраиваем колонки: вторая колонка (индекс 1) будет растягиваться.
        frame.columnconfigure(1, weight=1)

        # --- 2. РАЗМЕЩЕНИЕ ВИДЖЕТОВ В СЕТКЕ ---

        # Поле "Дата" (Строка 0)
        tk.Label(frame, text="Дата:").grid(row=0, column=0, sticky="e", pady=3)
        self.date_entry = tk.Entry(frame, width=25)
        self.date_entry.insert(0, date_val)
        # sticky="we" заставляет поле ввода растягиваться по горизонтали.
        self.date_entry.grid(row=0, column=1, sticky="we", pady=3)

        # Поле "Направленность" (Строка 1)
        tk.Label(frame, text="Направленность:").grid(row=1, column=0, sticky="e")

        # Для радиокнопок создаем отдельный фрейм, чтобы они стояли в одну линию.
        radio_frame = tk.Frame(frame)
        radio_frame.grid(row=1, column=1, sticky="w", pady=3)

        self.dir_var = tk.IntVar(value=direction_val)
        Radiobutton(radio_frame, text="+ К цели", variable=self.dir_var, value=1).pack(
            side="left"
        )
        Radiobutton(
            radio_frame, text="- От цели", variable=self.dir_var, value=-1
        ).pack(side="left", padx=(10, 0))

        # Поле "Событие" (Строка 2)
        tk.Label(frame, text="Событие:").grid(
            row=2, column=0, sticky="ne", pady=(10, 3)
        )

        self.event_text_field = tk.Text(frame, height=4, width=35, wrap="word")
        self.event_text_field.insert("1.0", event_text)

        # Используем sticky="we" здесь тоже для единообразия.
        self.event_text_field.grid(
            row=2, column=1, sticky="we", pady=(10, 3), padx=(0, 10)
        )

        # Кнопка "Сохранить" (Строка 3)
        save_btn = tk.Button(
            frame,
            text="Сохранить изменения",
            command=self.save_and_close,
            bg="#4CAF50",  # Зеленый цвет для красоты
            fg="white",
        )
        # Размещаем кнопку на 3-й строке, растягиваем на обе колонки (columnspan=2)
        save_btn.grid(row=3, columnspan=2, pady=(20, 5), ipadx=20)

    def save_and_close(self):
        """
        Сохраняет изменения из полей ввода,
        передает их в родительское окно и закрывает это окно.
        """
        # 1. Получаем новые значения из интерфейса
        new_date = self.date_entry.get()
        new_direction = self.dir_var.get()
        # Получаем текст из поля. "end-1c" - правильный способ избежать лишней пустой строки.
        new_event_text = self.event_text_field.get("1.0", tk.END).strip()

        # 2. Проверка: не пустое ли поле "Событие"?
        if not new_event_text:
            messagebox.showwarning("Ошибка", 'Поле "Событие" не может быть пустым.')
            return  # Выходим из функции, ничего не сохраняя

        # 3. Передаем данные в родительское окно для обновления БД и таблицы
        # self.parent_window - это ссылка на ProjectEventWindow, которую мы передали в __init__
        self.parent_window.update_event_in_db(
            self.event_code,  # Код события, которое мы редактируем
            new_date,  # Новая дата
            new_direction,  # Новое направление (+1 или -1)
            new_event_text,  # Новый текст
        )

        # 4. Закрываем окно редактирования
        self.window.destroy()

    def on_close(self):
        """Закрытие окна ввода событий.
        Теперь просто уничтожаем окно, не пытаясь ничего вернуть,
        так как за нас это сделал open_event_window_from_list."""
        try:
            # Просто закрываем текущее окно.
            # Логика возврата перенесена в main.py и выполняется там
            # через уничтожение и создание новых окон.
            self.window.destroy()
        except Exception as e:
            print(f"[ДИАГНОСТИКА] Ошибка при закрытии окна: {e}")