# -*- coding: utf-8 -*-
"""
Dialog to load Datasphere locations as a point layer.
"""

import json
import os
from pathlib import Path
from typing import Optional, Tuple

from qgis.PyQt import uic, QtWidgets
from qgis.PyQt.QtWidgets import QMessageBox, QDialogButtonBox
from qgis.core import (
    QgsApplication,
    QgsAuthMethodConfig,
    QgsCoordinateReferenceSystem,
    QgsFeature,
    QgsGeometry,
    QgsPointXY,
    QgsProject,
    QgsVectorLayer,
    QgsField,
    QgsFields,
    QgsMarkerSymbol,
    QgsSvgMarkerSymbolLayer,
    QgsCategorizedSymbolRenderer,
    QgsRendererCategory,
)
from qgis.PyQt.QtCore import QVariant

from .datasphere_core import list_locations, make_session
from .datasphere_settings import DatasphereSettingsStore

FORM_CLASS, _ = uic.loadUiType(
    os.path.join(os.path.dirname(__file__), "datasphere_dialog_load_locations.ui")
)


class DatasphereDialogLoadLocations(QtWidgets.QDialog, FORM_CLASS):
    """
    Simple UI to fetch Datasphere locations and load them into the project.
    """

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

        self.comboConfig.currentIndexChanged.connect(self._apply_selected_config)

        try:
            self.buttonBox.accepted.disconnect()
        except TypeError:
            pass
        self.buttonBox.accepted.connect(self._on_load_clicked)
        self.buttonBox.rejected.connect(self.reject)
        ok_btn = self.buttonBox.button(QDialogButtonBox.Ok)
        if ok_btn is not None:
            ok_btn.setText(self.tr("Load"))

        self.lineBaseUrl.setText("https://na.datasphere.online/external")
        self.lblStatus.clear()
        self._reload_configs()

    def _reload_configs(self) -> None:
        store = DatasphereSettingsStore()
        configs = store.list()
        self.comboConfig.blockSignals(True)
        self.comboConfig.clear()
        for cfg in configs:
            label = f"{cfg.name} ({cfg.base_url})"
            self.comboConfig.addItem(label, cfg)
        self.comboConfig.blockSignals(False)
        if self.comboConfig.count() > 0:
            self.comboConfig.setCurrentIndex(0)
            self._apply_selected_config()

    def _resolve_credentials_from_authcfg(
        self, authcfg: Optional[str]
    ) -> Tuple[Optional[str], Optional[str]]:
        if not authcfg:
            return None, None
        try:
            cfg = QgsAuthMethodConfig()
            if QgsApplication.authManager().loadAuthenticationConfig(
                authcfg, cfg, True
            ):
                return cfg.config("username") or None, cfg.config("password") or None
        except Exception:
            pass
        return None, None

    def _apply_selected_config(self) -> None:
        idx = self.comboConfig.currentIndex()
        cfg = self.comboConfig.itemData(idx)
        if not cfg:
            return
        self.lineBaseUrl.setText(cfg.base_url)
        try:
            self.spinDsId.setValue(int(cfg.ds_id or 0))
        except Exception:
            pass
        user_from_auth, pass_from_auth = self._resolve_credentials_from_authcfg(
            getattr(cfg, "authcfg", None)
        )
        self.lineUser.setText(cfg.username or user_from_auth or "")
        if pass_from_auth:
            self.linePass.setText(pass_from_auth)

    def _show_error(self, title: str, msg: str) -> None:
        QMessageBox.critical(self, title, msg)

    def _set_status(self, text: str) -> None:
        self.lblStatus.setText(text)

    def _on_load_clicked(self) -> None:
        base_url = self.lineBaseUrl.text().strip()
        username = self.lineUser.text().strip() or None
        password = self.linePass.text() or None
        layer_name = self.lineLayerName.text().strip() or self.tr(
            "Datasphere locations"
        )

        cfg = self.comboConfig.itemData(self.comboConfig.currentIndex())
        if (not username or not password) and cfg:
            from_auth_user, from_auth_pass = self._resolve_credentials_from_authcfg(
                getattr(cfg, "authcfg", None)
            )
            username = username or from_auth_user
            password = password or from_auth_pass

        self._set_status(self.tr("Loading locations..."))
        try:
            session = make_session(username, password)
            items = list_locations(base_url, session=session)
            layer = self._create_layer(layer_name, items)
            QgsProject.instance().addMapLayer(layer)
            self._set_status(self.tr(f"Loaded {len(items)} locations."))
            self.accept()
        except Exception as e:  # noqa: BLE001
            self._show_error(self.tr("Failed to load locations"), str(e))
            self._set_status(self.tr("Load failed."))

    def _create_layer(self, layer_name: str, items: list[dict]) -> QgsVectorLayer:
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326",
            layer_name,
            "memory",
        )
        pr = layer.dataProvider()
        fields = QgsFields()
        fields.append(QgsField("id", QVariant.String))
        fields.append(QgsField("name", QVariant.String))
        fields.append(QgsField("name2", QVariant.String))
        fields.append(QgsField("key", QVariant.String))
        fields.append(QgsField("org", QVariant.String))
        fields.append(QgsField("creator", QVariant.String))
        fields.append(QgsField("editor", QVariant.String))
        fields.append(QgsField("creationTime", QVariant.String))
        fields.append(QgsField("editTime", QVariant.String))
        fields.append(QgsField("geometryType", QVariant.String))
        fields.append(QgsField("icon", QVariant.String))
        fields.append(QgsField("raw_json", QVariant.String))
        pr.addAttributes(fields)
        layer.updateFields()

        unique_icons = set()
        feats = []
        for item in items:
            lat = item.get("latitude")
            lon = item.get("longitude")
            if lat is None or lon is None:
                continue

            # Resolve icon path from explicit 'icon' or metadata 'iconReference'
            def _resolve_icon_path(it: dict) -> str:
                meta = it.get("metadata") or {}
                icon_ref = it.get("icon") or meta.get("iconReference") or ""
                icon_ref = str(icon_ref).strip()
                if not icon_ref:
                    return ""
                # Example: "ki ki-bar-chart" -> "ki-bar-chart.svg"
                parts = icon_ref.split()
                icon_name = parts[-1] if parts else icon_ref
                if not icon_name.startswith("ki-") and icon_name.startswith("ki"):
                    # normalize "kiXYZ" -> "ki-XYZ"
                    icon_name = "ki-" + icon_name[2:]
                if not icon_name.endswith(".svg"):
                    icon_name = f"{icon_name}.svg"
                candidate = (
                    Path(__file__).parent / "icons" / "ki-water-icons" / icon_name
                )
                return str(candidate) if candidate.exists() else ""

            icon_path = _resolve_icon_path(item)
            if icon_path:
                unique_icons.add(icon_path)

            f = QgsFeature()
            f.setFields(fields)
            f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(float(lon), float(lat))))
            f.setAttribute("id", str(item.get("id") or ""))
            f.setAttribute("name", str(item.get("name") or ""))
            f.setAttribute("name2", str(item.get("name2") or ""))
            f.setAttribute("key", str(item.get("key") or ""))
            f.setAttribute("org", str(item.get("organization") or ""))
            f.setAttribute("creator", str(item.get("creator") or ""))
            f.setAttribute("editor", str(item.get("editor") or ""))
            f.setAttribute("creationTime", str(item.get("creationTime") or ""))
            f.setAttribute("editTime", str(item.get("editTime") or ""))
            f.setAttribute("geometryType", str(item.get("geometryType") or ""))
            f.setAttribute("icon", icon_path)
            try:
                f.setAttribute("raw_json", json.dumps(item, ensure_ascii=False))
            except Exception:
                f.setAttribute("raw_json", "")
            feats.append(f)

        pr.addFeatures(feats)
        layer.updateExtents()
        layer.setCrs(QgsCoordinateReferenceSystem("EPSG:4326"))

        # Style: categorized renderer per icon path, with fallback to default marker.
        default_icon = (
            Path(__file__).parent / "icons" / "ki-water-icons" / "ki-map-marker.svg"
        )

        def _make_symbol(svg_path: Path) -> QgsMarkerSymbol:
            svg_layer = QgsSvgMarkerSymbolLayer(str(svg_path))
            svg_layer.setSize(2)
            sym = QgsMarkerSymbol()
            sym.changeSymbolLayer(0, svg_layer)
            return sym

        categories = []
        # default / empty icon category
        categories.append(
            QgsRendererCategory("", _make_symbol(default_icon), self.tr("Default icon"))
        )
        for icon in sorted(unique_icons):
            categories.append(
                QgsRendererCategory(icon, _make_symbol(Path(icon)), Path(icon).name)
            )

        layer.setRenderer(QgsCategorizedSymbolRenderer("icon", categories))

        return layer
