# SPDX-FileCopyrightText: 2024 Luca Weiss
# SPDX-License-Identifier: GPL-3.0-only

from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Pango
from .. import esim
from .. import helpers


class DataObject(GObject.GObject):
    def __init__(self, data: str, children=None):
        super().__init__()
        self.data = data
        self.children = children


@Gtk.Template(resource_path="/eu/lucaweiss/lpa-gtk/pages/chipinfo.ui")
class ChipInfoPage(Gtk.Box):
    __gtype_name__ = "LpaGtkChipInfo"

    list_view = Gtk.Template.Child()
    overlay = Gtk.Template.Child()

    _active_slot: int
    _manager: esim.ESimManager

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.clipboard = Gdk.Display().get_default().get_clipboard()

    def set_manager(self, manager: esim.ESimManager) -> None:
        self._manager = manager

    def set_active_slot(self, slot: int) -> None:
        self._active_slot = slot

    def create_model_children(self, item):
        if not item.children:
            return None

        store = Gio.ListStore.new(DataObject)
        for child in item.children:
            store.append(child)

        return store

    def setup_expander(self, _widget, item):
        """Setup the widget to show in the Gtk.Listview"""
        label = Gtk.Label()
        label.set_size_request(-1, 50)
        label.set_margin_start(12)
        label.set_margin_end(12)
        label.set_wrap(True)
        label.set_wrap_mode(Pango.WrapMode.WORD_CHAR)
        label.set_hexpand(True)
        label.set_xalign(0) # align to start

        button = Gtk.Button.new_from_icon_name("edit-copy-symbolic")
        button.set_tooltip_text(_("Copy"))
        button.signal_handler_id = None
        button.set_halign(Gtk.Align.END)
        button.set_valign(Gtk.Align.CENTER)
        button.set_margin_end(10)

        box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        box.set_hexpand(True)
        box.append(label)
        box.append(button)

        expander = Gtk.TreeExpander.new()
        expander.set_child(box)
        item.set_child(expander)

    def unbind_expander(self, _widget, item):
        """Unbind the list item"""
        expander = item.get_child()
        box = expander.get_child()
        button = box.get_last_child()
        if button.signal_handler_id:
            button.disconnect(button.signal_handler_id)
            button.signal_handler_id = None

        if item.signal_handler_id:
            item.disconnect(item.signal_handler_id)
            item.signal_handler_id = None

    def copy_item_data(self, text):
        print(f"Copy text: {text}")
        self.clipboard.set(text)
        helpers.show_toast(self.overlay, _("Copied!"))

    def bind_expander(self, _widget, item):
        """Bind data from the store object to the widget"""
        expander = item.get_child()
        box = expander.get_child()
        label = box.get_first_child()
        button = box.get_last_child()

        row = item.get_item()
        expander.set_list_row(row)
        obj = row.get_item()
        label.set_label(obj.data)

        # Show a 'Copy' button when the row is selected
        handler_id = button.connect("clicked", lambda *_: self.copy_item_data(obj.data))
        button.signal_handler_id = handler_id
        button.set_visible(item.get_selected())

        handler_id = item.connect("notify::selected", lambda *_: button.set_visible(item.get_selected()))
        item.signal_handler_id = handler_id

    def value_to_data_object(self, key: str|None, value: int|str|dict|list|None) -> DataObject:
        """
        Convert the various types from lpac chip info into simple DataObject types
        """
        if key is None:
            key_part = ""
        elif isinstance(key, str):
            key_part = f"{key}: "
        else:
            raise RuntimeError(f"Unexpected key type: {type(key)}")

        if value is None:
            return DataObject(f"{key_part}<no value>")

        if isinstance(value, (int, str)):
            return DataObject(f"{key_part}{value}")

        if isinstance(value, dict):
            children = []
            for i, j in value.items():
                children.append(self.value_to_data_object(i, j))
            return DataObject(f"{key_part}{{...}}", children)

        if isinstance(value, list):
            children = []
            for i in value:
                children.append(self.value_to_data_object(None, i))
            return DataObject(f"{key_part}[...]", children)

        raise RuntimeError(f"Unexpected value type: {type(value)}")


    def update(self) -> None:
        store = Gio.ListStore.new(DataObject)

        model = Gtk.TreeListModel.new(root=store, passthrough=False, autoexpand=True,
                                      create_func=self.create_model_children)

        selection = Gtk.SingleSelection.new(model)

        factory = self.list_view.get_factory()
        factory.connect("setup", self.setup_expander)
        factory.connect("unbind", self.unbind_expander)
        factory.connect("bind", self.bind_expander)

        self.list_view.set_model(selection)

        chip_info = self._manager.get_chip_info(self._active_slot)
        for key, value in chip_info.items():
            store.append(self.value_to_data_object(key, value))
