from __future__ import annotations

from gi.repository import GObject, Gtk

import re

from iotas.category import Category
import iotas.config_manager
from iotas.note import Note
from iotas.ui_utils import add_mouse_right_click_accel, add_touch_longpress_accel


@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.Template.Child()
    _excerpt = Gtk.Template.Child()
    _category = Gtk.Template.Child()
    _revealer = Gtk.Template.Child()
    _selection_checkbox = Gtk.Template.Child()

    def __init__(self):
        super().__init__()
        self.__note = None
        self.__signal_ids = list()
        self.__index_category = None

        add_touch_longpress_accel(
            self, self.__start_selection, propagation=Gtk.PropagationPhase.CAPTURE
        )
        add_mouse_right_click_accel(self, self.__start_selection)
        self.connect("unrealize", self.__on_unrealize)

    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
        note.bind_property("excerpt", self._excerpt, "label", GObject.BindingFlags.SYNC_CREATE)
        self.__signal_ids.append(note.connect("notify::category", self.__on_category_changed))

        self.__signal_ids.append(note.connect("notify::dirty", self.__on_title_or_dirty_changed))
        self.__signal_ids.append(note.connect("notify::title", self.__on_title_or_dirty_changed))

    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(
        self,
        category: Category,
        # TODO Move to Python 3.11+ and use StrEnum
        style: str,
    ) -> None:
        """Update the category label.

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

    @GObject.Property(type=Note, default=None)
    def note(self) -> 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 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())

    def __on_title_or_dirty_changed(
        self,
        _obj: GObject.Object,
        _value: GObject.ParamSpec,
    ) -> None:
        self.__refresh_title_label()

    def __on_category_changed(
        self,
        _obj: GObject.Object,
        _value: GObject.ParamSpec,
    ) -> None:
        self.__refresh_category_label()

    def __start_selection(self) -> None:
        self.emit("context-click")

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

    def __refresh_category_label(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:
        note_has_category = self.__note.category != ""
        visible = self.__category_style != "none"
        visible = visible and note_has_category
        if (
            visible
            and self.__index_category is not None
            and self.__index_category.special_purpose is None
            and self.__index_category.name == self.__note.category
        ):
            visible = False
        return visible

    def __build_formatted_relative_category_name(self) -> str:
        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 __disconnect_signals(self) -> None:
        for sig_id in self.__signal_ids:
            self.note.disconnect(sig_id)
        self.__signal_ids = []

    def __on_unrealize(self, _row: IndexRow) -> None:
        self.__disconnect_signals()
