# SPDX-FileCopyrightText: 2022 Chris Heywood
#
# SPDX-License-Identifier: GPL-3.0-or-later

from gi.repository import GObject, Gtk

import re
from typing import Optional

from iotas.category import Category
from iotas.config_manager import ConfigManager
from iotas.note import Note


@Gtk.Template(resource_path="/org/gnome/World/Iotas/ui/index_row.ui")
class IndexRow(Gtk.Box):
    __gtype_name__ = "IndexRow"

    __gsignals__ = {
        "checkbox-toggled": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
        "context-click": (GObject.SignalFlags.RUN_FIRST, None, ()),
    }

    _title: Gtk.Label = Gtk.Template.Child()
    _excerpt: Gtk.Label = Gtk.Template.Child()
    _category: Gtk.Label = Gtk.Template.Child()
    _revealer: Gtk.Revealer = Gtk.Template.Child()
    _selection_checkbox: Gtk.CheckButton = Gtk.Template.Child()

    def __init__(self) -> None:
        super().__init__()
        self.__note: Optional[Note] = None
        self.__signal_ids: list[int] = []
        self.__index_category: Category | None = None
        self.connect("unrealize", lambda _o: self.__disconnect_signals())

    def populate(self, note: Note, category_style: str) -> None:
        """Populate the row.

        :param Note note: The note
        :param str category_style: The category style
        """
        if not note or self.__note == note:
            return
        if self.__note is not None:
            self.__disconnect_signals()

        self.__note = note
        self.__refresh_title_label()
        self.__category_style = category_style
        self.__set_default_category_label()
        note.bind_property("excerpt", self._excerpt, "label", GObject.BindingFlags.SYNC_CREATE)

        self.__signal_ids.append(
            note.connect("notify::category", lambda _o, _v: self.__refresh_category_label_style())
        )
        self.__signal_ids.append(
            note.connect("notify::dirty", lambda _o, _v: self.__refresh_title_label())
        )
        self.__signal_ids.append(
            note.connect("notify::title", lambda _o, _v: self.__refresh_title_label())
        )

    def set_checkbox_value(self, value) -> None:
        """Toggle the selected checkbox value.

        :param bool value: The new value
        """
        self._selection_checkbox.set_active(value)

    def toggle_selected(self) -> None:
        """Toggle the selected checkbox."""
        self._selection_checkbox.set_active(not self._selection_checkbox.get_active())

    def update_category_label(
        self,
        index_category: Category,
        # TODO Use StrEnum
        style: str,
    ) -> None:
        """Update the category label.

        :param Category category: The index category
        :param str category_style: The category style
        """
        self.__index_category = index_category
        self.__category_style = style
        self.__refresh_category_label_style()

    @GObject.Property(type=Note, default=None)
    def note(self) -> Optional[Note]:
        return self.__note

    @GObject.Property(type=bool, default=False)
    def child_revealed(self) -> bool:
        return self._revealer.get_reveal_child()

    @child_revealed.setter
    def set_child_revealed(self, revealed: bool) -> None:
        self._revealer.set_reveal_child(revealed)

    @Gtk.Template.Callback()
    def _on_selection_checkbox_toggled(self, button: Gtk.CheckButton) -> None:
        self.emit("checkbox-toggled", button.get_active())

    @Gtk.Template.Callback()
    def _on_longpress(self, _gesture: Gtk.GestureLongPress, _x: float, _y: float) -> None:
        self.emit("context-click")

    @Gtk.Template.Callback()
    def _on_right_click(
        self, _gesture: Gtk.GestureClick, _n_press: int, _x: float, _y: float
    ) -> None:
        self.emit("context-click")

    def __refresh_title_label(self) -> None:
        assert self.__note
        txt = self.__note.title
        remote_syncing = ConfigManager.get_default().nextcloud_sync_configured
        if self.__note.dirty and remote_syncing:
            txt = "•  " + txt
        self._title.set_text(txt)

    def __refresh_category_label_style(self) -> None:
        visible = self.__determine_category_visibility()
        self._category.set_visible(visible)
        if visible:
            category = self.__build_formatted_relative_category_name()
            self._category.set_label(category)

    def __determine_category_visibility(self) -> bool:
        assert self.__note
        if self.__category_style == "none":
            visible = False
        elif self.__note.category == "":
            visible = False
        elif (
            self.__index_category is not None
            and self.__index_category.special_purpose is None
            and self.__index_category.name == self.__note.category
        ):
            visible = False
        else:
            visible = True
        return visible

    def __build_formatted_relative_category_name(self) -> str:
        assert self.__note
        category = self.__note.category
        if self.__index_category is not None and self.__index_category.special_purpose is None:
            category = re.sub(r"^" + re.escape(self.__index_category.name) + "/", "", category)
        if "/" in category:
            category = " / ".join(category.split("/"))
        return category

    def __set_default_category_label(self) -> None:
        assert self.__note
        category = self.__note.category
        if category != "":
            if "/" in category:
                category = " / ".join(category.split("/"))
            self._category.set_label(category)
            self._category.set_visible(True)

    def __disconnect_signals(self) -> None:
        for sig_id in self.__signal_ids:
            self.note.disconnect(sig_id)
        self.__signal_ids = []
