# -*- coding: utf-8 -*-
"""
/***************************************************************************
 NetworkStorePluginDialog
                                 A QGIS plugin
 export layers to kisters network store
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2022-01-18
        git sha              : $Format:%H$
        copyright            : (C) 2022 by Attila Bibok
        email                : Attila.bibok@kisters.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os
import json
from qgis.PyQt import uic
from qgis.PyQt import QtWidgets

from qgis.PyQt.QtCore import pyqtSignal, QVariant, QDateTime, QDate, QTime

from qgis.core import (
    NULL,
    # QgsVectorFileWriter,
    QgsVectorLayer,
    # QgsVectorLayerUtils,
    # QgsWkbTypes,
    # QgsProject,
    # QgsField,
    # QgsFieldConstraints,
    # QgsReadWriteContext,
    # QgsProject,
    # QgsVectorFileWriter,
    # QgsCoordinateTransform,
    # QgsPointXY,
    # QgsField,
    # QgsVectorLayer,
    # QgsCoordinateReferenceSystem,
    QgsFeature,
    # QgsPoint,
    # QgsLineString,
    # QgsGeometry,
    # QgsLayerTree,
    # QgsLayerTreeNode
)
# from qgis.PyQt.QtWebEngineWidgets import QWebEngineSettings
# from PyQt5.QtWebEngineWidgets import QWebEngineSettings
# from qgis.core import QgsProject, QgsLayerTreeNode, QgsWkbTypes


# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS_EXPORT, _ = uic.loadUiType(
    os.path.join(os.path.dirname(__file__), "network_store_dialog_dock_jsoneditor.ui")
)

# Add the missing top-level keys here
KNOWN_TOP_LEVEL = {
    "config_id",
    "source_uid",
    "target_uid",
    "uid",
    "element_class",
    "adapter",
    "config_revision",
    "domain",
    # these are never part of config_obj
    "location",
}


def _to_jsonable(v):
    if v is NULL:
        return None
    if v is None or isinstance(v, (str, int, float, bool)):
        return v
    if isinstance(v, QDateTime):
        return v.toPyDateTime().isoformat()
    if isinstance(v, QDate):
        return v.toPyDate().isoformat()
    if isinstance(v, QTime):
        return v.toPyTime().isoformat()
    if QVariant is not None and isinstance(v, QVariant):  # type: ignore
        try:
            if v.isNull():  # type: ignore[attr-defined]
                return None
        except Exception:
            pass
        for caster in (int, float, bool, str):
            try:
                return caster(v)
            except Exception:
                continue
        return str(v)
    if isinstance(v, dict):
        return {str(k): _to_jsonable(x) for k, x in v.items()}
    if isinstance(v, (list, tuple)):
        return [_to_jsonable(x) for x in v]
    # numpy scalars optional…
    try:
        import numpy as np  # noqa

        if isinstance(v, np.generic):
            return v.item()
    except Exception:
        pass
    return str(v)


def _maybe_parse_json_string(s: str):
    """If `s` looks like a JSON object/array, parse it safely; else return as-is."""
    if not isinstance(s, str):
        return s
    t = s.strip()
    if not t:
        return s
    if (t[0] == "{" and t[-1] == "}") or (t[0] == "[" and t[-1] == "]"):
        try:
            return json.loads(t)
        except Exception:
            return s
    return s


def feature_to_json(feat: QgsFeature) -> dict:
    fields = feat.fields()
    row = {fields[i].name(): feat[i] for i in range(len(fields))}
    row = {
        k: _to_jsonable(v) for k, v in row.items() if v is not None and v is not NULL
    }

    app_id = {}
    if "adapter" in row and row["adapter"]:
        app_id["adapter"] = row["adapter"]

    out: dict = {}
    if app_id:
        out["app_id"] = app_id

    # top-level we want visible above config_obj
    TOP = (
        "config_id",
        "source_uid",
        "target_uid",
        "rank",
        "uid",
        "element_class",
        "config_revision",
        "domain",
    )
    for k in TOP:
        if k in row:
            out[k] = row[k]

    # everything else under config_obj
    cfg = {k: v for k, v in row.items() if k not in KNOWN_TOP_LEVEL}
    # auto-parse JSON-looking strings inside config_obj
    cfg = {k: _maybe_parse_json_string(v) for k, v in cfg.items()}

    out["config_obj"] = cfg
    return out


def derive_app_name(payload: dict, vl: QgsVectorLayer) -> str:
    # Prefer explicit hints if you have them:
    # e.g., you could stash vl.setCustomProperty("network:app_name", "JunctionElement")
    app = vl.customProperty("network:app_name", None)
    if app:
        return str(app)

    # Otherwise infer from attributes:
    adapter = (payload.get("app_id") or {}).get("adapter")
    element_class = payload.get("element_class")

    # Most of your schema keys are <Something>Element
    name = adapter or element_class or "Junction"
    if not name.endswith("Element"):
        name = f"{name}Element"
    return name


class NetworkStoreDockableJsonEditor(QtWidgets.QDockWidget, FORM_CLASS_EXPORT):
    # set up QWebEngineView, QWebChannel, register backend, etc.
    # expose tiny helpers the controller can call:
    #   self.load_single(data, schema, meta)
    #   self.load_all(data, versions_map, meta)
    #   self.show_status(msg), self.show_warning(msg), self.show_error(msg)
    saveRequested = pyqtSignal(dict)  # comes from QWebChannel bridge

    def __init__(self, parent=None):
        """Constructor."""
        super(NetworkStoreDockableJsonEditor, self).__init__(parent)
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect

        self.setupUi(self)

    def call_js(self, fn_name: str, *args):
        import json

        js = f"{fn_name}(" + ",".join(json.dumps(a) for a in args) + ");"
        self.web_engine_view.page().runJavaScript(js)

    # Public API the controller calls:
    def load_single(self, data, schema, meta):
        self.call_js("editorLoad", data, schema, meta)

    def load_all(self, data, versions_by_ver, meta):
        self.call_js("editorLoadAll", data, versions_by_ver, meta)

    def load_auto(self, data, versions_by_ver, meta):
        self.call_js("editorLoadAuto", data, versions_by_ver, meta)

    def show_status(self, msg):
        self.call_js("editorSetStatus", msg)

    def show_warning(self, msg):
        self.call_js("editorShowWarning", msg)

    def show_error(self, msg):
        self.call_js("editorShowError", msg)
