from gettext import gettext as _
from gi.repository import Adw, Gio, GLib, GObject, Gtk

from typing import List, Tuple

from iotas.category_header_bar import CategoryHeaderBar
from iotas.category_treeview_list_store import CategoryTreeViewListStore
from iotas.note import Note
from iotas.ui_utils import show_error_dialog


@Gtk.Template(resource_path="/org/gnome/gitlab/cheywood/Iotas/ui/selection_header_bar.ui")
class SelectionHeaderBar(Adw.Bin):
    __gtype_name__ = "SelectionHeaderBar"

    __gsignals__ = {
        "set-favourite": (GObject.SignalFlags.RUN_FIRST, None, ()),
        "clear-favourite": (GObject.SignalFlags.RUN_FIRST, None, ()),
        "delete": (GObject.SignalFlags.RUN_FIRST, None, ()),
        "categories-changed": (GObject.SignalFlags.RUN_FIRST, None, ()),
        "abort": (GObject.SignalFlags.RUN_FIRST, None, ()),
    }

    _stack = Gtk.Template.Child()
    _main_header_bar = Gtk.Template.Child()
    _category_header_bar = Gtk.Template.Child()
    _count_label = Gtk.Template.Child()

    def __init__(self):
        super().__init__()
        self.__active = False
        self.__selected = []
        self._category_header_bar.connect("categories-changed", self.__on_categories_changed)
        self._category_header_bar.connect("abort", self.__on_abort)
        self.__setup_actions()

    def setup(self, model: CategoryTreeViewListStore) -> None:
        """Perform initial setup.

        :param CategoryTreeViewListStore model: Categories model
        """
        self._category_header_bar.setup(model)

    def activate(self) -> None:
        """Activate header bar."""
        self._stack.set_visible_child(self._main_header_bar)
        self.active = True
        self.__selected = []
        self.__refresh()

    def set_note_selected(self, note: Note, select: bool) -> None:
        """Add or remove note from selection.

        :param Note note: Note to select
        :param bool select: Whether to add or remote from selection
        """
        if select:
            if note not in self.__selected:
                self.__selected.append(note)
        else:
            if note in self.__selected:
                self.__selected.remove(note)
        self.__refresh()

    def get_selected(self) -> List[Note]:
        """Get selected notes.

        :return: Selected notes
        :rtype: List[Note]
        """
        return self.__selected

    def get_categories_changeset(self) -> List[Tuple[Note, str]]:
        """Get categories changeset.

        :return: Selected of category-changed notes and their previous categories
        :rtype: List[Tuple[Note, str]]
        """
        return self._category_header_bar.take_changeset()

    def edit_category_for_selection(self) -> None:
        """Edit the category for the selected notes."""
        if not self.__selected:
            return

        msg = _("Unable to change category on read-only note")
        if self.__error_for_readonly_in_selection(msg):
            return

        self._stack.set_visible_child(self._category_header_bar)
        self._category_header_bar.activate(self.__selected)

    def toggle_favourite_on_selection(self) -> None:
        """Toggle favourites for the selected notes.

        If a collection of notes with mixed favourite states are selected the destination state
        is the less common one in those selected.
        """
        if not self.__selected:
            return

        set = len([x for x in self.__selected if x.favourite])
        unset = len(self.__selected) - set
        if set >= unset:
            self.emit("clear-favourite")
        else:
            self.emit("set-favourite")

    def handle_delete_keyboard_shortcut(self) -> None:
        """Handle pressed delete keyboard shortcut."""
        if self._stack.get_visible_child() == self._category_header_bar:
            self._category_header_bar.clear_and_apply()
        else:
            self.__delete_selection()

    @GObject.Property(type=bool, default=False)
    def active(self) -> bool:
        return self.__active

    @active.setter
    def active(self, value: bool) -> None:
        self.__active = value

    def __setup_actions(self) -> None:
        action_group = Gio.SimpleActionGroup.new()

        action = Gio.SimpleAction.new("categorise")
        action.connect("activate", self.__on_categorise)
        action_group.add_action(action)

        action = Gio.SimpleAction.new("favourite")
        action.connect("activate", self.__on_favourite)
        action_group.add_action(action)

        action = Gio.SimpleAction.new("delete")
        action.connect("activate", self.__on_delete)
        action_group.add_action(action)

        self.insert_action_group("selection", action_group)
        self.__action_group = action_group

    def __on_favourite(self, _action: Gio.SimpleAction, _param: GLib.Variant) -> None:
        self.toggle_favourite_on_selection()

    def __on_categorise(self, _action: Gio.SimpleAction, _param: GLib.Variant) -> None:
        self.edit_category_for_selection()

    def __on_delete(self, _action: Gio.SimpleAction, _param: GLib.Variant) -> None:
        self.__delete_selection()

    def __on_categories_changed(self, _obj: CategoryHeaderBar) -> None:
        self.emit("categories-changed")

    def __on_abort(self, _obj: CategoryHeaderBar) -> None:
        self.emit("abort")

    def __delete_selection(self) -> None:
        """Delete the selected notes."""
        if not self.__selected:
            return
        self.emit("delete")

    def __refresh(self) -> None:
        # Translators: Description, {} is a number
        self._count_label.set_text(_("{} Selected").format(len(self.__selected)))

        if self._stack.get_visible_child() == self._category_header_bar:
            self._category_header_bar.replace_notes(self.__selected)

        for action in self.__action_group.list_actions():
            action = self.__action_group.lookup(action)
            action.set_property("enabled", self.__selected)

    def __error_for_readonly_in_selection(self, message: str) -> bool:
        have_readonly = False
        for note in self.__selected:
            if note.read_only:
                have_readonly = True
                break
        if have_readonly:
            show_error_dialog(self.get_root(), _("Unable to change category on read-only note"))
        return have_readonly
