from gi.repository import Adw, Gdk, GObject, Gtk

from enum import IntEnum, auto

from iotas.index_note_list import IndexNoteList
from iotas.note_manager import NoteManager
from iotas.ui_utils import check_for_search_starting, check_for_open_first_result_shortcut


class IndexSearchState(IntEnum):
    NO_TERM = auto()
    EMPTY = auto()
    RESULTS = auto()


@Gtk.Template(resource_path="/org/gnome/World/Iotas/ui/index_search_header_bar.ui")
class IndexSearchHeaderBar(Adw.Bin):
    __gtype_name__ = "IndexSearchHeaderBar"
    __gsignals__ = {
        "changed": (GObject.SignalFlags.RUN_FIRST, None, ()),
    }

    _entry = Gtk.Template.Child()

    def __init__(self) -> None:
        super().__init__()
        self.__active = False

    def setup(self, note_list: IndexNoteList, note_manager: NoteManager) -> None:
        """Perform initial setup.

        :param IndexNoteList note_list: The note list
        :param NoteManager note_manager: The note manager
        """
        self.__note_list = note_list
        self.__note_manager = note_manager

    def check_if_starting(
        self,
        controller: Gtk.EventControllerKey,
        keyval: int,
        state: Gdk.ModifierType,
        clear: bool = True,
    ) -> bool:
        """Check if starting search via type to search.

        :param Gtk.EventControllerKey controller: The key controller
        :param int keyval: The key value
        :param Gdk.ModifierType state: Any modifier state
        :param bool clear: Whether to clear the entry
        :return: Whether search is starting
        :rtype: bool
        """
        if not check_for_search_starting(controller, keyval, state):
            return False

        if clear:
            self._entry.set_text("")
        if controller.forward(self._entry.get_delegate()):
            return True

        return False

    def check_for_open_first_result_shortcut(
        self, controller: Gtk.EventControllerKey, keyval: int, state: Gdk.ModifierType
    ) -> bool:
        """Check keyboard shortcut to activate first result has been pressed.

        :param Gtk.EventControllerKey controller: The key controller
        :param int keyval: The key value
        :param Gdk.ModifierType state: Any modifier state
        :return: Whether pressed
        :rtype: bool
        """
        # Clunky way to determine if we have input focus
        window = self.get_root()
        focus_widget = window.get_property("focus-widget")
        if focus_widget.is_ancestor(self._entry):
            return check_for_open_first_result_shortcut(keyval, state)
        else:
            return False

    def enter(self, clear_text: bool, select_all: bool) -> None:
        """Enter search.

        :param bool clear_text: Whether to clear existing text
        :param bool select_all: Whether to select all text (when not clearing)
        """
        self._entry.grab_focus()
        if clear_text:
            self._entry.set_text("")
        elif select_all:
            self._entry.select_region(0, -1)
        self.active = True

    def search(self) -> IndexSearchState:
        """Perform a search.

        :return: The resulting search state
        :rtype: IndexSearchState
        """
        search_text = self.text
        if len(search_text) > 0:
            found_ids = self.__note_manager.search_notes(search_text, sort=True)
            if len(found_ids) > 0:
                self.__note_list.restrict_for_search_by_ids(found_ids)
                return IndexSearchState.RESULTS
            else:
                return IndexSearchState.EMPTY
        else:
            return IndexSearchState.NO_TERM

    def exit(self) -> None:
        """Exit search.

        :param bool value: New value
        """
        self.active = False

    def focus_entry(self) -> None:
        """Focus the text entry."""
        self._entry.grab_focus()

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

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

    @GObject.Property(type=str)
    def text(self) -> str:
        return self._entry.get_text().strip()

    @text.setter
    def text(self, value: str) -> str:
        return self._entry.set_text(value)

    @Gtk.Template.Callback()
    def _on_entry_changed(self, _entry: Gtk.Editable) -> None:
        self.emit("changed")
