from qgis.PyQt import QtWidgets, QtCore
import os
import json
import tempfile, requests
from .api import APIGetOrderDocuments, APIGetDownloadByPath, APIGetOrderTasks, APIGetOrderBaseDocuments, APIGetBaseDocumentDownloadUrl, APIGetBaseDocumentStyles
from qgis.core import (
    QgsVectorLayer, QgsRasterLayer, QgsProject, QgsFeature, QgsGeometry,
    QgsPointXY, QgsField, QgsMarkerSymbol,
    QgsSimpleMarkerSymbolLayer, QgsFontMarkerSymbolLayer,
    QgsCategorizedSymbolRenderer, QgsRendererCategory,
    QgsTextFormat, QgsTextBufferSettings,
    QgsPalLayerSettings, QgsVectorLayerSimpleLabeling,
)
from qgis.gui import QgsMapToolIdentifyFeature
from PyQt5.QtCore import QVariant
from qgis.PyQt.QtGui import QColor, QFont
from qgis.utils import iface

# Referenz auf aktives Klick-Tool (verhindert Garbage Collection)
_annotation_tool = None

TYPE_LABELS = {
    "error": "Fehler",
    "question": "Frage",
    "reminder": "Erinnerung",
    "request": "Korrektur",
    "additional": "Änderung",
}

TYPE_COLORS = {
    "error": "#e11d48",
    "question": "#2563eb",
    "reminder": "#f97316",
    "request": "#16a34a",
    "additional": "#111827",
}

TYPE_ICONS = {
    "error": "!",
    "question": "?",
    "reminder": "i",
    "request": "\u2713",
    "additional": "+",
}

STATUS_LABELS = {
    "open": "Offen",
    "done": "Abgenommen",
}


def _open_review_dialog(task, user_status, cognito_session, order_id):
    """Öffnet den ReviewDetailsDialog für eine Anmerkung."""
    from ..dialog.review_dialogs import ReviewDetailsDialog
    dlg = ReviewDetailsDialog(
        review=task,
        user_status=user_status,
        cognito_session=cognito_session,
        order_id=order_id,
    )
    dlg.exec_()


