from __future__ import annotations

import html
import json
import re

from qgis.core import Qgis
from qgis.PyQt.QtCore import QUrl, pyqtSlot
from qgis.PyQt.QtGui import (
    QBrush,
    QDesktopServices,
    QGuiApplication,
    QStandardItem,
    QStandardItemModel,
)
from qgis.PyQt.QtWidgets import (
    QDockWidget,
    QMenu,
    QMessageBox,
    QTextBrowser,
    QTreeView,
    QVBoxLayout,
    QWidget,
)

from .classes.settings import Settings
from .compat import (
    ALIGN_CENTER,
    CLIPBOARD_MODE,
    COLOR_BLUE,
    CUSTOM_CONTEXT_MENU,
    FOREGROUND_ROLE,
    MSGBOX_BTN_NO,
    MSGBOX_BTN_YES,
    QABSTRACTITEMVIEW_NO_EDIT_TRIGGERS,
    USER_ROLE,
)


class MetaType:
    PROJECT = "project"
    LAYER = "layer"
    FOLDER = "folder"
    NONE = "none"


class QRAVEMetaWidget(QDockWidget):
    def __init__(self, parent=None):
        """Constructor."""
        super().__init__(parent)
        self.setupUi()

        self.treeView.setContextMenuPolicy(CUSTOM_CONTEXT_MENU)
        self.treeView.customContextMenuRequested.connect(self.open_menu)
        self.treeView.doubleClicked.connect(self.default_tree_action)

        self.settings = Settings()
        self.model = QStandardItemModel()
        self.treeView.setModel(self.model)
        self.meta = None
        self.description = None
        self.menu = QMenu()

        # Initialize our classes
        self.hide()

    @pyqtSlot(str, str, dict, str, bool)
    def load(
        self,
        label: str,
        meta_type: str,
        meta: dict,
        description: str,
        show: bool = False,
    ):
        # re-initialize our model
        self.model.clear()
        self.meta = meta
        self.description = description
        root_item = self.model.invisibleRootItem()
        self.model.setColumnCount(2)
        self.model.setHorizontalHeaderLabels(["Meta Name", "Meta Value"])

        if description is not None and len(description) > 0:
            self.descriptionBox.setHtml(self._description_to_html(description))
            self.descriptionBox.show()
        else:
            self.descriptionBox.setHtml("<i>No description available.</i>")
            self.descriptionBox.show()
        if meta_type == MetaType.PROJECT:
            self.treeView.setHeaderHidden(False)
            self.setWindowTitle(f"Project Metadata: {label}")
            self.treeView.setEnabled(True)
            if meta is not None and len(meta.keys()) > 0:
                if "project" in meta and len(meta["project"].keys()) > 0:
                    proj_meta = QStandardItem("Project Meta")
                    proj_meta_font = proj_meta.font()
                    proj_meta_font.setBold(True)
                    proj_meta.setFont(proj_meta_font)
                    for k, v in meta["project"].items():
                        self.appendMetaItem(proj_meta, k, v[0], v[1] if len(v) > 1 else None)
                    root_item.appendRow(proj_meta)
                if "warehouse" in meta and meta["warehouse"] is not None and len(meta["warehouse"].keys()) > 0:
                    wh_meta = QStandardItem("Warehouse Meta")
                    wh_meta_font = wh_meta.font()
                    wh_meta_font.setBold(True)
                    wh_meta.setFont(wh_meta_font)
                    for k, v in meta["warehouse"].items():
                        self.appendMetaItem(wh_meta, k, v[0], v[1] if len(v) > 1 else None)
                    root_item.appendRow(wh_meta)

        elif meta_type == MetaType.FOLDER:
            self.setWindowTitle(f"Folder: {label}")
            self.descriptionBox.hide()
            self.treeView.setHeaderHidden(True)
            self.treeView.setEnabled(False)
            self.model.setColumnCount(1)
            self.model.setHorizontalHeaderLabels(["Meta Name"])
            no_item = QStandardItem("Folders have no Metadata")
            no_item.setTextAlignment(ALIGN_CENTER)
            no_f = no_item.font()
            no_f.setItalic(True)
            no_item.setFont(no_f)
            root_item.appendRow(no_item)

        elif meta_type == MetaType.LAYER:
            self.setWindowTitle(f"Layer Metadata: {label}")
            self.treeView.setEnabled(True)
            self.treeView.setHeaderHidden(False)
            if meta is not None and len(meta.keys()) > 0:
                for k, v in meta.items():
                    self.appendMetaItem(root_item, k, v[0], v[1] if len(v) > 1 else None)
            else:
                self.treeView.setHeaderHidden(True)
                self.treeView.setEnabled(False)
                self.model.setColumnCount(1)
                self.model.setHorizontalHeaderLabels(["Meta Name"])
                no_item = QStandardItem("This layer has no Metadata")
                no_item.setTextAlignment(ALIGN_CENTER)
                no_f = no_item.font()
                no_f.setItalic(True)
                no_item.setFont(no_f)
                root_item.appendRow(no_item)
        elif meta_type == MetaType.NONE:
            self.descriptionBox.hide()
            self.treeView.setHeaderHidden(True)
            self.treeView.setEnabled(False)
            self.model.setColumnCount(1)
            self.setWindowTitle(f"Riverscapes Metadata: {label}")
            no_item = QStandardItem("This item cannot have metadata")
            no_item.setTextAlignment(ALIGN_CENTER)
            no_f = no_item.font()
            no_f.setItalic(True)
            no_item.setFont(no_f)
            root_item.appendRow(no_item)
            return

        # self.tree.header().setDefaultSectionSize(180)

        # self._populateTree(self.tree, )
        # Finally expand all levels
        self.treeView.expandAll()
        if show:
            self.show()

    def appendMetaItem(self, root_item: QStandardItem, key: str, value: str, meta_type=None):
        val_item = QStandardItem(value)
        if value is not None and len(value) > 0:
            val_item.setToolTip(value)
        val_item.setData(meta_type, USER_ROLE)

        if meta_type is not None:
            meta_type_lower = meta_type.lower()
            # Getting ready for custom meta types
            if (meta_type_lower in ["url", "link", "image", "video"]) and value is not None and value.startswith("http"):
                val_item.setData(QBrush(COLOR_BLUE), FOREGROUND_ROLE)
        # val_item.setUnderlineStyle(QTextCharFormat.SingleUnderline)

        root_item.appendRow([QStandardItem(key), val_item])

    def _description_to_html(self, text: str) -> str:
        """Convert a plain-text description to HTML, hyperlinking any http/https URLs."""
        _URL_RE = re.compile(r"(https?://[^\s<>\"']+)")
        escaped = html.escape(text)
        linked = _URL_RE.sub(r'<a href="\1">\1</a>', escaped)
        # Preserve newlines
        return linked.replace("\n", "<br/>")

    def closeEvent(self, event):
        self.hide()

    def default_tree_action(self, index):
        item = self.model.itemFromIndex(index)
        meta_type = item.data(USER_ROLE)
        text = item.text()

        if meta_type is not None and text is not None:
            meta_type_lower = meta_type.lower()
            if (meta_type_lower in ["url", "link", "image", "video"]) and text.startswith("http"):
                result = QMessageBox.question(
                    self,
                    "Riverscapes Viewer",
                    "Visit in browser?",
                    MSGBOX_BTN_YES | MSGBOX_BTN_NO,
                )
                if result == MSGBOX_BTN_YES:
                    QDesktopServices.openUrl(QUrl(text))
        else:
            self.copy(text)

    def open_menu(self, position):

        indexes = self.treeView.selectedIndexes()
        if len(indexes) < 1 or self.meta is None or len(self.meta.keys()) == 0:
            return

        # No multiselect so there is only ever one item
        item_name = self.model.itemFromIndex(indexes[0])
        item_val = self.model.itemFromIndex(indexes[1]) if len(indexes) > 0 else None

        self.menu.clear()
        if item_val is not None:
            row_text = {item_name.text(): item_val.text()}
            meta_type = item_val.data(USER_ROLE)
            meta_type_lower = meta_type.lower() if meta_type else ""
            if (meta_type_lower in ["url", "link", "image", "video"]) and item_val.text().startswith("http"):
                self.menu.addAction(
                    "Visit URL in Browser",
                    lambda: QDesktopServices.openUrl(QUrl(item_val.text())),
                )
                self.menu.addSeparator()
            self.menu.addAction("Copy name", lambda: self.copy(item_name.text()))
            self.menu.addAction("Copy value", lambda: self.copy(item_val.text()))
            self.menu.addAction(
                "Copy row (json)",
                lambda: self.copy(json.dumps(row_text, indent=4, sort_keys=True)),
            )
        self.menu.addAction(
            "Copy all rows (json)",
            lambda: self.copy(json.dumps(self.meta, indent=4, sort_keys=True)),
        )

        self.menu.exec(self.treeView.viewport().mapToGlobal(position))

    def copy(self, data: str):
        self.settings.msg_bar("Item Copied to clipboard:", data, Qgis.Success)
        cb = QGuiApplication.clipboard()
        cb.clear(mode=CLIPBOARD_MODE)
        cb.setText(data, mode=CLIPBOARD_MODE)

    def clear_and_hide(self):
        """Clear the metadata panel and hide it."""
        self.model.clear()
        self.setWindowTitle("Riverscapes Metadata")
        self.hide()

    def setupUi(self):

        self.resize(555, 559)
        self.dockWidgetContents = QWidget()
        self.verticalLayout = QVBoxLayout(self.dockWidgetContents)
        self.verticalLayout.setContentsMargins(0, 4, 0, 4)
        self.verticalLayout.setSpacing(0)

        # Add a read-only QTextBrowser for the description above the tree view;
        # QTextBrowser renders HTML links and opens them in the system browser.
        self.descriptionBox = QTextBrowser(self.dockWidgetContents)
        self.descriptionBox.setReadOnly(True)
        self.descriptionBox.setOpenExternalLinks(True)
        self.descriptionBox.setMinimumHeight(60)  # About 2 lines of text
        self.descriptionBox.setMaximumHeight(60)  # Prevents it from growing too tall
        self.verticalLayout.addWidget(self.descriptionBox)

        self.treeView = QTreeView(self.dockWidgetContents)
        self.treeView.setEditTriggers(QABSTRACTITEMVIEW_NO_EDIT_TRIGGERS)
        self.treeView.setProperty("showDropIndicator", False)
        self.treeView.setAlternatingRowColors(True)
        self.treeView.setIndentation(0)
        self.treeView.setSortingEnabled(False)
        self.treeView.setHeaderHidden(False)
        self.verticalLayout.addWidget(self.treeView)
        self.setWidget(self.dockWidgetContents)
