import os
import sys
import openpyxl
from threading import Thread
from tkinter import Tk, Label, Entry, Button, filedialog, Text, Frame, Menu, Checkbutton, IntVar, messagebox
import tkinter as tk
from tkinter import ttk
import pandas as pd
from datetime import datetime
import win32com.client as win32
from PIL import Image, ImageTk
import base64
import difflib
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
from openpyxl.styles import Font, Border, Side, PatternFill
from openpyxl.utils import get_column_letter
import threading
import shutil
import time
from tkcalendar import DateEntry
from ttkthemes import ThemedTk
from concurrent.futures import ThreadPoolExecutor

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

class MailApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Автоматизация отправки писем в Outlook")
        self.root.geometry("910x700")

        self.style = ttk.Style(root)
        self.style.theme_use('radiance')

        self.notebook = ttk.Notebook(root)
        self.notebook.pack(fill='both', expand=True)

        self.tab1 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab1, text="Согласование АП и Итогов")
        self.create_tab1()

        self.tab2 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab2, text="Отправка расформировок")
        self.create_tab2()

        self.tab3 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab3, text="Нарезка АП")
        self.create_tab3()

        self.tab4 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab4, text="Объединение файлов")
        self.create_tab4()

        self.tab5 = ttk.Frame(self.notebook)
        self.notebook.add(self.tab5, text="Перемножение ТТ_ТП_Проект")
        self.create_tab5()

        self.mails = []
        self.recipient_file = None

    def create_tab1(self):
        frame = ttk.Frame(self.tab1, padding=10)
        frame.grid(row=0, column=0, sticky="nsew")
        self.tab1.rowconfigure(0, weight=1)
        self.tab1.columnconfigure(0, weight=1)

        ttk.Label(frame, text="Выбранные АП:").grid(row=0, column=0, sticky="w", pady=(0, 5))
        self.files_text = ttk.Entry(frame, width=85)
        self.files_text.grid(row=0, column=1, pady=(0, 5), sticky="ew")
        self.btn_select_files = ttk.Button(frame, text="Выбрать файлы", command=self.select_files, width=15)
        self.btn_select_files.grid(row=0, column=2, pady=(0, 5), padx=(5, 0), sticky="e")

        ttk.Label(frame, text="Дата С (ДД.ММ.ГГГГ):").grid(row=1, column=0, sticky="w", pady=(0, 5))
        self.date_from = DateEntry(frame, width=25, date_pattern='dd.mm.yyyy')
        self.date_from.grid(row=1, column=1, pady=(0, 5), sticky="w")
        self.add_copy_paste_menu(self.date_from)
        
        ttk.Label(frame, text="Дата По (ДД.ММ.ГГГГ):").grid(row=2, column=0, sticky="w", pady=(0, 5))
        self.date_to = DateEntry(frame, width=25, date_pattern='dd.mm.yyyy')
        self.date_to.grid(row=2, column=1, pady=(0, 5), sticky="w")
        self.add_copy_paste_menu(self.date_to)

        ttk.Label(frame, text="Тема письма:").grid(row=3, column=0, sticky="w", pady=(0, 5))
        self.subject_entry = ttk.Combobox(frame, width=100, values=[
            "Согласование АП ДМП МК КА",
            "Согласование АП ДМП ММ КА",
            "Согласование АП ДМП ГМ КА",
            "Согласование итогов акции ДМП МК КА",
            "Согласование итогов акции ДМП ММ КА",
            "Согласование итогов акции ДМП ГМ КА"
        ])
        self.subject_entry.grid(row=3, column=1, columnspan=2, pady=(0, 5), sticky="ew")
        self.add_copy_paste_menu(self.subject_entry)

        ttk.Label(frame, text="Текст письма:").grid(row=4, column=0, sticky="w", pady=(0, 5))
        self.message_text = tk.Text(frame, width=85, height=15, wrap="word")
        self.message_text.grid(row=4, column=1, columnspan=2, pady=(0, 5), sticky="w")
        self.add_copy_paste_menu(self.message_text)

        ttk.Label(frame, text="Выберите вариант текста:").grid(row=5, column=0, sticky="w", pady=(0, 5))
        self.text_option_combobox = ttk.Combobox(frame, width=104, values=[
            "Согласование АП",
            "Согласование итогов"
        ])
        self.text_option_combobox.grid(row=5, column=1, columnspan=2, pady=(0, 5), sticky="ew")
        self.text_option_combobox.bind("<<ComboboxSelected>>", self.insert_text_option)

        ttk.Label(frame, text="Подпись:").grid(row=6, column=0, sticky="nw", pady=(0, 5))
        self.signature_text = tk.Text(frame, width=85, height=5, wrap="word")
        self.signature_text.grid(row=6, column=1, columnspan=2, pady=(0, 5), sticky="w")
        self.add_copy_paste_menu(self.signature_text)

        ttk.Label(frame, text="Выберите подпись:").grid(row=7, column=0, sticky="w", pady=(0, 5))
        self.signature_combobox = ttk.Combobox(frame, width=100, values=[
            "Дубинин Иван Викторович",
            "Ольховая Дарья Владимировна",
            "Чугунова Евгения Игоревна",
            "Миллер Алена Юрьевна",
            "Радимцева Жанна Владимировна",
            "Близнюк Мария Сергеевна"
        ])
        self.signature_combobox.grid(row=7, column=1, columnspan=2, pady=(0, 5), sticky="ew")
        self.signature_combobox.bind("<<ComboboxSelected>>", self.insert_signature)

        self.request_summary_var = IntVar()
        self.request_summary_check = ttk.Checkbutton(frame, text="Запрос итогов", variable=self.request_summary_var)
        self.request_summary_check.grid(row=8, column=1, sticky="w", pady=(0, 5))

        self.request_summary_gm_var = IntVar()
        self.request_summary_gm_check = ttk.Checkbutton(frame, text="Запрос итогов ГМ", variable=self.request_summary_gm_var)
        self.request_summary_gm_check.grid(row=8, column=1, sticky="", pady=(0, 5))

        self.ap_prepak_var = IntVar()
        self.ap_prepak_check = ttk.Checkbutton(frame, text="АП препак", variable=self.ap_prepak_var)
        self.ap_prepak_check.grid(row=8, column=2, sticky="e", pady=(0, 5))

        ttk.Label(frame, text="Файл с адресами:").grid(row=9, column=0, sticky="w", pady=(5, 5))
        self.recipient_file_label = ttk.Entry(frame, width=107)
        self.recipient_file_label.grid(row=9, column=1, columnspan=2, pady=(5, 10), sticky="ew")

        self.btn_select_recipients = ttk.Button(frame, text="Выбрать файл с адресами", command=self.select_recipient_file, width=25)
        self.btn_select_recipients.grid(row=10, column=0, pady=(5, 10), sticky="e")

        self.btn_send = ttk.Button(frame, text="Создать письма", command=self.create_emails, width=20)
        self.btn_send.grid(row=10, column=2, pady=(5, 10), sticky="w")

        self.btn_send_all = ttk.Button(frame, text="Отправить все письма", command=self.confirm_send_all_mails, width=22)
        self.btn_send_all.grid(row=10, column=1, pady=(5, 10))

        for i in range(11):
            frame.rowconfigure(i, weight=1)
        frame.columnconfigure(1, weight=1)

    def select_files(self):
        files = filedialog.askopenfilenames(filetypes=[("Excel files", "*.xlsx *.xls")])
        self.file_paths = list(files)
        self.files_text.delete(0, "end")
        self.files_text.insert(0, f"{len(self.file_paths)} АП выбрано")

    def select_recipient_file(self):
        file = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx *.xls")])
        if file:
            self.recipient_file = file
            self.recipient_file_label.delete(0, "end")
            self.recipient_file_label.insert(0, os.path.basename(file))
            self.recipient_file_label.insert(0, "Выбраны ")

            df = pd.read_excel(self.recipient_file)
            missing_recipients = []
            for file_path in self.file_paths:
                file_name = os.path.basename(file_path).rsplit('.', 1)[0]
                if file_name not in df['Название'].values:
                    missing_recipients.append(file_name)

            if missing_recipients:
                messagebox.showwarning("Внимание", f"Адреса эл.почты не найдены для следующих КА: {', '.join(missing_recipients)}")

    def add_copy_paste_menu(self, widget):
        menu = Menu(widget, tearoff=0)
        menu.add_command(label="Копировать", command=lambda: widget.event_generate("<<Copy>>"))
        menu.add_command(label="Вставить", command=lambda: widget.event_generate("<<Paste>>"))
        widget.bind("<Button-3>", lambda event: menu.tk_popup(event.x_root, event.y_root))

    def insert_signature(self, event):
        signatures = {
            "Дубинин Иван Викторович": "Дубинин Иван Викторович\nГлавный специалист\nСектор сопровождения ДМП малых форматов\n8 (861) 210-98-10, доб. 41594\nКраснодар, ул. Солнечная 15/5, ГК 3, каб. 4313\nСемья магазинов «Магнит»\nРабочий день: 8:15-17:15\nОбед: 12:30-13:30",
            "Ольховая Дарья Владимировна": "Ольховая (Дубинина) Дарья Владимировна\nВедущий специалист\nСектор сопровождения ДМП больших форматов\n8 (861) 210-98-10, доб. 18246\nКраснодар, ул. Солнечная 15/5, ГК, каб. 4313\nСемья магазинов «Магнит»\nmagnit-info.ru",
            "Чугунова Евгения Игоревна": "С уважением,\nЧугунова Евгения Игоревна\nВедущий специалист\nСектор сопровождения ДМП больших форматов\nКраснодар, ул. Солнечная 15/5, ГК 3, каб.4313\nТел. 8 918 144 85 41\nСемья магазинов «Магнит»\nmagnit-info.ru",
            "Миллер Алена Юрьевна": "Миллер Алена Юрьевна\nВедущий специалист\nСектор сопровождения ДМП малых форматов\n8 (861) 210-98-10, доб. 12937\nКраснодар, ул. Солнечная 15/5, ГК 3, каб. 4313\nСемья магазинов «Магнит»\nРабочий день: 8:15-17:15\nОбед: 12:30-13:30",
            "Радимцева Жанна Владимировна": "Радимцева Жанна Владимировна\nГлавный специалист сектора сопровождения ДМП малых форматов\nУправление по аналитике и автоматизации торгового пространства\nДепартамент технологий управления ассортиментом и торговым пространством\nДирекция по развитию сети, продажам и персоналу АО \"Тандер\"\nАО \"Тандер\" 350072, г.Краснодар, ул.Солнечная, 15/5\nКаб.4313. ГК3\nтел.:   (861) 210-98-10\nwww: www.magnit-info.ru",
            "Близнюк Мария Сергеевна": "Близнюк Мария Сергеевна"
        }

        selected_signature = self.signature_combobox.get()
        if selected_signature in signatures:
            self.signature_text.delete("1.0", "end")
            self.signature_text.insert("1.0", signatures[selected_signature])

    def insert_text_option(self, event):
        selected_option = self.text_option_combobox.get()
        date_from = self.date_from.get_date().strftime("%d.%m.%Y")
        date_to = self.date_to.get_date().strftime("%d.%m.%Y")

        if selected_option == "Согласование АП":
            text = f"""Добрый день!

Уважаемые партнеры! 
Обращаем внимание, что данное письмо носит исключительно информационный характер, подтверждение и согласование АП с вашей стороны не требует.
Проекты ДМП будут реализованы, согласно информации во вложении на основании поданных и забронированных данных менеджерами промо категории.

Во вложении АП по проектам ДМП формата МК на период с {date_from} по {date_to}
Загрузка рассчитана исходя из прогноза продаж и размера оборудования, проставлена в столбце АМПО. Просьба ознакомиться.

Спасибо!"""
        elif selected_option == "Согласование итогов":
            text = f"""Добрый день!

Уважаемые партнеры! 
В период с {date_from} по {date_to} компания 
Тандер провела акцию Дополнительной выкладки в формате МК. 
Необходимо согласовать итоги акции. 
При согласовании итогов просьба указывать номера претензий, если имеются претензионные ТТ. 

Позиции в акцию!"""
        else:
            text = ""

        self.message_text.delete("1.0", "end")
        self.message_text.insert("1.0", text)

    def create_emails(self):
        if not self.file_paths or not self.recipient_file:
            messagebox.showwarning("Внимание", "Выберите АП и файл с адресами КА!")
            return

        try:
            df = pd.read_excel(self.recipient_file)

            if not {'Название', 'Адрес', 'Копия'}.issubset(df.columns):
                messagebox.showerror("Ошибка", "Файл с адресами должен содержать столбцы: Название, Адрес, Копия")
                return
            
            outlook = win32.Dispatch("Outlook.Application")

            for file_path in self.file_paths:
                file_name = os.path.basename(file_path).rsplit('.', 1)[0]
                
                if self.ap_prepak_var.get():
                    df_file = pd.read_excel(file_path)
                    date_from = pd.to_datetime(df_file.at[0, 'Период с']).strftime("%d.%m.%Y")
                    date_to = pd.to_datetime(df_file.at[0, 'Период по']).strftime("%d.%m.%Y")
                else:
                    date_from = self.date_from.get_date().strftime("%d.%m.%Y")
                    date_to = self.date_to.get_date().strftime("%d.%m.%Y")

                subject = f"{self.subject_entry.get()} {file_name} с {date_from} по {date_to}"
                
                message_body = self.message_text.get("1.0", "end").strip().replace("\n", "<br>")
                signature = self.signature_text.get("1.0", "end").strip().replace("\n", "<br>")

                recipients = df[df['Название'] == file_name]
                if recipients.empty:
                    messagebox.showwarning("Внимание", f"Нет адресов для {file_name}. Пропускаем.")
                    continue
                
                to_emails = "; ".join(recipients['Адрес'].dropna().astype(str).tolist())
                cc_emails = "; ".join(recipients['Копия'].dropna().astype(str).tolist())

                mail = outlook.CreateItem(0)
                mail.Subject = subject
                mail.HTMLBody = f"{message_body}<br><br>{signature}"

                if self.request_summary_var.get():
                    summary_data = pd.read_excel(file_path, sheet_name=1, usecols=[10, 11, 12])
                    summary_data = summary_data.dropna(how='all')
                    summary_data = summary_data.dropna(how='any')
                    if not summary_data.empty:
                        summary_html = summary_data.to_html(index=False, header=False, border=1)
                        mail.HTMLBody = f"{message_body}<br><br>{summary_html}<br><br>{signature}"

                if self.request_summary_gm_var.get():
                    summary_data_gm = pd.read_excel(file_path, sheet_name=1, usecols=[10, 11, 12, 13, 14])
                    summary_data_gm = summary_data_gm.dropna(how='all')
                    summary_data_gm = summary_data_gm.dropna(how='any')
                    if not summary_data_gm.empty:
                        summary_html_gm = summary_data_gm.to_html(index=False, header=True, border=1)
                        mail.HTMLBody = f"{message_body}<br><br>{summary_html_gm}<br><br>{signature}"

                if not self.request_summary_var.get():
                    mail.Attachments.Add(file_path)

                mail.To = to_emails
                mail.CC = cc_emails

                self.mails.append(mail)

                mail.Display()

            messagebox.showinfo("Готово", "Письма готовы для отправки!.")

        except Exception as e:
            messagebox.showerror("Ошибка", f"Произошла ошибка: {e}")

    def confirm_send_all_mails(self):
        if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите отправить все открытые письма?"):
            self.send_all_mails()

    def send_all_mails(self):
        if not self.mails:
            messagebox.showwarning("Внимание", "Нет писем для отправки.")
            return

        for mail in self.mails:
            mail.Send()

        messagebox.showinfo("Готово", "Все письма отправлены.")

    def create_tab2(self):
        frame = ttk.Frame(self.tab2, padding=10)
        frame.grid(row=0, column=0, sticky="nsew")
        self.tab2.rowconfigure(0, weight=1)
        self.tab2.columnconfigure(0, weight=1)

        def choose_files():
            file_paths = filedialog.askopenfilenames(filetypes=[("Excel files", "*.xlsx *.xls")])
            if file_paths:
                file_entry.delete(0, tk.END)
                file_entry.insert(0, "; ".join(file_paths))

        def choose_photo_folder():
            folder_path = filedialog.askdirectory()
            if folder_path:
                photo_folder_entry.delete(0, tk.END)
                photo_folder_entry.insert(0, folder_path)

                missing_photos = []
                for file_path in file_entry.get().split("; "):
                    file_name = os.path.basename(file_path).split('.')[0]
                    photo_file_path = os.path.join(folder_path, f"{file_name}.jpg")
                    if not os.path.exists(photo_file_path):
                        missing_photos.append(file_name)

                if missing_photos:
                    messagebox.showwarning("Внимание", f"Фото не найдено для проектов: {', '.join(missing_photos)}")

        def close_program():
            if messagebox.askyesno("Закрыть программу", "Вы уверены, что хотите закрыть программу?"):
                root.destroy()

        def create_html_table(df):
            return df.to_html(index=False, escape=False, border=1)

        def get_image_html(file_path):
            with open(file_path, "rb") as img_file:
                encoded_string = base64.b64encode(img_file.read()).decode()
            return f'<img src="data:image/jpeg;base64,{encoded_string}" width="300"/>'

        def run_program():
            file_paths = file_entry.get().split("; ")
            photo_folder = photo_folder_entry.get()
            signature = self.signature_entry_tab2.get("1.0", "end").strip().replace("\n", "<br>")

            if not file_paths or not all(os.path.exists(file_path) for file_path in file_paths):
                messagebox.showerror("Ошибка", "Файл(ы) не выбраны или путь неверен.")
                return

            if not os.path.isdir(photo_folder):
                messagebox.showerror("Ошибка", "Папка с фотографиями не выбрана или путь неверен.")
                return

            outlook = win32.Dispatch('outlook.application')

            for file_path in file_paths:
                file_name = os.path.basename(file_path).split('.')[0]

                try:
                    df = pd.read_excel(file_path)
                    date_from = df.at[1, 'Дата начала акции'].strftime("%d.%m.%Y")
                    date_to = df.at[1, 'Дата окончания акции'].strftime("%d.%m.%Y")
                except Exception as e:
                    messagebox.showerror("Ошибка", f"Не удалось прочитать файл {file_name}: {str(e)}")
                    return

                df_unique_first = df[['Код препака', 'Наименование препака']].drop_duplicates()
                df_unique_second = df[['Наименование проекта', 'Код ТП', 'Наименование ТП', 'Загрузка оборудования']].drop_duplicates()

                html_table_first = create_html_table(df_unique_first)
                html_table_second = create_html_table(df_unique_second)

                mail = outlook.CreateItem(0)

                mail.To = "mark_m@magnit.ru"
                mail.Subject = f"Расформировка препак «{file_name}» период {date_from} - {date_to}"
                mail.BodyFormat = 2

                photo_file_path = os.path.join(photo_folder, f"{file_name}.jpg")
                img_html = ""
                if os.path.exists(photo_file_path):
                    img_html = f"<p>Фото:</p>{get_image_html(photo_file_path)}"
                elif search_similar_var.get():
                    similar_photos = difflib.get_close_matches(file_name, os.listdir(photo_folder), n=1, cutoff=0.8)
                    if similar_photos:
                        similar_photo_path = os.path.join(photo_folder, similar_photos[0])
                        img_html = f"<p>Фото:</p>{get_image_html(similar_photo_path)}"
                    else:
                        messagebox.showwarning("Внимание", f"Фото не найдено для файла {file_name} и похожих названий.")
                else:
                    messagebox.showwarning("Внимание", f"Фото не найдено для файла {file_name}.")

                mail.Attachments.Add(file_path)

                mail.HTMLBody = f"""<p>Добрый день,</p>
                                    <p>Требуется согласовать расформировку «{file_name}» за период {date_from} - {date_to}.</p>
                                    <p>Данные по препакам:</p>
                                    {html_table_first}
                                    <p>Данные по проектам:</p>
                                    {html_table_second}
                                    {img_html}
                                    <br><br>{signature}"""
                self.mails.append(mail)

                mail.Display()

        ttk.Label(frame, text="Выберите файлы расформировки:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
        file_entry = ttk.Entry(frame, width=93)
        file_entry.grid(row=0, column=1, padx=5, pady=5, sticky="w")
        ttk.Button(frame, text="Выбрать", command=choose_files).grid(row=0, column=2, padx=5, pady=5, sticky="e")

        ttk.Label(frame, text="Выберите папку с фото:").grid(row=1, column=0, sticky="w", padx=5, pady=5)
        photo_folder_entry = ttk.Entry(frame, width=93)
        photo_folder_entry.grid(row=1, column=1, padx=5, pady=5, sticky="w")
        ttk.Button(frame, text="Выбрать", command=choose_photo_folder).grid(row=1, column=2, padx=5, pady=5, sticky="e")

        ttk.Label(frame, text="Подпись:").grid(row=2, column=0, sticky="w", padx=5, pady=5)
        self.signature_entry_tab2 = tk.Text(frame, width=80, height=20, wrap="word")
        self.signature_entry_tab2.grid(row=2, column=1, columnspan=2, pady=(5, 5), sticky="w")
        self.add_copy_paste_menu(self.signature_entry_tab2)

        ttk.Label(frame, text="Выберите подпись:").grid(row=3, column=0, sticky="w", pady=(0, 5))
        self.signature_combobox_tab2 = ttk.Combobox(frame, width=104, values=[
            "Дубинин Иван Викторович",
            "Ольховая Дарья Владимировна",
            "Чугунова Евгения Игоревна",
            "Миллер Алена Юрьевна",
            "Радимцева Жанна Владимировна",
            "Близнюк Мария Сергеевна"
        ])
        self.signature_combobox_tab2.grid(row=3, column=1, columnspan=2, pady=(0, 5), sticky="w")
        self.signature_combobox_tab2.bind("<<ComboboxSelected>>", self.insert_signature_tab2)

        search_similar_var = IntVar()
        search_similar_check = ttk.Checkbutton(frame, text="Искать похожие", variable=search_similar_var)
        search_similar_check.grid(row=4, column=1, sticky="w", pady=(5, 5), padx=(5, 5))

        frame_bottom = ttk.Frame(frame)
        frame_bottom.grid(row=5, column=1, columnspan=3, pady=(5, 5))

        ttk.Button(frame_bottom, text="Создать письма", command=run_program).grid(row=0, column=0, padx=10, pady=0, sticky="w")
        ttk.Button(frame_bottom, text="Закрыть программу", command=close_program).grid(row=0, column=1, padx=10, pady=0, sticky="")

        self.btn_send_all = ttk.Button(frame_bottom, text="Отправить все письма", command=self.confirm_send_all_mails, width=20)
        self.btn_send_all.grid(row=0, column=2, padx=10, pady=0, sticky="e")

        for i in range(6):
            frame.rowconfigure(i, weight=1)
        frame.columnconfigure(1, weight=1)

    def insert_signature_tab2(self, event):
        signatures = {
            "Дубинин Иван Викторович": "Дубинин Иван Викторович\nГлавный специалист\nСектор сопровождения ДМП малых форматов\n8 (861) 210-98-10, доб. 41594\nКраснодар, ул. Солнечная 15/5, ГК 3, каб. 4313\nСемья магазинов «Магнит»\nРабочий день: 8:15-17:15\nОбед: 12:30-13:30",
            "Ольховая Дарья Владимировна": "Ольховая (Дубинина) Дарья Владимировна\nВедущий специалист\nСектор сопровождения ДМП больших форматов\n8 (861) 210-98-10, доб. 18246\nКраснодар, ул. Солнечная 15/5, ГК, каб. 4313\nСемья магазинов «Магнит»\nmagnit-info.ru",
            "Чугунова Евгения Игоревна": "С уважением,\nЧугунова Евгения Игоревна\nВедущий специалист\nСектор сопровождения ДМП больших форматов\nКраснодар, ул. Солнечная 15/5, ГК 3, каб.4313\nТел. 8 918 144 85 41\nСемья магазинов «Магнит»\nmagnit-info.ru",
            "Миллер Алена Юрьевна": "Миллер Алена Юрьевна\nВедущий специалист\nСектор сопровождения ДМП малых форматов\n8 (861) 210-98-10, доб. 12937\nКраснодар, ул. Солнечная 15/5, ГК 3, каб. 4313\nСемья магазинов «Магнит»\nРабочий день: 8:15-17:15\nОбед: 12:30-13:30",
            "Радимцева Жанна Владимировна": "Радимцева Жанна Владимировна\nГлавный специалист сектора сопровождения ДМП малых форматов\nУправление по аналитике и автоматизации торгового пространства\nДепартамент технологий управления ассортиментом и торговым пространством\nДирекция по развитию сети, продажам и персоналу АО \"Тандер\"\nАО \"Тандер\" 350072, г.Краснодар, ул.Солнечная, 15/5\nКаб.4313. ГК3\nтел.:   (861) 210-98-10\nwww: www.magnit-info.ru",
            "Близнюк Мария Сергеевна": "Близнюк Мария Сергеевна"
        }

        selected_signature = self.signature_combobox_tab2.get()
        if selected_signature in signatures:
            self.signature_entry_tab2.delete("1.0", "end")
            self.signature_entry_tab2.insert("1.0", signatures[selected_signature])

    def create_tab3(self):
        frame = ttk.Frame(self.tab3, padding=10)
        frame.grid(row=0, column=0, sticky="nsew")
        self.tab3.rowconfigure(0, weight=1)
        self.tab3.columnconfigure(0, weight=1)

        self.source_file_path = tk.StringVar()
        self.save_folder_path = tk.StringVar()
        self.column_name = tk.StringVar()
        
        self.stop_flag = False
        self.decommission_flag = tk.BooleanVar(value=False)
        self.ap_for_gm_flag = tk.BooleanVar(value=False)
        
        self.unique_values = []
        
        self.create_widgets_tab3(frame)
    
    def create_widgets_tab3(self, frame):
        self.menu = tk.Menu(self.root)
        self.root.config(menu=self.menu)
        
        templates_menu = tk.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label="Шаблоны", menu=templates_menu, font=("Arial", 14, "bold"))
    
        templates_menu.add_command(label="Шаблон ГМ.xlsx", command=lambda: self.download_template("Шаблон ГМ.xlsx"))
        templates_menu.add_command(label="Шаблон МК.xlsx", command=lambda: self.download_template("Шаблон МК.xlsx"))
        templates_menu.add_command(label="Шаблон ММ.xlsx", command=lambda: self.download_template("Шаблон ММ.xlsx"))
        templates_menu.add_command(label="Шаблон Препаки.xlsx", command=lambda: self.download_template("Шаблон Препаки.xlsx"))
        templates_menu.add_command(label="Шаблон Расформировки.xlsx", command=lambda: self.download_template("Шаблон Расформировки.xlsx"))
        templates_menu.add_command(label="Шаблон Адреса КА.xlsx", command=lambda: self.download_template("Шаблон Адреса КА.xlsx"))
        templates_menu.add_command(label="Шаблон перемножение ТТ_ТП_Проект.xlsx", command=lambda: self.download_template("Шаблон перемножение ТТ_ТП_Проект.xlsx"))
        
        source_label = ttk.Label(frame, text="Исходный шаблон:")
        source_label.grid(row=0, column=0, padx=10, pady=10)
        
        source_entry = ttk.Entry(frame, width=80, textvariable=self.source_file_path)
        source_entry.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
        
        browse_button = ttk.Button(frame, text="Выбрать файл", command=self.select_source_file)
        browse_button.grid(row=0, column=2, padx=10, pady=10)
        
        save_label = ttk.Label(frame, text="Папка для сохранения:")
        save_label.grid(row=1, column=0, padx=10, pady=10)
        
        save_entry = ttk.Entry(frame, width=80, textvariable=self.save_folder_path)
        save_entry.grid(row=1, column=1, padx=10, pady=10, sticky="ew")
        
        save_browse_button = ttk.Button(frame, text="Выбрать папку", command=self.select_save_folder)
        save_browse_button.grid(row=1, column=2, padx=10, pady=10)
        
        columns_label = ttk.Label(frame, text="Выбор принципа нарезки АП:")
        columns_label.grid(row=2, column=0, padx=10, pady=10)
        
        self.columns_combobox = ttk.Combobox(frame, values=[], textvariable=self.column_name)
        self.columns_combobox.grid(row=2, column=1, padx=10, pady=10, sticky="w")
        
        decommission_checkbox = ttk.Checkbutton(frame, text="Расформировка", variable=self.decommission_flag)
        decommission_checkbox.grid(row=2, column=2, padx=10, pady=10)
        
        ap_for_gm_checkbox = ttk.Checkbutton(frame, text="АП для ГМ", variable=self.ap_for_gm_flag)
        ap_for_gm_checkbox.grid(row=2, column=1, padx=10, pady=10, sticky="e")
        
        self.log_text_tab3 = tk.Text(frame, width=80, height=23)
        self.log_text_tab3.grid(row=3, columnspan=4, sticky='ew', padx=10, pady=10)
        self.log_text_tab3.config(wrap='word', state='disabled')
        
        self.progress_bar_tab3 = ttk.Progressbar(frame, orient='horizontal', mode='determinate')
        self.progress_bar_tab3.grid(row=4, columnspan=4, sticky='ew', padx=10, pady=20)
        
        start_button = ttk.Button(frame, text="Начать нарезку", command=self.start_splitting)
        start_button.grid(row=5, column=0, padx=10, pady=10)
        start_button.config(style='Green.TButton')
        
        stop_button = ttk.Button(frame, text="Остановить", command=self.stop_splitting)
        stop_button.grid(row=5, column=1, padx=10, pady=10)
        stop_button.config(style='Red.TButton')
        
        close_button = ttk.Button(frame, text="Закрыть", command=self.close_program)
        close_button.grid(row=5, column=2, padx=10, pady=10)
        
        self.style = ttk.Style()
        self.style.configure('Green.TButton', foreground='green')
        self.style.configure('Red.TButton', foreground='red')

    def select_source_file(self):
        file_path = filedialog.askopenfilename(filetypes=[('Excel files', '*.xlsx')])
        if file_path:
            self.source_file_path.set(file_path)
            df = pd.read_excel(file_path)
            columns = list(df.columns)
            self.columns_combobox['values'] = columns
            self.log(self.log_text_tab3, f"Выбран исходный файл: {file_path}")

    def select_save_folder(self):
        folder_path = filedialog.askdirectory()
        if folder_path:
            self.save_folder_path.set(folder_path)
            self.log(self.log_text_tab3, f"Выбрана папка для сохранения: {folder_path}")

    def start_splitting(self):
        if not self.source_file_path.get():
            messagebox.showerror("Ошибка", "Не выбран исходный файл.")
            return
        
        if not self.save_folder_path.get():
            messagebox.showerror("Ошибка", "Не выбрана папка для сохранения.")
            return
        
        if not self.column_name.get():
            messagebox.showerror("Ошибка", "Не выбран столбец для разделения.")
            return
        
        self.stop_flag = False
        
        self.start_time = time.time()
        threading.Thread(target=self.split_files, args=(self.log_text_tab3, self.progress_bar_tab3)).start()
        
        self.log(self.log_text_tab3, "Процесс нарезки запущен, пожалуйста, подождите...")

    def stop_splitting(self):
        self.stop_flag = True
        self.log(self.log_text_tab3, "Процесс нарезки остановлен по запросу пользователя.")

    def split_files(self, log_text, progress_bar):
        df = pd.read_excel(self.source_file_path.get())
        unique_values = df[self.column_name.get()].unique().tolist()
        total_files = len(unique_values)
        
        for i, value in enumerate(unique_values):
            if self.stop_flag:
                break
            
            filtered_df = df.loc[df[self.column_name.get()] == value]
            filename = f"{value}.xlsx"
            filepath = os.path.join(self.save_folder_path.get(), filename)
            
            wb = Workbook()
            ws = wb.active
            ws.title = "АП"
            for r in dataframe_to_rows(filtered_df, index=False, header=True):
                ws.append(r)
            
            self.apply_formatting(ws)
            
            if not self.decommission_flag.get():
                ws_count = wb.create_sheet('Количество ТТ')
                
                unique_projects = filtered_df[['Наименование проекта', 'Тип_0']].drop_duplicates()
                for r in dataframe_to_rows(unique_projects, index=False, header=True):
                    ws_count.append(r)
                
                for row in ws_count.iter_rows(min_row=1, max_row=unique_projects.shape[0] + 1, min_col=1, max_col=2):
                    for cell in row:
                        ws_count.cell(row=cell.row, column=cell.col_idx + 10, value=cell.value)
                
                for row in ws_count.iter_rows(min_row=1, max_row=unique_projects.shape[0] + 1, min_col=1, max_col=2):
                    for cell in row:
                        cell.value = None
                
                project_counts = filtered_df.groupby('Наименование проекта')['Код ТТ'].nunique().reset_index()
                project_counts.columns = ['Наименование проекта', 'Итог']
                
                ws_count.cell(row=1, column=13, value='Итог')
                
                for index, row in project_counts.iterrows():
                    project_name = row['Наименование проекта']
                    count = row['Итог']
                    for cell in ws_count['K']:
                        if cell.value == project_name:
                            ws_count.cell(row=cell.row, column=13, value=count)
                
                if self.ap_for_gm_flag.get():
                    ws_count.cell(row=1, column=14, value='Количество ДМП')
                    ws_count.cell(row=1, column=15, value='Сумма ДМП')
                    
                    dmp_counts = filtered_df.groupby('Наименование проекта')['Количество ДМП на 1ТТ'].mean().reset_index()
                    dmp_counts.columns = ['Наименование проекта', 'Количество ДМП']
                    
                    for index, row in dmp_counts.iterrows():
                        project_name = row['Наименование проекта']
                        dmp_count = row['Количество ДМП']
                        for cell in ws_count['K']:
                            if cell.value == project_name:
                                ws_count.cell(row=cell.row, column=14, value=dmp_count)
                                total_dmp = ws_count.cell(row=cell.row, column=13).value * dmp_count
                                ws_count.cell(row=cell.row, column=15, value=total_dmp)
                
                self.apply_formatting(ws_count)
                
                for col in range(1, 11):
                    ws_count.column_dimensions[get_column_letter(col)].hidden = True
                
                for column in ws_count.columns:
                    max_length = 0
                    column = [cell for cell in column]
                    for cell in column:
                        try:
                            if len(str(cell.value)) > max_length:
                                max_length = len(cell.value)
                        except:
                            pass
                    adjusted_width = (max_length + 2) * 1.2
                    ws_count.column_dimensions[column[0].column_letter].width = adjusted_width
            
            wb.save(filepath)
            
            self.update_progress(progress_bar, i + 1, total_files)
        
        end_time = time.time()
        elapsed_time = end_time - self.start_time
        elapsed_message = f"Время выполнения: {elapsed_time:.2f} секунд."
        
        if self.stop_flag:
            self.root.after(0, self.show_stop_message, log_text)
        else:
            self.root.after(0, self.show_completion_message, log_text, elapsed_message)

    def apply_formatting(self, ws):
        font = Font(name='Tahoma', size=12)
        border = Border(left=Side(style='thin'), 
                        right=Side(style='thin'), 
                        top=Side(style='thin'), 
                        bottom=Side(style='thin'))
        
        header_fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid')
        
        for row in ws.iter_rows(min_row=1, max_row=ws.max_row, min_col=1, max_col=ws.max_column):
            for cell in row:
                if cell.value:
                    cell.font = font
                    cell.border = border
                    if cell.row == 1:
                        cell.fill = header_fill
        
        date_column_titles = ["Период с", "Период по", "Дата начала акции", "Дата окончания акции"]
        date_format = 'dd/mm/yyyy'

        column_mapping = {cell.value: cell.column_letter for cell in ws[1] if cell.value in date_column_titles}
        
        for title, col_letter in column_mapping.items():
            for cell in ws[col_letter][1:]:
                cell.number_format = date_format

    def update_progress(self, progress_bar, current, total):
        progress = int((current / total) * 100)
        progress_bar['value'] = progress
        self.root.update_idletasks()

    def show_completion_message(self, log_text, elapsed_message):
        messagebox.showinfo("Завершено", "Нарезка файлов завершена.")
        self.log(log_text, "Нарезка файлов завершена.")
        self.log(log_text, elapsed_message)

    def show_stop_message(self, log_text):
        messagebox.showinfo("Остановлено", "Процесс нарезки остановлен.")
        self.log(log_text, "Процесс нарезки остановлен.")

    def log(self, log_text, message):
        log_text.config(state='normal')
        log_text.insert(tk.END, f"{message}\n")
        log_text.see(tk.END)
        log_text.config(state='disabled')

    def close_program(self):
        if messagebox.askokcancel("Подтверждение", "Вы уверены, что хотите выйти?"):
            self.root.destroy()

    def download_template(self, template_name):
        template_path = resource_path(f"templates/{template_name}")
        
        save_path = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx")], initialfile=template_name)
        if save_path:
            shutil.copyfile(template_path, save_path)
            self.log(self.log_text_tab3, f"Шаблон {template_name} успешно скачан и сохранен в {save_path}")

    def create_tab4(self):
        frame = ttk.Frame(self.tab4, padding=10)
        frame.grid(row=0, column=0, sticky="nsew")
        self.tab4.rowconfigure(0, weight=1)
        self.tab4.columnconfigure(0, weight=1)

        self.file_paths = []
        self.merge_different_structure = tk.BooleanVar()
        self.save_path_var = tk.StringVar()

        self.create_widgets_tab4(frame)

    def create_widgets_tab4(self, frame):
        style = ttk.Style()
        style.configure("Green.TButton", foreground="green")
        style.configure("Red.TButton", foreground="red")

        self.file_frame = ttk.LabelFrame(frame, text="Выберите файлы для объединения")
        self.file_frame.grid(row=0, column=0, columnspan=3, padx=10, pady=10, sticky="nsew")

        self.file_listbox = tk.Listbox(self.file_frame, selectmode=tk.MULTIPLE)
        self.file_listbox.pack(fill="both", expand=True)

        self.button_frame = ttk.Frame(self.file_frame)
        self.button_frame.pack(pady=5)

        self.add_files_button = ttk.Button(self.button_frame, text="Добавить файлы", command=self.add_files_tab4)
        self.add_files_button.pack(side="left", padx=5)

        self.remove_files_button = ttk.Button(self.button_frame, text="Удалить файлы", command=self.remove_files_tab4)
        self.remove_files_button.pack(side="left", padx=5)

        self.save_frame = ttk.LabelFrame(frame, text="Выберите путь для сохранения")
        self.save_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky="nsew")

        self.save_path_entry = ttk.Entry(self.save_frame, textvariable=self.save_path_var, state="readonly")
        self.save_path_entry.pack(fill="x", padx=5, pady=5)

        self.browse_button = ttk.Button(self.save_frame, text="Обзор", command=self.browse_save_path_tab4)
        self.browse_button.pack(pady=5)

        self.structure_frame = ttk.LabelFrame(frame, text="Настройки объединения")
        self.structure_frame.grid(row=2, column=0, columnspan=3, padx=10, pady=10, sticky="nsew")

        self.structure_checkbox = ttk.Checkbutton(self.structure_frame, text="Объединение файлов с разной структурой", variable=self.merge_different_structure)
        self.structure_checkbox.pack(pady=5)

        self.log_frame = ttk.LabelFrame(frame, text="Логи")
        self.log_frame.grid(row=3, column=0, columnspan=3, padx=10, pady=10, sticky="nsew")

        self.log_text_tab4 = tk.Text(self.log_frame, wrap=tk.WORD, state=tk.DISABLED, height=7)
        self.log_text_tab4.pack(fill="both", expand=True)

        self.progress_button_frame = ttk.Frame(frame)
        self.progress_button_frame.grid(row=4, column=0, columnspan=3, padx=10, pady=10, sticky="nsew")

        self.progress_bar_tab4 = ttk.Progressbar(self.progress_button_frame, orient="horizontal", length=300, mode="determinate")
        self.progress_bar_tab4.pack(side="left", padx=5, pady=5, expand=True, fill="x")

        self.merge_button = ttk.Button(self.progress_button_frame, text="Объединить", command=self.start_merge_thread_tab4, style="Green.TButton")
        self.merge_button.pack(side="left", padx=5)

        self.close_button = ttk.Button(self.progress_button_frame, text="Закрыть", command=self.close_app_tab4, style="Red.TButton")
        self.close_button.pack(side="right", padx=5)

        for i in range(5):
            frame.rowconfigure(i, weight=1)
        for i in range(3):
            frame.columnconfigure(i, weight=1)

    def add_files_tab4(self):
        files = filedialog.askopenfilenames(filetypes=[("Excel files", "*.xlsx;*.xls")])
        for file in files:
            self.file_paths.append(file)
            self.file_listbox.insert(tk.END, os.path.basename(file))
            self.log(self.log_text_tab4, f"Добавлен файл: {os.path.basename(file)}")

    def remove_files_tab4(self):
        selected_indices = self.file_listbox.curselection()
        for index in reversed(selected_indices):
            self.log(self.log_text_tab4, f"Удален файл: {self.file_listbox.get(index)}")
            self.file_listbox.delete(index)
            self.file_paths.pop(index)

    def browse_save_path_tab4(self):
        save_path = filedialog.askdirectory()
        self.save_path_var.set(save_path)
        self.log(self.log_text_tab4, f"Выбран путь для сохранения: {save_path}")

    def start_merge_thread_tab4(self):
        merge_thread = threading.Thread(target=self.merge_files_tab4, args=(self.log_text_tab4, self.progress_bar_tab4))
        merge_thread.start()

    def merge_files_tab4(self, log_text, progress_bar):
        files = self.file_paths
        save_path = self.save_path_var.get()
        merge_different_structure = self.merge_different_structure.get()

        if not files or not save_path:
            messagebox.showwarning("Предупреждение", "Пожалуйста, выберите файлы и путь для сохранения.")
            return

        try:
            self.log(log_text, "Начало объединения файлов...")
            self.update_progress_bar_tab4(progress_bar, 0, len(files))

            start_time = time.time()

            df = pd.read_excel(files[0], engine='openpyxl')
            header = df.columns
            self.log(log_text, f"Загружен файл: {files[0]}")
            self.update_progress_bar_tab4(progress_bar, 1)

            with ThreadPoolExecutor() as executor:
                futures = [executor.submit(pd.read_excel, file, engine='openpyxl') for file in files[1:]]
                for i, future in enumerate(futures, start=2):
                    df_temp = future.result()
                    self.log(log_text, f"Загружен файл: {files[i - 1]}")

                    if merge_different_structure:
                        df_temp = df_temp.reindex(columns=header, fill_value='')

                    df = pd.concat([df, df_temp], ignore_index=True)
                    self.update_progress_bar_tab4(progress_bar, i)

            self.log(log_text, "Все файлы загружены, идет процесс сохранения документа, пожалуйста подождите!")

            current_datetime = datetime.now().strftime("%d.%m.%Y - %H.%M.%S")
            save_path = os.path.join(save_path, f"Объединённый файл {current_datetime}.xlsx")

            save_dir = os.path.dirname(save_path)
            if not os.path.exists(save_dir):
                os.makedirs(save_dir)

            self.save_excel_tab4(df, save_path, header)
            self.log(log_text, f"Сохранен файл: {save_path}")

            end_time = time.time()
            elapsed_time = end_time - start_time
            elapsed_time_str = time.strftime("%M:%S", time.gmtime(elapsed_time))
            self.log(log_text, f"Объединение завершено. Время выполнения: {elapsed_time_str}")

            messagebox.showinfo("Успех", f"Объединение завершено. Файл сохранен по пути: {save_path}")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Произошла ошибка: {str(e)}")
            self.log(log_text, f"Ошибка: {str(e)}")

    def save_excel_tab4(self, df, save_path, header):
        wb = Workbook()
        ws = wb.active

        font = Font(name="Tahoma", size=12)
        border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
        light_blue_fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")

        for r_idx, row in enumerate(dataframe_to_rows(df, index=False, header=True), 1):
            for c_idx, value in enumerate(row, 1):
                cell = ws.cell(row=r_idx, column=c_idx, value=value)
                cell.font = font
                cell.border = border
                if r_idx == 1 and value:
                    cell.fill = light_blue_fill

        wb.save(save_path)

    def log(self, log_text, message):
        log_text.config(state=tk.NORMAL)
        log_text.insert(tk.END, message + "\n")
        log_text.config(state=tk.DISABLED)
        log_text.yview(tk.END)

    def update_progress_bar_tab4(self, progress_bar, value, maximum=None):
        if maximum is not None:
            progress_bar["maximum"] = maximum
        progress_bar["value"] = value
        self.root.update_idletasks()

    def close_app_tab4(self):
        if messagebox.askyesno("Закрыть программу", "Вы действительно хотите закрыть программу?"):
            self.root.destroy()

    def create_tab5(self):
        frame = ttk.Frame(self.tab5, padding=10)
        frame.grid(row=0, column=0, sticky="nsew")
        self.tab5.rowconfigure(0, weight=1)
        self.tab5.columnconfigure(0, weight=1)

        self.template_path_var = tk.StringVar()
        self.result_path_var = tk.StringVar()

        ttk.Label(frame, text="Выберите шаблон:").grid(row=0, column=0, padx=10, pady=5, sticky="w")
        ttk.Entry(frame, textvariable=self.template_path_var, width=90).grid(row=0, column=1, padx=10, pady=5, sticky="we")
        ttk.Button(frame, text="Выбрать", command=self.select_template).grid(row=0, column=2, padx=10, pady=5, sticky="e")

        ttk.Label(frame, text="Сохранить результат:").grid(row=1, column=0, padx=10, pady=5, sticky="w")
        ttk.Entry(frame, textvariable=self.result_path_var, width=90).grid(row=1, column=1, padx=10, pady=5, sticky="we")
        ttk.Button(frame, text="Выбрать папку", command=self.select_result_folder).grid(row=1, column=2, padx=10, pady=5, sticky="e")

        button_frame = ttk.Frame(frame)
        button_frame.grid(row=2, column=0, columnspan=3, pady=20, sticky="we")

        start_button = ttk.Button(button_frame, text="Запустить", command=self.start_processing, style="Start.TButton")
        start_button.pack(side=tk.LEFT, padx=10)

        close_button = ttk.Button(button_frame, text="Закрыть", command=self.confirm_close, style="Close.TButton")
        close_button.pack(side=tk.RIGHT, padx=10)

        open_button = ttk.Button(button_frame, text="Открыть итоговый файл", command=self.open_result_file, style="Open.TButton")
        open_button.pack(side=tk.LEFT, padx=10)

        ttk.Label(frame, text="Логи:").grid(row=3, column=0, padx=10, pady=5, sticky="w")

        self.log_text_tab5 = tk.Text(frame, height=25, width=60)
        self.log_text_tab5.grid(row=3, column=1, columnspan=2, padx=10, pady=5, sticky="nsew")

        ttk.Label(frame, text="Прогресс:").grid(row=4, column=0, padx=10, pady=5, sticky="w")

        self.progress_bar_tab5 = ttk.Progressbar(frame, orient="horizontal", length=300, mode="determinate")
        self.progress_bar_tab5.grid(row=4, column=1, columnspan=2, padx=10, pady=5, sticky="we")

        self.style = ttk.Style()
        self.style.configure("Start.TButton", foreground="green")
        self.style.configure("Close.TButton", foreground="red")
        self.style.configure("Open.TButton", foreground="orange")

    def select_template(self):
        file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")])
        if file_path:
            self.template_path_var.set(file_path)
            self.log_projects_in_template(file_path)

    def log_projects_in_template(self, file_path):
        try:
            df_tp = pd.read_excel(file_path, sheet_name="ТП")
            df_tt = pd.read_excel(file_path, sheet_name="ТТ")

            unique_projects_tp = df_tp['Наименование проекта'].unique()
            unique_projects_tt = df_tt['Наименование проекта'].unique()

            self.log_text_tab5.insert(tk.END, "Проекты в шаблоне:\n")
            self.log_text_tab5.insert(tk.END, "Из листа 'ТП':\n")
            for project in unique_projects_tp:
                self.log_text_tab5.insert(tk.END, f"- {project}\n")
            self.log_text_tab5.insert(tk.END, "Из листа 'ТТ':\n")
            for project in unique_projects_tt:
                self.log_text_tab5.insert(tk.END, f"- {project}\n")
            self.log_text_tab5.see(tk.END)
            self.root.update()

        except Exception as e:
            self.log_text_tab5.insert(tk.END, f"Ошибка при чтении шаблона: {str(e)}\n")
            self.log_text_tab5.see(tk.END)
            self.root.update()

    def select_result_folder(self):
        folder_path = filedialog.askdirectory()
        if folder_path:
            self.result_path_var.set(folder_path)

    def start_processing(self):
        template_path = self.template_path_var.get()
        result_folder = self.result_path_var.get()

        if not template_path or not result_folder:
            messagebox.showerror("Ошибка", "Пожалуйста, выберите шаблон и папку для сохранения результата.")
            return

        self.log_text_tab5.insert(tk.END, "Начало обработки...\n")
        self.log_text_tab5.see(tk.END)
        self.root.update()

        Thread(target=self.process_data, args=(template_path, result_folder, self.log_text_tab5, self.progress_bar_tab5)).start()

    def process_data(self, template_path, result_folder, log_text, progress_bar):
        start_time = time.time()

        try:
            df_tp = pd.read_excel(template_path, sheet_name="ТП")
            df_tt = pd.read_excel(template_path, sheet_name="ТТ")

            unique_projects = df_tp['Наименование проекта'].unique()
            total_projects = len(unique_projects)
            progress_bar["maximum"] = total_projects

            with ThreadPoolExecutor() as executor:
                futures = []
                for i, project in enumerate(unique_projects):
                    futures.append(executor.submit(self.process_project, df_tp, df_tt, project, i, total_projects))

                result_data = []
                for future in futures:
                    data, log_message = future.result()
                    result_data.extend(data)
                    log_text.insert(tk.END, log_message)
                    log_text.see(tk.END)
                    self.root.update()

                    self.root.after(0, self.update_progress_bar_tab5, progress_bar, len(result_data))

            result_df = pd.DataFrame(result_data, columns=[
                'Наименование проекта', 'Код ТП', 'Наименование ТП', 'Код ТТ', 'Наименование ТТ'
            ])

            self.save_result(result_df, result_folder)

            end_time = time.time()
            elapsed_time = end_time - start_time
            elapsed_time_formatted = time.strftime("%M:%S", time.gmtime(elapsed_time))

            log_text.insert(tk.END, f"Готово. Время выполнения: {elapsed_time_formatted}\n")
            log_text.insert(tk.END, f"Результат сохранен в файл: {os.path.join(result_folder, self.result_file_name)}\n")
            log_text.see(tk.END)
            self.root.update()

            unique_projects_count = result_df['Наименование проекта'].nunique()
            log_text.insert(tk.END, f"Количество объединенных проектов: {unique_projects_count}\n")
            log_text.see(tk.END)
            self.root.update()

        except Exception as e:
            log_text.insert(tk.END, f"Ошибка: {str(e)}\n")
            log_text.see(tk.END)
            self.root.update()

    def process_project(self, df_tp, df_tt, project, i, total_projects):
        tp_filtered = df_tp[df_tp['Наименование проекта'] == project]
        tt_filtered = df_tt[df_tt['Наименование проекта'] == project]

        result_data = []
        for _, tp_row in tp_filtered.iterrows():
            for _, tt_row in tt_filtered.iterrows():
                result_data.append([
                    tp_row['Наименование проекта'],
                    tp_row['Код ТП'],
                    tp_row['Наименование ТП'],
                    tt_row['Код ТТ'],
                    tt_row['Наименование ТТ']
                ])

        log_message = f"Обработка проекта: {project}\nОбработано проектов: {i + 1}/{total_projects}\n"
        return result_data, log_message

    def save_result(self, result_df, result_folder):
        current_time = datetime.now().strftime("%d.%m.%Y - %H.%M.%S")
        self.result_file_name = f"Перемноженные ТТ_ТП {current_time}.xlsx"
        result_path = os.path.join(result_folder, self.result_file_name)

        with pd.ExcelWriter(result_path, engine='openpyxl') as writer:
            result_df.to_excel(writer, index=False, sheet_name='Результат')
            workbook = writer.book
            worksheet = writer.sheets['Результат']

            for column_cells in worksheet.columns:
                for cell in column_cells:
                    cell.font = openpyxl.styles.Font(name='Tahoma', size=12)
                    cell.border = openpyxl.styles.Border(left=openpyxl.styles.Side(style='thin'),
                                                         right=openpyxl.styles.Side(style='thin'),
                                                         top=openpyxl.styles.Side(style='thin'),
                                                         bottom=openpyxl.styles.Side(style='thin'))

            for cell in worksheet[1]:
                cell.fill = openpyxl.styles.PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid')

    def confirm_close(self):
        if messagebox.askyesno("Подтверждение", "Вы действительно хотите закрыть программу?"):
            self.root.destroy()

    def open_result_file(self):
        result_folder = self.result_path_var.get()
        if result_folder and hasattr(self, 'result_file_name'):
            result_path = os.path.join(result_folder, self.result_file_name)
            if os.path.exists(result_path):
                os.startfile(result_path)
            else:
                messagebox.showerror("Ошибка", "Файл результата не найден.")

    def update_progress_bar_tab5(self, progress_bar, value):
        progress_bar["value"] = value
        self.root.update_idletasks()

if __name__ == "__main__":
    root = ThemedTk(theme="radiance")
    app = MailApp(root)
    root.mainloop() - перепиши всю программу на PyQt6 - UI должен быть современным, красивым, удобным, симметричным, объемным, с использованием теней, с бело-зеленым цветовым акцентом. При этом весь функционал должен сохраниться