class DocumentList(QtWidgets.QWidget):
    def __init__(self, cognito_session, order_id, review_id, parent=None, plan_id=None, user_status=""):
        super().__init__(parent)
        self.cognito_session = cognito_session
        self.review_id = review_id
        self.order_id = order_id
        self.plan_id = plan_id
        self.user_status = user_status

        self.layout = QtWidgets.QVBoxLayout(self)
        self.setLayout(self.layout)

        self.load_documents()

    def load_documents(self):
        """Lädt Dokumente über die API und zeigt sie an"""
        try:
            documents = APIGetOrderDocuments(self.cognito_session, self.order_id, self.review_id)
        except Exception as e:
            documents = []

        # Vorherige Einträge entfernen
        while self.layout.count():
            item = self.layout.takeAt(0)
            if item.widget():
                item.widget().deleteLater()

        # .geojson-Dateien ausfiltern
        documents = [doc for doc in documents if not doc.get("fileName", "").lower().endswith(".geojson")]

        if not documents:
            self.layout.addWidget(QtWidgets.QLabel("Keine Dokumente vorhanden."))
            return

        for doc in documents:
            self.layout.addWidget(self._build_doc_row(doc))

    def _build_doc_row(self, doc: dict):
        """Erzeugt eine Zeile mit Dokumentnamen + Buttons"""
        row = QtWidgets.QWidget()
        h = QtWidgets.QHBoxLayout(row)
        h.setContentsMargins(0, 0, 0, 0)

        # Name
        name_label = QtWidgets.QLabel(doc.get("fileName", "-"))
        h.addWidget(name_label, stretch=1)

        # Buttons
        path = doc.get("key", "")
        fileName = doc.get("fileName", "")
        if path.lower().endswith(".gml"):
            btn1 = QtWidgets.QPushButton("Plan in QGIS prüfen")
            btn1.clicked.connect(lambda _, fileName=fileName: self.import_gml(fileName))
            h.addWidget(btn1)
        if path.lower().endswith((".tif", ".tiff")) or fileName.lower().endswith((".tif", ".tiff")):
            btn_tif = QtWidgets.QPushButton("In QGIS anzeigen")
            btn_tif.clicked.connect(lambda _, fileName=fileName: self.import_tif(fileName))
            h.addWidget(btn_tif)
        btn = QtWidgets.QPushButton("Herunterladen")
        btn.clicked.connect(lambda _, fileName=fileName: self.download_file(fileName))
        h.addWidget(btn)

        return row

    def import_gml(self, fileName: str):
        try:
            s3_url = APIGetDownloadByPath(self.cognito_session, fileName, self.order_id, self.review_id)
            if not s3_url:
                raise Exception("Kein S3-Downloadlink in API-Antwort gefunden.")

            gml_resp = requests.get(s3_url, timeout=60)
            gml_resp.raise_for_status()

            tmp = tempfile.NamedTemporaryFile(
                suffix=".gml",
                prefix=f"{self.review_id}_",
                delete=False
            )
            tmp.write(gml_resp.content)
            tmp.close()

            # Styles direkt aus dem GML-Dokument generieren lassen
            try:
                from .api import APIGetOrderDocumentStyles
                from .styles import apply_style_to_gml_layers

                style_data = APIGetOrderDocumentStyles(
                    self.cognito_session, self.order_id, self.review_id, fileName
                )
                server_styles = style_data.get("styles") if style_data else None
                details = style_data.get("details", []) if style_data else []
                refs = style_data.get("refs", []) if style_data else []

                layer_name = 'Abzunehmender Plan'
                loaded_layers = apply_style_to_gml_layers(tmp.name, layer_name, server_styles)

                if loaded_layers:
                    for layer in loaded_layers:
                        if layer and layer.isValid():
                            layer.setCustomProperty("xplanung24_plan_id", self.plan_id or "")
                            if details:
                                layer.setCustomProperty("xplanung24_details", json.dumps(details))
                            if refs:
                                layer.setCustomProperty("xplanung24_refs", json.dumps(refs))

                self._import_review_points()
                return
            except Exception as style_err:
                pass  # Fallback wird unten ausgeführt

            # Fallback: einfacher Import ohne Styles/Details
            layer_name = 'Abzunehmender Plan'
            vlayer = QgsVectorLayer(tmp.name, layer_name, "ogr")
            if not vlayer.isValid():
                vlayer = QgsVectorLayer(tmp.name, layer_name, "GMLAS")
            if not vlayer.isValid():
                raise Exception("Layer konnte nicht geladen werden.")

            iface.addVectorLayer(vlayer.source(), vlayer.name(), vlayer.providerType())

            self._import_review_points()
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "Fehler", f"{e}")

    def import_tif(self, fileName: str):
        """Importiert eine TIF/GeoTIFF-Datei als Raster-Layer und legt sie hinter den Plan"""
        try:
            s3_url = APIGetDownloadByPath(self.cognito_session, fileName, self.order_id, self.review_id)
            if not s3_url:
                raise Exception("Kein S3-Downloadlink in API-Antwort gefunden.")

            tif_resp = requests.get(s3_url, timeout=120)
            tif_resp.raise_for_status()

            # Temporäre Datei erstellen - gleiche Logik wie in layer_details_dialog.py
            suffix = ".tif"
            if fileName.lower().endswith(".tiff"):
                suffix = ".tiff"

            tmp_file = tempfile.NamedTemporaryFile(
                suffix=suffix,
                prefix="xplan_order_raster_",
                delete=False
            )
            tmp_file.write(tif_resp.content)
            tmp_file.flush()  # WICHTIG: Daten auf Festplatte schreiben
            tmp_file.close()

            # Raster-Layer laden
            layer_name = os.path.splitext(os.path.basename(fileName))[0]
            rlayer = QgsRasterLayer(tmp_file.name, layer_name)

            if not rlayer.isValid():
                raise Exception("Raster-Layer konnte nicht geladen werden.")

            # Layer zum Projekt hinzufügen (gleiche Logik wie layer_details_dialog.py)
            project = QgsProject.instance()
            root = project.layerTreeRoot()

            project.addMapLayer(rlayer, False)
            num_children = len(root.children())
            root.insertLayer(num_children, rlayer)

            # Canvas aktualisieren
            iface.mapCanvas().refresh()

            iface.messageBar().pushMessage(
                "XPlanung24",
                f"GeoTIFF '{layer_name}' wurde in QGIS geladen.",
                level=0,  # Info
                duration=4
            )

        except Exception as e:
            iface.messageBar().pushMessage(
                "XPlanung24",
                f"Fehler beim Laden: {str(e)}",
                level=2,  # Critical
                duration=5
            )

    def _import_review_points(self):
        """Lädt Review-Anmerkungen mit Koordinaten als Punkt-Layer in QGIS."""
        try:
            tasks = APIGetOrderTasks(self.cognito_session, order_id=self.order_id)
        except Exception:
            return

        # Nur Tasks mit Location-Daten
        geo_tasks = []
        for task in tasks:
            loc = task.get("location")
            if not loc:
                continue
            if isinstance(loc, str):
                try:
                    loc = json.loads(loc)
                except Exception:
                    continue
            geom = loc.get("geometry")
            if geom and geom.get("type") == "Point" and geom.get("coordinates"):
                geo_tasks.append((task, geom["coordinates"]))

        if not geo_tasks:
            return

        # Memory-Layer erstellen (EPSG:4326 = WGS84 lon/lat)
        mem_layer = QgsVectorLayer("Point?crs=EPSG:4326", "Abnahme-Anmerkungen", "memory")
        dp = mem_layer.dataProvider()

        dp.addAttributes([
            QgsField("task_id", QVariant.String),
            QgsField("name", QVariant.String),
            QgsField("typ", QVariant.String),
            QgsField("status", QVariant.String),
            QgsField("beschreibung", QVariant.String),
        ])
        mem_layer.updateFields()

        tasks_dict = {}
        features = []
        for task, coords in geo_tasks:
            feat = QgsFeature(mem_layer.fields())
            feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(coords[0], coords[1])))
            feat.setAttribute("task_id", task.get("id", ""))
            feat.setAttribute("name", task.get("name", ""))
            feat.setAttribute("typ", TYPE_LABELS.get(task.get("type", ""), task.get("type", "")))
            feat.setAttribute("status", STATUS_LABELS.get(task.get("status", ""), task.get("status", "")))
            feat.setAttribute("beschreibung", (task.get("message") or "")[:200])
            features.append(feat)
            tasks_dict[task.get("id", "")] = task

        dp.addFeatures(features)
        mem_layer.updateExtents()

        # Kategorisiertes Styling nach Typ mit Icons
        categories = []
        for type_key, color in TYPE_COLORS.items():
            label = TYPE_LABELS.get(type_key, type_key)
            char = TYPE_ICONS.get(type_key, "?")

            symbol = QgsMarkerSymbol()
            symbol.deleteSymbolLayer(0)

            # Farbiger Kreis als Hintergrund
            bg = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Circle, 6.0)
            bg.setColor(QColor(color))
            bg.setStrokeColor(QColor("#ffffff"))
            bg.setStrokeWidth(0.4)
            symbol.appendSymbolLayer(bg)

            # Typ-Zeichen auf dem Kreis
            char_layer = QgsFontMarkerSymbolLayer()
            char_layer.setFontFamily("Sans Serif")
            char_layer.setCharacter(char)
            char_layer.setSize(3.5)
            char_layer.setColor(QColor("#ffffff"))
            symbol.appendSymbolLayer(char_layer)

            categories.append(QgsRendererCategory(label, symbol, label))

        renderer = QgsCategorizedSymbolRenderer("typ", categories)
        mem_layer.setRenderer(renderer)

        # Text-Labels mit Name der Anmerkung
        pal = QgsPalLayerSettings()
        pal.fieldName = "name"
        pal.enabled = True

        text_format = QgsTextFormat()
        text_format.setFont(QFont("Sans Serif", 8))
        text_format.setSize(8)
        text_format.setColor(QColor("#333333"))

        buf = QgsTextBufferSettings()
        buf.setEnabled(True)
        buf.setSize(1.2)
        buf.setColor(QColor("#ffffff"))
        text_format.setBuffer(buf)

        pal.setFormat(text_format)

        mem_layer.setLabeling(QgsVectorLayerSimpleLabeling(pal))
        mem_layer.setLabelsEnabled(True)

        QgsProject.instance().addMapLayer(mem_layer)

        # Klick-Tool für Anmerkungen aktivieren
        self._setup_annotation_click_tool(mem_layer, tasks_dict)

    def _setup_annotation_click_tool(self, layer, tasks_dict):
        """Richtet ein Klick-Tool ein, das bei Klick auf eine Anmerkung den Detail-Dialog öffnet."""
        global _annotation_tool

        canvas = iface.mapCanvas()
        cognito_session = self.cognito_session
        order_id = self.order_id
        user_status = self.user_status

        _annotation_tool = QgsMapToolIdentifyFeature(canvas, layer)

        def on_feature_clicked(feature):
            task_id = feature['task_id']
            task = tasks_dict.get(task_id)
            if not task:
                return
            _open_review_dialog(task, user_status, cognito_session, order_id)

        _annotation_tool.featureIdentified.connect(on_feature_clicked)
        canvas.setMapTool(_annotation_tool)
        iface.setActiveLayer(layer)

        iface.messageBar().pushMessage(
            "XPlanung24",
            "Klicken Sie auf eine Anmerkung, um Details anzuzeigen.",
            level=0,
            duration=8
        )

    def download_file(self, path: str):
        try:
            # S3-Download-Link von der API holen
            s3_url = APIGetDownloadByPath(self.cognito_session, path, self.order_id, self.review_id)
            if not s3_url:
                raise Exception("Kein S3-Downloadlink in API-Antwort gefunden.")

            # Dialog „Speichern unter“
            options = QtWidgets.QFileDialog.Options()
            options |= QtWidgets.QFileDialog.DontUseNativeDialog
            save_path, _ = QtWidgets.QFileDialog.getSaveFileName(
                self, 
                "Datei speichern unter", 
                path.split('/')[-1],  # Standardname aus dem Pfad
                "Alle Dateien (*)",
                options=options
            )

            if not save_path:
                return  # User hat abgebrochen

            # Datei herunterladen
            resp = requests.get(s3_url, timeout=60)
            resp.raise_for_status()

            with open(save_path, 'wb') as f:
                f.write(resp.content)

            QtWidgets.QMessageBox.information(self, "Erfolg", f"Datei gespeichert:\n{save_path}")

        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "Fehler", f"{e}")


