import os
import pandas as pd
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QPushButton, QListWidget, QLabel, QFileDialog, QMessageBox, QTextEdit
from PyQt6.QtGui import QFontDatabase, QFont
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from reportlab.lib.pagesizes import landscape, A4
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.utils import ImageReader
from datetime import datetime
import logging

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

# Загрузка шрифтов DejaVuSans и DejaVuSans-Bold
pdfmetrics.registerFont(TTFont('DejaVuSans', 'DejaVuSans.ttf'))
pdfmetrics.registerFont(TTFont('DejaVuSans-Bold', 'DejaVuSans-Bold.ttf'))

# Установка размера страницы 297x419 мм
custom_page_size = (297 * 2.83465, 419 * 2.83465)  # Перевод мм в точки (1 мм = 2.83465 точки)

class PDFGeneratorThread(QThread):
    log_signal = pyqtSignal(str)

    def __init__(self, folder_paths, save_path, photo_files):
        super().__init__()
        self.folder_paths = folder_paths
        self.save_path = save_path
        self.photo_files = photo_files

    def run(self):
        data = self.aggregate_data()
        photo_data = self.aggregate_photo_data()
        self.generate_pdfs(data, photo_data)

    def aggregate_data(self):
        data = pd.DataFrame()
        for folder in self.folder_paths:
            for file in os.listdir(folder):
                if file.endswith(".xlsx") or file.endswith(".xls"):
                    file_path = os.path.join(folder, file)
                    try:
                        xls = pd.ExcelFile(file_path)
                        for sheet_name in xls.sheet_names:
                            file_data = pd.read_excel(file_path, sheet_name=sheet_name)
                            data = pd.concat([data, file_data], ignore_index=True)
                            self.log_signal.emit(f"Прочитан файл: {file_path}, лист: {sheet_name}")
                    except Exception as e:
                        self.log_signal.emit(f"Ошибка при чтении файла {file_path}: {e}")
        # Конвертируем даты, игнорируя ошибки
        data["Период с"] = pd.to_datetime(data["Период с"], format="%d.%m.%Y", errors='coerce')
        data["Период по"] = pd.to_datetime(data["Период по"], format="%d.%m.%Y", errors='coerce')
        return data

    def aggregate_photo_data(self):
        photo_data = pd.DataFrame()
        for file_path in self.photo_files:
            try:
                file_data = pd.read_excel(file_path)
                photo_data = pd.concat([photo_data, file_data], ignore_index=True)
                self.log_signal.emit(f"Прочитан файл с фотографиями: {file_path}")
            except Exception as e:
                self.log_signal.emit(f"Ошибка при чтении файла {file_path}: {e}")
        return photo_data

    def generate_pdfs(self, data, photo_data):
        # Группируем данные по торговым точкам
        data_by_tt = data.groupby("Код ТТ")
        for tt_code, tt_data in data_by_tt:
            tt_name = tt_data["Название ТТ"].iloc[0]
            period_start = tt_data["Период с"].min()
            period_end = tt_data["Период по"].max()

            # Преобразуем даты в строки для имени файла
            period_start_str = period_start.strftime('%Y-%m-%d') if pd.notnull(period_start) else "unknown_start"
            period_end_str = period_end.strftime('%Y-%m-%d') if pd.notnull(period_end) else "unknown_end"

            # Имя PDF файла
            pdf_name = f"{tt_code}_{tt_name}_{period_start_str}_{period_end_str}.pdf"
            pdf_path = os.path.join(self.save_path, pdf_name)
            
            # Создание PDF
            c = canvas.Canvas(pdf_path, pagesize=custom_page_size)
            width, height = custom_page_size

            # Заголовок
            c.setFont("DejaVuSans", 10)
            y_position = height - 50
            c.drawString(30, y_position, "Администратору необходимо осуществить выкладку товара на ДМП:")
            y_position -= 20

            # Группируем данные по типу оборудования
            equipment_types = ["Магнит-хит", "Навесное оборудование", "Оборудование КА", "Оборудование сети", "Желтый акционный стеллаж"]
            for equipment_type in equipment_types:
                eq_data = tt_data[tt_data["Тип оборудования"] == equipment_type]
                if not eq_data.empty:
                    # Выводим заголовок типа оборудования
                    c.setFont("DejaVuSans-Bold", 10)
                    c.drawString(30, y_position, f"Оборудование: {equipment_type}")
                    y_position -= 20

                    # Основные данные по каждому типу оборудования
                    for project, project_data in eq_data.groupby("Наименование проекта"):
                        c.setFont("DejaVuSans", 8)
                        project_text = f"Проект: {project}\n"
                        if equipment_type == "Магнит-хит" or equipment_type == "Оборудование сети":
                            project_text += f"Название оборудования: {project_data['Наименование оборудования'].iloc[0]}\n"
                            project_text += f"Полный период размещения: {project_data['Полный период размещения'].iloc[0]}\n"
                            project_text += f"Ссылка на планограмму: {project_data['Ссылка на ПГ'].iloc[0]}\n"
                            project_text += f"Комментарий: {project_data['Комментарий'].iloc[0] if 'Комментарий' in project_data.columns else '-'}"
                        elif equipment_type == "Навесное оборудование" or equipment_type == "Оборудование КА":
                            project_text += f"Зона размещения: {project_data['Зона размещения'].iloc[0]}\n"
                            project_text += f"Название оборудования: {project_data['Наименование оборудования'].iloc[0]}\n"
                            project_text += f"Полный период размещения: {project_data['Полный период размещения'].iloc[0]}\n"
                            project_text += f"Действия ТТ по окончанию размещения: {project_data['Действия ТТ по окончанию размещения'].iloc[0]}\n"
                            project_text += f"Комментарий: {project_data['Комментарий'].iloc[0] if 'Комментарий' in project_data.columns else '-'}"
                        elif equipment_type == "Желтый акционный стеллаж":
                            project_text += f"Зона размещения: {project_data['Зона размещения'].iloc[0]}\n"
                            project_text += f"Ссылка на планограмму: {project_data['Ссылка на ПГ'].iloc[0]}\n"
                            project_text += f"Комментарий: {project_data['Комментарий'].iloc[0] if 'Комментарий' in project_data.columns else '-'}"
                        
                        # Сохраняем начальную позицию для расчета высоты фотографии
                        initial_y_position = y_position

                        # Разбиваем текст на строки, если он превышает 2/3 ширины листа
                        max_width = width * 2 / 4
                        for line in project_text.split("\n"):
                            wrapped_lines = self.wrap_text(c, line, max_width)
                            for wrapped_line in wrapped_lines:
                                c.drawString(70, y_position, wrapped_line)
                                y_position -= 15

                        # Создание таблицы с уникальными данными
                        if equipment_type == "Магнит-хит" or equipment_type == "Оборудование сети" or equipment_type == "Желтый акционный стеллаж":
                            table_columns = ["Код позиции", "Наименование позиции", "Загрузка в шт", "Место выкладки товара (адрес)"]
                        elif equipment_type == "Навесное оборудование" or equipment_type == "Оборудование КА":
                            table_columns = ["Код позиции", "Наименование позиции", "Загрузка в шт"]

                        if all(col in project_data.columns for col in table_columns):
                            table_data = project_data[table_columns].drop_duplicates()
                            table_data = table_data.fillna("-")
                            c.drawString(50, y_position, "Таблица данных:")
                            y_position -= 15
                            
                            # Выделение всех границ таблицы
                            col_widths = [max(table_data[col].astype(str).apply(len).max() * 6, 50) for col in table_columns]
                            row_height = 15
                            table_width = sum(col_widths)
                            table_height = len(table_data) * row_height

                            # Рисуем таблицу
                            for i, row in enumerate(table_data.itertuples()):
                                for j, value in enumerate(row[1:]):
                                    c.rect(50 + sum(col_widths[:j]), y_position - (i + 1) * row_height, col_widths[j], row_height)
                                    c.drawString(55 + sum(col_widths[:j]), y_position - (i + 1) * row_height + 3, str(value))
                            
                            y_position -= table_height

                            # Добавление фотографии оборудования
                            photo_rows = photo_data.loc[photo_data['Название проекта'] == project]
                            if not photo_rows.empty:
                                photo_path = photo_rows['Путь'].iloc[0]
                                if os.path.exists(photo_path):
                                    img = ImageReader(photo_path)
                                    img_width, img_height = img.getSize()
                                    aspect_ratio = img_height / img_width
                                    img_width = min(width - table_width - 100, img_width)
                                    img_height = img_width * aspect_ratio
                                    img_height = min(initial_y_position - y_position - 15, img_height)
                                    c.drawImage(img, width - img_width - 30, initial_y_position - img_height, width=img_width, height=img_height)
                                else:
                                    self.log_signal.emit(f"Фотография для проекта {project} не найдена.")
                            else:
                                self.log_signal.emit(f"Фотография для проекта {project} не найдена.")

                        # Добавляем дополнительный отступ между проектами
                        y_position -= 20

                        # Проверка на необходимость создания новой страницы
                        if y_position < 100:
                            c.showPage()
                            y_position = height - 50
                            c.setFont("DejaVuSans", 10)
                            c.drawString(30, y_position, "")
                            y_position -= 20

            c.save()
            self.log_signal.emit(f"Создан PDF файл: {pdf_path}")

        self.log_signal.emit("PDF файлы успешно созданы!")

    def wrap_text(self, canvas, text, max_width):
        """Разбивает текст на строки, если он превышает максимальную ширину."""
        words = text.split()
        lines = []
        current_line = ""
        for word in words:
            test_line = current_line + word + " "
            width = canvas.stringWidth(test_line, "DejaVuSans", 8)
            if width > max_width:
                lines.append(current_line.strip())
                current_line = word + " "
            else:
                current_line = test_line
        lines.append(current_line.strip())
        return lines

class PDFGeneratorApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Excel to PDF Converter")
        self.folder_paths = []
        self.save_path = ""
        self.photo_files = []

        # Основной виджет
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        # Основной макет
        main_layout = QVBoxLayout()
        main_widget.setLayout(main_layout)

        # Список для отображения выбранных путей к папкам
        self.folder_listbox = QListWidget()
        main_layout.addWidget(self.folder_listbox)

        # Кнопки для управления папками
        folder_buttons_layout = QHBoxLayout()
        main_layout.addLayout(folder_buttons_layout)

        self.add_folder_button = QPushButton("Добавить папку")
        self.add_folder_button.clicked.connect(self.add_folder)
        folder_buttons_layout.addWidget(self.add_folder_button)

        self.remove_folder_button = QPushButton("Удалить папку")
        self.remove_folder_button.clicked.connect(self.remove_folder)
        folder_buttons_layout.addWidget(self.remove_folder_button)

        # Кнопка для выбора пути сохранения
        self.save_path_button = QPushButton("Выбрать путь для сохранения PDF")
        self.save_path_button.clicked.connect(self.choose_save_path)
        main_layout.addWidget(self.save_path_button)

        # Поле для отображения пути для сохранения PDF
        self.save_path_label = QLabel("Путь для сохранения PDF не выбран")
        main_layout.addWidget(self.save_path_label)

        # Кнопка для выбора файлов с фотографиями
        self.photo_files_button = QPushButton("Выбрать файлы с фотографиями")
        self.photo_files_button.clicked.connect(self.choose_photo_files)
        main_layout.addWidget(self.photo_files_button)

        # Поле для отображения пути к файлам с фотографиями
        self.photo_files_label = QLabel("Файлы с фотографиями не выбраны")
        main_layout.addWidget(self.photo_files_label)

        # Кнопка для запуска программы
        self.start_button = QPushButton("Запуск")
        self.start_button.clicked.connect(self.start_processing)
        main_layout.addWidget(self.start_button)

        # Кнопка для закрытия программы
        self.close_button = QPushButton("Закрыть")
        self.close_button.clicked.connect(self.close)
        main_layout.addWidget(self.close_button)

        # Поле для вывода логов
        self.log_text_edit = QTextEdit()
        self.log_text_edit.setReadOnly(True)
        main_layout.addWidget(self.log_text_edit)

        # Применение стилей
        self.apply_styles()

    def apply_styles(self):
        stylesheet = """
        QMainWindow {
            background-color: #f0f0f0;
        }
        QPushButton {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            font-size: 14px;
            margin: 5px;
        }
        QPushButton:hover {
            background-color: #45a049;
        }
        QListWidget {
            background-color: white;
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 10px;
            font-size: 14px;
        }
        QLabel {
            font-size: 14px;
            color: #333;
        }
        QTextEdit {
            background-color: white;
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 10px;
            font-size: 12px;
        }
        """
        self.setStyleSheet(stylesheet)

    def add_folder(self):
        folder_path = QFileDialog.getExistingDirectory(self, "Выберите папку")
        if folder_path:
            self.folder_paths.append(folder_path)
            self.folder_listbox.addItem(folder_path)
            self.log_info(f"Добавлена папка: {folder_path}")

    def remove_folder(self):
        selected_items = self.folder_listbox.selectedItems()
        for item in selected_items:
            self.folder_paths.remove(item.text())
            self.folder_listbox.takeItem(self.folder_listbox.row(item))
            self.log_info(f"Удалена папка: {item.text()}")

    def choose_save_path(self):
        self.save_path = QFileDialog.getExistingDirectory(self, "Выберите путь для сохранения PDF")
        if self.save_path:
            self.save_path_label.setText(f"Сохранение в: {self.save_path}")
            self.log_info(f"Выбран путь для сохранения PDF: {self.save_path}")

    def choose_photo_files(self):
        file_paths, _ = QFileDialog.getOpenFileNames(self, "Выберите файлы с фотографиями", "", "Excel files (*.xlsx *.xls)")
        if file_paths:
            self.photo_files = file_paths
            self.photo_files_label.setText(f"Выбраны файлы: {', '.join(file_paths)}")
            self.log_info(f"Выбраны файлы с фотографиями: {', '.join(file_paths)}")

    def start_processing(self):
        if not self.folder_paths:
            QMessageBox.warning(self, "Ошибка", "Пожалуйста, выберите хотя бы одну папку с файлами Excel.")
            self.log_error("Ошибка: не выбрана ни одна папка с файлами Excel.")
            return
        if not self.save_path:
            QMessageBox.warning(self, "Ошибка", "Пожалуйста, выберите путь для сохранения PDF файлов.")
            self.log_error("Ошибка: не выбран путь для сохранения PDF файлов.")
            return
        if not self.photo_files:
            QMessageBox.warning(self, "Ошибка", "Пожалуйста, выберите файлы с фотографиями.")
            self.log_error("Ошибка: не выбраны файлы с фотографиями.")
            return

        self.pdf_thread = PDFGeneratorThread(self.folder_paths, self.save_path, self.photo_files)
        self.pdf_thread.log_signal.connect(self.log_info)
        self.pdf_thread.start()

    def log_info(self, message):
        self.log_text_edit.append(f"INFO: {message}")

    def log_error(self, message):
        self.log_text_edit.append(f"ERROR: {message}")

if __name__ == "__main__":
    app = QApplication([])
    window = PDFGeneratorApp()
    window.show()
    app.exec() - Текст в таблицах написанный на кириллице не читаемый, вместо текста черные квадрат. Так же необходимо Установить размера страницы 297x419 мм. Так же чтобы таблицы и изображение помещались по ширине на лист