from gi.repository import Adw, Gio, GLib, GObject, Gtk

import logging

import iotas.config_manager
from iotas.category_manager import CategoryManager
from iotas import const
from iotas.editor import Editor
from iotas.index import Index
from iotas.note import Note
from iotas.note_database import NoteDatabase
from iotas.note_manager import NoteManager
from iotas.nextcloud_login_window import NextcloudLoginWindow
from iotas.sync_manager import SyncManager


@Gtk.Template(resource_path="/org/gnome/gitlab/cheywood/Iotas/ui/window.ui")
class Window(Adw.ApplicationWindow):
    __gtype_name__ = "Window"

    _leaflet = Gtk.Template.Child()
    _index = Gtk.Template.Child()
    _editor = Gtk.Template.Child()

    def __init__(self, app: Adw.Application, db: NoteDatabase, sync_manager: SyncManager):
        super().__init__(application=app)

        self.set_icon_name(const.APP_ID)

        if iotas.const.IS_DEVEL:
            self.add_css_class("devel")

        self.app = self.get_application()

        self.__db = db
        self.__sync_manager = sync_manager

        self.__category_manager = CategoryManager(self.__db)
        self.__note_manager = NoteManager(self.__db, self.__category_manager)

        self.__using_keyboard_navigation = False
        self.__secret_service_failed = False

        self.__setup_actions()

        self._index.setup(self.__note_manager, self.__category_manager, self.__sync_manager)
        self._index.connect("note-opened", self.__on_note_opened)

        self._editor.setup(self.__category_manager.tree_store)
        self._editor.connect("note-modified", self.__on_note_edited)
        self._editor.connect("note-deleted", self.__on_editor_note_deleted)
        self._editor.connect("category-changed", self.__on_note_category_changed)
        self._editor.connect("exit", self.__on_editor_exit)

        self.__note_manager.connect("sync-requested", self.__on_sync_requested)

        self.__sync_manager.connect("ready", self.__on_sync_ready)
        self.__sync_manager.connect("note-conflict", self.__on_note_conflict)
        self.__sync_manager.connect("secret-service-failure", self.__on_secret_service_failure)
        self.__sync_manager.connect("notify::authenticated", self.__on_sync_authenticated_changed)
        self.__sync_manager.connect(
            "remote-category-sanitised", self.__on_remote_category_sanitised
        )

        self.connect("close-request", self.__on_close_request)

        (width, height) = iotas.config_manager.get_window_size()
        self.set_default_size(width, height)
        self._index.active = True
        self._editor.active = False

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

    @using_keyboard_navigation.setter
    def using_keyboard_navigation(self, value: bool) -> None:
        self.__using_keyboard_navigation = value

    @Gtk.Template.Callback()
    def _on_child_switch(self, _obj: GObject.Object, _value: GObject.ParamSpec) -> None:
        returned_to_index = self._leaflet.get_visible_child() is self._index
        if returned_to_index:
            note = self._editor.current_note
            self.__note_manager.persist_note_after_closing(note)
            self._index.refresh_after_note_closed(note)
            self._editor.close_note()
        self._index.active = returned_to_index
        self._editor.active = not returned_to_index

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

        action = Gio.SimpleAction.new("fullscreen")
        action.connect("activate", self.__toggle_fullscreen)
        action_group.add_action(action)
        app.set_accels_for_action("win.fullscreen", ["F11"])

        action = Gio.SimpleAction.new("refresh")
        action.connect("activate", self.__on_refresh)
        action_group.add_action(action)
        app.set_accels_for_action("win.refresh", ["<Control>r"])
        action.set_enabled(False)

        action = Gio.SimpleAction.new("start-nextcloud-signin")
        action.connect("activate", self.__on_start_nextcloud_signin)
        action_group.add_action(action)

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

    def __on_note_opened(self, _obj: Index, note: Note) -> None:
        if not note.content_loaded:
            self.__db.populate_note_content(note)
        self._editor.init_note(note)
        self._leaflet.set_visible_child(self._editor)

    def __on_editor_note_deleted(self, _obj: GObject.Object, note: Note) -> None:
        if not self._leaflet.get_visible_child() is self._editor:
            return

        self._leaflet.set_visible_child(self._index)
        self.__sync_manager.flush_pending_deletions()
        self.__note_manager.delete_notes([note])
        self._index.update_for_note_deletions([note])

    def __on_editor_exit(self, _obj: GObject.Object) -> None:
        self._leaflet.set_visible_child(self._index)

    def __on_note_conflict(self, _obj: GObject.Object, note: Note) -> None:
        if self._editor.current_note is not None and note is self._editor.current_note:
            logging.info("Note being currently edited in conflict")
            self._editor.current_note.handling_conflict = True
            self._leaflet.set_visible_child(self._index)
            self._index.show_note_conflict_alert()

    def __on_sync_ready(self, _obj: GObject.Object, new_setup: bool) -> None:
        action = self.__action_group.lookup("refresh")
        action.set_enabled(True)
        action = self.__action_group.lookup("start-nextcloud-signin")
        action.set_enabled(False)

    def __on_refresh(
        self,
        _obj: GObject.Object,
        _value: GObject.ParamSpec,
    ) -> None:
        """Refresh from server."""
        self.__sync_manager.sync_now()

    def __on_start_nextcloud_signin(
        self,
        _obj: GObject.Object,
        _value: GObject.ParamSpec,
    ) -> None:
        """Perform Nextcloud sign in."""
        window = NextcloudLoginWindow(self.__sync_manager, self.__secret_service_failed)
        window.set_transient_for(self)
        window.present()

    def __on_sync_authenticated_changed(
        self,
        _obj: GObject.Object,
        _value: GObject.ParamSpec,
    ) -> None:
        self._editor.set_sync_authenticated(self.__sync_manager.authenticated)

    def __toggle_fullscreen(self, _action: Gio.SimpleAction, _param: GLib.Variant) -> None:
        hiding_headerbar = iotas.config_manager.get_hide_editor_headerbar_when_fullscreen()
        if self.is_fullscreen():
            self.unfullscreen()
            if hiding_headerbar and not self._editor.headerbar_manually_hidden:
                self._editor.show_headerbar()
        else:
            self.fullscreen()
            if hiding_headerbar:
                editor_active = self._leaflet.get_visible_child() is self._editor
                self._editor.hide_headerbar(editor_active)

    def __on_close_request(self, _obj: GObject.Object) -> None:
        """On window destroyed."""
        self.__sync_manager.close()
        if not self.is_fullscreen():
            iotas.config_manager.set_window_size(self.get_width(), self.get_height())
        iotas.config_manager.set_first_start(False)
        self.close()

    def __on_secret_service_failure(self, _obj: GObject.Object) -> None:
        self.__secret_service_failed = True
        self._index.show_secret_service_failure_alert()

    def __on_note_edited(self, _editor: Editor, note: Note) -> None:
        self.__note_manager.persist_note_while_editing(note)

    def __on_note_category_changed(
        self, _obj: GObject.Object, note: Note, old_category: str
    ) -> None:
        self.__note_manager.persist_note_category(note, old_category)

    def __on_sync_requested(self, _obj: GObject.Object) -> None:
        self.__sync_manager.sync_now()

    def __on_remote_category_sanitised(
        self, _obj: SyncManager, old_category: str, new_category: str
    ) -> None:
        self.__category_manager.note_category_changed(old_category, new_category)
