"""
DragDropMixin - Mixin für Drag-and-Drop-Funktionalität.

Dieses Mixin enthält alle Methoden zur Verarbeitung von Drag-and-Drop-Operationen
für das MainWindow.
"""

import logging
from pathlib import Path

from PySide6.QtGui import QDragEnterEvent, QDropEvent
from PySide6.QtWidgets import QMessageBox, QProgressBar

from ui.XmlToXslAssignDialog import XmlToXslAssignDialog
from ui.threads import XmlBatchProcessingThread

logger = logging.getLogger(__name__)


class DragDropMixin:
    """
    Mixin für Drag-and-Drop-Funktionalität.

    Dieses Mixin wird von MainWindow verwendet und erwartet folgende Attribute:
    - self.ui: Das UI-Objekt mit treeWidget
    - self.project: Das aktuelle Projekt
    - self.pdf_project: Die Projekt-Daten (ProjectData)
    - self.batch_processing_thread: Thread für Batch-Verarbeitung
    - self.batch_progress_bar: QProgressBar für Batch-Fortschritt
    - self._save_project_settings(): Methode zum Speichern der Projekt-Einstellungen
    - self._load_nodes_to_tree(): Methode zum Laden der Nodes in den Tree
    """

    def _setup_drag_drop(self):
        """Aktiviert Drag&Drop für das TreeWidget."""
        try:
            # Aktiviere Drag&Drop für das TreeWidget
            self.ui.treeWidget.setAcceptDrops(True)
            self.ui.treeWidget.setDragDropMode(self.ui.treeWidget.DragDropMode.DropOnly)

            # Überschreibe die Drag&Drop-Events
            self.ui.treeWidget.dragEnterEvent = self.tree_drag_enter_event
            self.ui.treeWidget.dragMoveEvent = self.tree_drag_move_event
            self.ui.treeWidget.dropEvent = self.tree_drop_event

            logger.debug("Drag&Drop für TreeWidget aktiviert")

        except Exception as e:
            logger.error(f"Fehler beim Aktivieren von Drag&Drop: {e}")

    def tree_drag_enter_event(self, event: QDragEnterEvent):
        """
        Wird ausgeführt, wenn ein Drag-Vorgang über das TreeWidget beginnt.

        Args:
            event: Das Drag-Enter-Event
        """
        try:
            # Prüfe ob URLs (Dateien) gedraggt werden
            if event.mimeData().hasUrls():
                urls = event.mimeData().urls()

                # Prüfe ob mindestens eine XML-Datei dabei ist
                xml_files = [url.toLocalFile() for url in urls if url.toLocalFile().lower().endswith(".xml")]

                if xml_files:
                    event.acceptProposedAction()
                    logger.debug(f"Drag-Enter akzeptiert: {len(xml_files)} XML-Dateien")
                else:
                    event.ignore()
                    logger.debug("Drag-Enter ignoriert: Keine XML-Dateien")
            else:
                event.ignore()
                logger.debug("Drag-Enter ignoriert: Keine URLs")

        except Exception as e:
            logger.error(f"Fehler in tree_drag_enter_event: {e}")
            event.ignore()

    def tree_drag_move_event(self, event):
        """
        Wird ausgeführt, wenn ein Drag-Vorgang über das TreeWidget bewegt wird.

        Args:
            event: Das Drag-Move-Event
        """
        try:
            # Prüfe ob URLs (Dateien) gedraggt werden
            if event.mimeData().hasUrls():
                urls = event.mimeData().urls()

                # Prüfe ob mindestens eine XML-Datei dabei ist
                xml_files = [url.toLocalFile() for url in urls if url.toLocalFile().lower().endswith(".xml")]

                if xml_files:
                    event.acceptProposedAction()
                else:
                    event.ignore()
            else:
                event.ignore()

        except Exception as e:
            logger.error(f"Fehler in tree_drag_move_event: {e}")
            event.ignore()

    def tree_drop_event(self, event: QDropEvent):
        """
        Wird ausgeführt, wenn Dateien auf das TreeWidget gedroppt werden.

        Args:
            event: Das Drop-Event
        """
        try:
            # Prüfe ob ein Projekt geladen ist
            if not hasattr(self, "project") or not self.project:
                QMessageBox.warning(self, "Warnung", "Kein Projekt geladen. Bitte öffnen Sie zuerst ein Projekt.")
                event.ignore()
                return

            if not hasattr(self, "pdf_project") or not self.pdf_project:
                QMessageBox.warning(self, "Warnung", "Keine Projekt-Einstellungen geladen.")
                event.ignore()
                return

            # Hole die URLs aus dem Drop-Event
            if not event.mimeData().hasUrls():
                event.ignore()
                return

            urls = event.mimeData().urls()
            xml_files = []

            # Sammle alle XML-Dateien
            for url in urls:
                file_path = url.toLocalFile()
                if file_path.lower().endswith(".xml"):
                    xml_files.append(Path(file_path))

            if not xml_files:
                QMessageBox.information(self, "Information", "Keine XML-Dateien zum Hinzufügen gefunden.")
                event.ignore()
                return

            logger.info(f"Drop-Event: {len(xml_files)} XML-Dateien erkannt")

            # Verarbeite alle XML-Dateien mit optionalem "Alle zuordnen" Feature
            self._handle_multiple_xml_files_drop(xml_files)

            event.acceptProposedAction()

        except Exception as e:
            error_msg = f"Fehler beim Verarbeiten des Drop-Events: {str(e)}"
            logger.error(error_msg)
            QMessageBox.critical(self, "Fehler", error_msg)
            event.ignore()

    def _handle_multiple_xml_files_drop(self, xml_files: list):
        """
        Verarbeitet mehrere XML-Dateien asynchron per Drag&Drop.
        Zeigt einen Dialog zur Auswahl der XSL-Knoten und startet dann die Batch-Verarbeitung im Hintergrund.

        Args:
            xml_files: Liste von Pfaden zu XML-Dateien
        """
        if not xml_files:
            return

        try:
            # Prüfe ob Projekt-Nodes verfügbar sind
            if not self.pdf_project or not self.pdf_project.nodes:
                QMessageBox.warning(self, "Warnung", "Keine Projekt-Nodes verfügbar für die Zuordnung.")
                return

            # Zeige Dialog für die erste Datei
            dialog = XmlToXslAssignDialog(parent=self, xml_file_path=xml_files[0], project_nodes=self.pdf_project.nodes)

            if dialog.exec() != XmlToXslAssignDialog.DialogCode.Accepted:
                logger.debug("Dialog abgebrochen - keine Dateien verarbeitet")
                return

            # Hole die ausgewählten XSL-Knoten
            selected_xsl_nodes = dialog.get_selected_xsl_nodes()

            if not selected_xsl_nodes:
                logger.warning("Keine XSL-Knoten ausgewählt")
                return

            # Prüfe ob "Alle zuordnen" aktiviert wurde
            apply_to_all = dialog.is_apply_to_all()

            # Bestimme welche Dateien verarbeitet werden sollen
            files_to_process = xml_files if apply_to_all else [xml_files[0]]

            # Stoppe vorherigen Batch-Thread falls noch aktiv
            if self.batch_processing_thread and self.batch_processing_thread.isRunning():
                self.batch_processing_thread.quit()
                self.batch_processing_thread.wait()

            # Zusätzliche Sicherheitsprüfung für project_dir
            if not self.project or not self.project.project_dir:
                QMessageBox.warning(self, "Fehler", "Projekt-Verzeichnis ist nicht verfügbar")
                return

            # Erstelle und starte neuen Batch-Verarbeitungs-Thread
            self.batch_processing_thread = XmlBatchProcessingThread(
                xml_files=files_to_process,
                selected_xsl_nodes=selected_xsl_nodes,
                project_dir=Path(self.project.project_dir),
                pdf_project=self.pdf_project,
            )

            # Verbinde Signale
            self.batch_processing_thread.progress_update.connect(self._on_batch_progress_update)
            self.batch_processing_thread.processing_finished.connect(self._on_batch_processing_finished)

            # Zeige Progressbar
            self._show_batch_progress_bar(len(files_to_process))

            # Starte Thread
            self.batch_processing_thread.start()

            logger.info(
                f"Batch-Verarbeitung von {len(files_to_process)} Datei(en) gestartet (apply_to_all={apply_to_all})"
            )

        except Exception as e:
            error_msg = f"Fehler beim Starten der Batch-Verarbeitung: {str(e)}"
            logger.error(error_msg)
            QMessageBox.critical(self, "Fehler", error_msg)

    def _show_batch_progress_bar(self, total_files: int):
        """
        Zeigt einen Progressbar in der Statusbar für die Batch-Verarbeitung.

        Args:
            total_files: Gesamtanzahl der zu verarbeitenden Dateien
        """
        if self.batch_progress_bar is None:
            self.batch_progress_bar = QProgressBar()
            self.batch_progress_bar.setMaximumHeight(20)
            self.batch_progress_bar.setMaximumWidth(300)

        self.batch_progress_bar.setMinimum(0)
        self.batch_progress_bar.setMaximum(total_files)
        self.batch_progress_bar.setValue(0)
        self.batch_progress_bar.setFormat("%v/%m Dateien")

        # Füge Progressbar zur Statusbar hinzu
        self.statusBar().addPermanentWidget(self.batch_progress_bar)
        self.batch_progress_bar.show()

    def _hide_batch_progress_bar(self):
        """Versteckt und entfernt den Progressbar aus der Statusbar."""
        if self.batch_progress_bar:
            self.statusBar().removeWidget(self.batch_progress_bar)
            self.batch_progress_bar.hide()

    def _on_batch_progress_update(self, current: int, total: int, current_file: str):
        """
        Wird aufgerufen wenn der Batch-Thread einen Fortschritt meldet.

        Args:
            current: Aktuelle Dateinummer
            total: Gesamtanzahl der Dateien
            current_file: Name der aktuellen Datei
        """
        if self.batch_progress_bar:
            self.batch_progress_bar.setValue(current)

        self.statusBar().showMessage(f"Verarbeite: {current_file} ({current}/{total})")

    def _on_batch_processing_finished(self, stats: dict):
        """
        Wird aufgerufen wenn die Batch-Verarbeitung abgeschlossen ist.

        Args:
            stats: Statistik-Dictionary mit Verarbeitungsergebnissen
        """
        try:
            # Verstecke Progressbar
            self._hide_batch_progress_bar()

            # Speichere Projekt-Einstellungen
            if stats["processed"] > 0:
                self._save_project_settings()

            # Aktualisiere Tree
            self._load_nodes_to_tree()

            # Zeige Zusammenfassungsdialog
            self._show_drop_summary_dialog(stats)

            # Statusbar-Nachricht
            self.statusBar().showMessage(
                f"Batch-Verarbeitung abgeschlossen: {stats['processed']}/{stats['total']} Dateien", 5000
            )

        except Exception as e:
            logger.error(f"Fehler beim Abschließen der Batch-Verarbeitung: {e}")

    def _show_drop_summary_dialog(self, stats: dict):
        """
        Zeigt einen Zusammenfassungsdialog über die verarbeiteten XML-Dateien.

        Args:
            stats: Statistik-Dictionary mit Verarbeitungsergebnissen
        """
        # Erstelle Zusammenfassungstext
        summary_lines = []
        summary_lines.append("Verarbeitung abgeschlossen:\n")
        summary_lines.append(f"Gesamt: {stats['total']} Datei(en)")
        summary_lines.append(f"Verarbeitet: {stats['processed']} Datei(en)")

        if stats["new_added"] > 0:
            summary_lines.append(f"Neu hinzugefuegt: {stats['new_added']} Datei(en)")

        if stats["existing_added"] > 0:
            summary_lines.append(f"Vorhandene zugeordnet: {stats['existing_added']} Datei(en)")

        if stats["already_assigned"] > 0:
            summary_lines.append(f"Bereits zugeordnet: {stats['already_assigned']} Datei(en)")

        if stats["cancelled"] > 0:
            summary_lines.append(f"Abgebrochen: {stats['cancelled']} Datei(en)")

        if stats["renamed_files"]:
            summary_lines.append("\nUmbenannte Dateien:")
            for renamed in stats["renamed_files"]:
                summary_lines.append(f"   - {renamed}")

        if stats["errors"] > 0:
            summary_lines.append(f"\nFehler: {stats['errors']}")
            for error_msg in stats["error_messages"][:5]:  # Zeige max. 5 Fehler
                summary_lines.append(f"   - {error_msg}")
            if len(stats["error_messages"]) > 5:
                summary_lines.append(f"   ... und {len(stats['error_messages']) - 5} weitere Fehler")

        summary_text = "\n".join(summary_lines)

        # Wähle Icon basierend auf Erfolg
        if stats["errors"] > 0:
            QMessageBox.warning(self, "Verarbeitung mit Fehlern abgeschlossen", summary_text)
        elif stats["cancelled"] > 0:
            QMessageBox.information(self, "Verarbeitung abgebrochen", summary_text)
        else:
            QMessageBox.information(self, "Verarbeitung erfolgreich", summary_text)