class BaseDocumentList(QtWidgets.QWidget):
    """Widget zur Anzeige der Basisdokumente eines Auftrags (direkt am Auftrag, nicht an Review)"""

    def __init__(self, cognito_session, order_id, parent=None):
        super().__init__(parent)
        self.cognito_session = cognito_session
        self.order_id = order_id

        self.layout = QtWidgets.QVBoxLayout(self)
        self.setLayout(self.layout)

        self.load_documents()

    def load_documents(self):
        """Lädt Basisdokumente über die API und zeigt sie an"""
        try:
            documents = APIGetOrderBaseDocuments(self.cognito_session, self.order_id)
        except Exception as e:
            documents = []

        # Vorherige Einträge entfernen
        while self.layout.count():
            item = self.layout.takeAt(0)
            if item.widget():
                item.widget().deleteLater()

        if not documents:
            self.layout.addWidget(QtWidgets.QLabel("Keine Auftragsdokumente vorhanden."))
            return

        for doc in documents:
            self.layout.addWidget(self._build_doc_row(doc))

    def _build_doc_row(self, doc: dict) -> QtWidgets.QWidget:
        """Baut eine Zeile für ein Dokument"""
        row = QtWidgets.QWidget()
        h = QtWidgets.QHBoxLayout(row)
        h.setContentsMargins(0, 4, 0, 4)

        fileName = doc.get("fileName", "")
        size = doc.get("size", 0)

        # Dateiname und Größe
        name_label = QtWidgets.QLabel(f"📄 {fileName}")
        name_label.setStyleSheet("font-size: 13px;")
        h.addWidget(name_label)

        size_label = QtWidgets.QLabel(f"({self._format_size(size)})")
        size_label.setStyleSheet("font-size: 11px; color: #666;")
        h.addWidget(size_label)

        h.addStretch()

        # GML-Button
        if fileName.lower().endswith(".gml"):
            btn_gml = QtWidgets.QPushButton("In QGIS anzeigen")
            btn_gml.clicked.connect(lambda _, fn=fileName: self.import_gml(fn))
            h.addWidget(btn_gml)

        # TIF-Button
        if fileName.lower().endswith((".tif", ".tiff")):
            btn_tif = QtWidgets.QPushButton("In QGIS anzeigen")
            btn_tif.clicked.connect(lambda _, fn=fileName: self.import_tif(fn))
            h.addWidget(btn_tif)

        # Download-Button
        btn = QtWidgets.QPushButton("Herunterladen")
        btn.clicked.connect(lambda _, fn=fileName: self.download_file(fn))
        h.addWidget(btn)

        return row

    def _format_size(self, size: int) -> str:
        """Formatiert die Dateigröße"""
        if size < 1024:
            return f"{size} B"
        elif size < 1024 * 1024:
            return f"{size / 1024:.1f} KB"
        else:
            return f"{size / (1024 * 1024):.1f} MB"

    def import_gml(self, fileName: str):
        """Importiert eine GML-Datei als Vector-Layer in QGIS mit Styles"""
        try:
            s3_url = APIGetBaseDocumentDownloadUrl(self.cognito_session, fileName, self.order_id)
            if not s3_url:
                raise Exception("Kein S3-Downloadlink in API-Antwort gefunden.")

            gml_resp = requests.get(s3_url, timeout=60)
            gml_resp.raise_for_status()

            tmp = tempfile.NamedTemporaryFile(
                suffix=".gml",
                prefix="xplan_order_base_",
                delete=False
            )
            tmp.write(gml_resp.content)
            tmp.close()

            # Styles aus API laden und anwenden
            try:
                from .styles import apply_style_to_gml_layers

                style_data = APIGetBaseDocumentStyles(
                    self.cognito_session, self.order_id, fileName
                )
                server_styles = style_data.get("styles") if style_data else None
                details = style_data.get("details", []) if style_data else []
                refs = style_data.get("refs", []) if style_data else []

                layer_name = 'Auftragsdokument'
                loaded_layers = apply_style_to_gml_layers(tmp.name, layer_name, server_styles)

                if loaded_layers:
                    for layer in loaded_layers:
                        if layer and layer.isValid():
                            if details:
                                layer.setCustomProperty("xplanung24_details", json.dumps(details))
                            if refs:
                                layer.setCustomProperty("xplanung24_refs", json.dumps(refs))

                iface.messageBar().pushMessage(
                    "XPlanung24",
                    f"GML '{fileName}' wurde mit Styles in QGIS geladen.",
                    level=0,
                    duration=4
                )
                return
            except Exception as style_err:
                pass  # Fallback wird unten ausgeführt

            # Fallback: einfacher Import ohne Styles
            layer_name = os.path.splitext(os.path.basename(fileName))[0]
            vlayer = QgsVectorLayer(tmp.name, layer_name, "ogr")
            if not vlayer.isValid():
                vlayer = QgsVectorLayer(tmp.name, layer_name, "GMLAS")
            if not vlayer.isValid():
                raise Exception("Layer konnte nicht geladen werden.")

            iface.addVectorLayer(vlayer.source(), vlayer.name(), vlayer.providerType())

            iface.messageBar().pushMessage(
                "XPlanung24",
                f"GML '{layer_name}' wurde in QGIS geladen.",
                level=0,
                duration=4
            )

        except Exception as e:
            iface.messageBar().pushMessage(
                "XPlanung24",
                f"Fehler beim Laden: {str(e)}",
                level=2,
                duration=5
            )

    def import_tif(self, fileName: str):
        """Importiert eine TIF/GeoTIFF-Datei als Raster-Layer in QGIS"""
        try:
            s3_url = APIGetBaseDocumentDownloadUrl(self.cognito_session, fileName, self.order_id)
            if not s3_url:
                raise Exception("Kein S3-Downloadlink in API-Antwort gefunden.")

            tif_resp = requests.get(s3_url, timeout=120)
            tif_resp.raise_for_status()

            suffix = ".tif"
            if fileName.lower().endswith(".tiff"):
                suffix = ".tiff"

            tmp_file = tempfile.NamedTemporaryFile(
                suffix=suffix,
                prefix="xplan_order_base_raster_",
                delete=False
            )
            tmp_file.write(tif_resp.content)
            tmp_file.flush()
            tmp_file.close()

            layer_name = os.path.splitext(os.path.basename(fileName))[0]
            rlayer = QgsRasterLayer(tmp_file.name, layer_name)

            if not rlayer.isValid():
                raise Exception("Raster-Layer konnte nicht geladen werden.")

            project = QgsProject.instance()
            root = project.layerTreeRoot()

            project.addMapLayer(rlayer, False)
            num_children = len(root.children())
            root.insertLayer(num_children, rlayer)

            iface.mapCanvas().refresh()

            iface.messageBar().pushMessage(
                "XPlanung24",
                f"GeoTIFF '{layer_name}' wurde in QGIS geladen.",
                level=0,
                duration=4
            )

        except Exception as e:
            iface.messageBar().pushMessage(
                "XPlanung24",
                f"Fehler beim Laden: {str(e)}",
                level=2,
                duration=5
            )

    def download_file(self, fileName: str):
        """Lädt die Datei herunter"""
        try:
            s3_url = APIGetBaseDocumentDownloadUrl(self.cognito_session, fileName, self.order_id)
            if not s3_url:
                raise Exception("Kein S3-Downloadlink in API-Antwort gefunden.")

            options = QtWidgets.QFileDialog.Options()
            options |= QtWidgets.QFileDialog.DontUseNativeDialog
            save_path, _ = QtWidgets.QFileDialog.getSaveFileName(
                self,
                "Datei speichern unter",
                fileName,
                "Alle Dateien (*)",
                options=options
            )

            if not save_path:
                return

            resp = requests.get(s3_url, timeout=60)
            resp.raise_for_status()

            with open(save_path, 'wb') as f:
                f.write(resp.content)

            QtWidgets.QMessageBox.information(self, "Erfolg", f"Datei gespeichert:\n{save_path}")

        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "Fehler", f"Fehler beim Herunterladen: {e}")