""" turtle plugin base

This file is part of Turtle.

Turtle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Turtle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Turtle. If not, see <https://www.gnu.org/licenses/>. 
"""
import os
import time
import subprocess
import pygit2
from gi.repository import Gio
import turtlevcs
from turtlevcs.service import TurtleServiceConnector


TURTLE_SHARED_ITEMS = [
    "settings",
    "about"
]
TURTLE_NON_REPO_ITEMS = [
    "init",
    "clone"
]

class TurtlePlugin(TurtleServiceConnector):
    """ turtle plugin """

    repo_files = None
    repo_background = None

    path_files = None
    path_background = None

    items_file = None
    items_background = None

    menu_item_creator = None
    menu_creator = None

    handles = {}

    settings = None

    repo_time = 0

    def __init__(
            self,
            menu_item_creator, menu_creator,
            info_provider_update_complete_invoke,
            is_test=False):
        self.menu_item_creator = menu_item_creator
        self.menu_creator = menu_creator
        self.info_provider_update_complete_invoke = info_provider_update_complete_invoke

        self.is_test = is_test
        if not is_test:
            self.settings = turtlevcs.get_settings()

        try:
            self._get_bus()
        except Exception as ex:
            print(f"cannot connect to dbus service: {str(ex)}")

    def _create_repository(self, path, is_background):
        if is_background:
            try:
                new_repo_time = time.time()
                diff_time = new_repo_time - self.repo_time

                # update the repo status once repo folder is left or after 3 seconds to enforce
                # a status update in service even if we do not leave repo folder, i.e. on F5 refresh
                if not self.repo_background or self.repo_background.workdir != path + "/" or diff_time > 3:
                    self.repo_time = new_repo_time
                    self.repo_background = pygit2.Repository(path)
                    if self.settings.get_boolean("show-emblems"):
                        self._set_path(path)
            except Exception as ex:
                self.repo_background = None
                raise ex

            return self.repo_background
        else:
            try:
                if self.repo_background and path.startswith(self.repo_background.workdir):
                    self.repo_files = self.repo_background
                else:
                    self.repo_files = pygit2.Repository(path)
            except Exception as ex:
                self.repo_files = None
                raise ex

            return self.repo_files

    def _show_emblems(self):
        if self.is_test:
            return True, True, True

        show_emblems = self.settings.get_boolean("show-emblems")
        show_status_emblem = self.settings.get_boolean("show-status-emblem")
        show_turtle_emblem = self.settings.get_boolean("show-turtle-emblem")

        return show_emblems, show_status_emblem, show_turtle_emblem

    def _valid_folder(self, path):
        if self.is_test:
            return True

        restrict_to_home = self.settings.get_boolean("restrict-to-home")

        if restrict_to_home:
            if not path.startswith("/home/") and not path.startswith("/var/home/"):
                return False

        return True

    def _open_turtle(self, module, path):
        subprocess.Popen(["turtle_cli", module, path])

    def _menu_activate_cb(self, menu, module):
        name = menu.props.name
        path = self.path_background if "background" in name else self.path_files

        self._open_turtle(module, path)

    def __create_menus(self, path, background, repo, is_file):
        if self.is_test:
            menu_settings = ["*commit", "+init", "+diff", "+unstage", "+clean", "+about"]
        else:
            menu_settings = self.settings.get_strv("menu-items")
        turtle_items = []
        menu_items = []

        for item in menu_settings:
            if item.startswith("*"):
                turtle_items.append(item[1:])
            elif item.startswith("+"):
                menu_items.append(item[1:])

        if repo:
            turtle_items = [i for i in turtle_items if i not in TURTLE_NON_REPO_ITEMS]
            menu_items = [i for i in menu_items if i not in TURTLE_NON_REPO_ITEMS]
        else:
            turtle_items = [i for i in turtle_items if i in (
                TURTLE_NON_REPO_ITEMS + TURTLE_SHARED_ITEMS)]
            menu_items = [i for i in menu_items if i in (
                TURTLE_NON_REPO_ITEMS + TURTLE_SHARED_ITEMS)]

        main_menu_items = self.__create_menu_helper(turtle_items, path, background, repo, is_file)
        sub_menu_items = self.__create_menu_helper(menu_items, path, background, repo, is_file)

        if len(sub_menu_items) > 0:
            turtle_menu = self.menu_item_creator(
                name=self.__create_menu_item_name("Turtle", background),
                label="Turtle",
                tip="Turtle Menu",
                icon="de.philippun1.turtle-symbolic")
            submenu = self.menu_creator()

            try:
                turtle_menu.set_submenu(submenu)
            except Exception as _ex:
                # fallback to set_menu mode for thunar
                turtle_menu.set_menu(submenu)

            for sub_item in sub_menu_items:
                submenu.append_item(sub_item)

            main_menu_items += [turtle_menu]

        return main_menu_items

    def __create_menu_helper(self, items, path, background, repo, is_file):
        menu_items = []

        for item in items:
            menu_item = self.__create_menu_item_helper(path, item, background, repo, is_file)
            if menu_item:
                menu_items.append(menu_item)

        return menu_items

    def __create_menu_item_helper(self, path, name, background, repo, is_file):
        item = self.__get_menu_item_by_id(name, background)

        if name == "commit":
            commit_label = "Commit"

            try:
                show_branch = self.settings.get_boolean("show-branch-in-menu")
                if show_branch:
                    head_name = repo.head.shorthand
                    head_name = head_name.replace("_", "__")
                    commit_label = f"Commit to \"{head_name}\""
            except Exception as _ex:
                pass

            item.props.label = commit_label
        elif name == "diff_file":
            valid_diff = False
            if is_file:
                status = repo.status()
                file = path.removeprefix(repo.workdir)
                if file in status:
                    if status[file] & pygit2.enums.FileStatus.WT_MODIFIED > 0:
                        valid_diff = True

            if not valid_diff:
                return None

        if name in ["revert", "stage", "unstage"]:
            if is_file:
                # for files we check if we can actually revert or (un)stage
                status = repo.status()
                file = path.removeprefix(repo.workdir)
                if file in status:
                    has_wt_status, _ = turtlevcs.has_worktree_status(status[file])
                    has_index_status, _ = turtlevcs.has_index_status(status[file])

                    if not has_wt_status and name in ["revert", "stage"]:
                        return None

                    if not has_index_status and name == "unstage":
                        return None
            elif name != "revert":
                # folders only have revert dialog
                return None


        return item

    def get_menu_items(self, files, background):
        """ create menu entries """

        self._create_menu_items()

        menu_items = []
        file = files[0]
        path = file.get_location().get_path()
        uri = file.get_uri()

        path_list = ",".join(f.get_location().get_path() for f in files)
        if background:
            self.path_background = path_list
        else:
            self.path_files = path_list

        # only use local files for now
        if uri.startswith("file://") and self._valid_folder(path):
            try:
                is_file = file.get_file_type() == Gio.FileType.REGULAR
            except Exception as _ex:
                # TODO fallback for thunar
                is_file = not file.is_directory()

            show_menu = False
            repo = None
            try:
                repo = self._create_repository(path, background)
                show_menu = True
            except Exception as _ex:
                if self.is_test or self.settings.get_boolean("enable-everywhere"):
                    show_menu = True

            if show_menu:
                menu_items = self.__create_menus(path, background, repo, is_file)

        return menu_items

    def update_file_info(self, provider, handle, closure, file):
        """ update_file_info from Nautilus.InfoProvider """
        finished = True

        try:
            path = file.get_location().get_path()
            uri = file.get_uri()

            try:
                is_file = file.get_file_type() == Gio.FileType.REGULAR
            except Exception as _ex:
                # TODO fallback for thunar
                is_file = not file.is_directory()

            # only use local files for now
            if uri.startswith("file://") and self._valid_folder(path):
                try:
                    show_emblems, show_status_emblem, show_turtle_emblem = self._show_emblems()

                    if show_emblems:
                        if not is_file or self.repo_background:
                            if not self.is_test and os.path.exists(path + "/.git"):
                                if show_turtle_emblem:
                                    file.add_emblem("de.philippun1.turtle-symbolic")
                                if not show_status_emblem:
                                    return True

                            key = id(file)
                            self.handles[key] = (provider, handle, closure, file)

                            finished = self._status_for_path_from_service(
                                key,
                                path,
                                self._check_folder_async_finished,
                                self._check_folder_async_error)
                except Exception as _ex:
                    pass
        except Exception as _ex:
            pass

        return finished

    def _check_folder_async_finished(self, key, status):
        try:
            (provider, handle, closure, file) = self.handles.pop(key, None)

            show_emblems, _show_status_emblem, _show_turtle_emblem = self._show_emblems()

            if show_emblems and file is not None:
                status = int(status)
                if status > -1:
                    self._set_emblem_for_status(file, status)

            self.info_provider_update_complete_invoke(
                closure,
                provider,
                handle,
                0
            )
        except Exception as _ex:
            pass

    def _check_folder_async_error(self, error):
        print(f"check folder async error: {error}")

    def _set_emblem_for_status(self, file, status):
        try:
            emblem = turtlevcs.status_icon_map[status]
        except Exception as _ex:
            # special case if file has WT and INDEX status
            emblem = "emblem-important"

        file.add_emblem(emblem)

    def _create_menu_items(self):
        def _create_items(items, background):
            for identifier, name, tip, icon in turtlevcs.MENU_ITEMS:
                item = self.menu_item_creator(
                    name=self.__create_menu_item_name(identifier, background),
                    label=name,
                    tip=tip,
                    icon=icon)
                item.connect("activate", self._menu_activate_cb, identifier)
                items[identifier] = item

        if self.items_background is None:
            self.items_background = {}
            _create_items(self.items_background, background=True)

        if self.items_file is None:
            self.items_file = {}
            _create_items(self.items_file, background=False)

    def __create_menu_item_name(self, name, background):
        item_name = "TurtleMenuProvider::"
        if background:
            item_name += "background" + "::"

        item_name += name

        return item_name

    def __get_menu_item_by_id(self, identifier, background):
        return self.items_background[identifier] if background \
            else self.items_file[identifier]
