# Copyright (c) 2017-2018 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# This program 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.
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.

from gi.repository import Secret, Gio

from threading import Thread

from passbook.keepass_secret import KeepassSecret
from passbook.define import App
from passbook.logger import Logger


class KeepassCollection:
    """
        For API compatiblity with Secret.Collection
    """

    def __init__(self, uri):
        """
            Init secret
        """
        self.__uri = uri
        self.__cancellable = Gio.Cancellable()
        self.__kp = None
        if App().secret_service is not None:
            SecretSchema = {
                "type": Secret.SchemaAttributeType.STRING,
                "uri": Secret.SchemaAttributeType.STRING
            }
            SecretAttributes = {
                "type": "Passbook keepass password",
                "uri": self.__uri
            }
            schema = Secret.Schema.new("org.gnome.Passbook",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            App().secret_service.search(schema, SecretAttributes,
                                        Secret.SearchFlags.ALL,
                                        None,
                                        self.__on_secret_search)

    def set_kp(self, kp):
        """
            Set keepass
            @param kp as pykeepass.PyKeePass
        """
        self.__kp = kp

    def save(self):
        """
            Save db in a thread
        """
        thread = Thread(target=self.__save)
        thread.daemon = True
        thread.start()

    def get_group(self, group_path):
        """
            Get group for path
            @param group_path as str
        """
        # Check parent groups exist
        parent = self.__kp.root_group
        for group in group_path.split("/"):
            if not group:
                continue
            kp_group = self.__kp.find_groups(
                name=group, group=parent, first=True)
            parent = self.__kp.add_group(parent, group)\
                if kp_group is None else kp_group
        return parent

    def add_password(self, desc, login, password, group):
        """
            Add a new password
            @param desc as str
            @param login as str
            @param password as str
            @param group as str
            @return pykeepass.entry
        """
        try:
            kp_group = self.get_group(group)
            item = self.__kp.add_entry(kp_group, desc, login, password)
            self.save()
            return item
        except Exception as e:
            Logger.error("KeepassCollection::add_password(): %s", e)
            return None

    def get_label(self):
        """
            Get collection label
            @return str
        """
        f = Gio.File.new_for_uri(self.__uri)
        label = f.get_basename()
        split = label.split(".")
        if split[-1] == "kdbx":
            label = split[:-1][0]
        return label

    def get_items(self):
        """
            Get collection items
            @return [KeepassSecret]
        """
        items = []
        for entry in self.__kp.entries:
            items.append(KeepassSecret(self, entry))
        return items

    def get_object_path(self):
        """
            Get object path
            @return str
        """
        f = Gio.File.new_for_uri(self.__uri)
        if f.query_exists():
            return self.__uri
        return None

    @property
    def uri(self):
        """
            Get collection uri
            @param uri as str
        """
        return self.__uri

    @property
    def kp(self):
        """
            Get keepass object
            @return pykeepass.PyKeePass
        """
        return self.__kp

#######################
# PRIVATE             #
#######################
    def __save(self):
        """
            Save collection
        """
        # Stop any running save
        self.__cancellable.cancel()
        self.__cancellable.reset()
        from pykeepass.kdbx_parsing.kdbx import KDBX
        f = Gio.File.new_for_uri(self.__uri)
        db = KDBX.build(self.__kp.kdbx,
                        password=self.__kp.password,
                        keyfile=self.__kp.keyfile)
        try:
            stream = f.replace(None, False,
                               Gio.FileCreateFlags.REPLACE_DESTINATION |
                               Gio.FileCreateFlags.PRIVATE,
                               self.__cancellable)
            stream.write_all(db)
            stream.close()
        except Exception as e:
            Logger.error("KeepassCollection::__save(): %s", e)

    def __on_secret_search(self, source, result):
        """
            Set username/password input
            @param source as GObject.Object
            @param result as Gio.AsyncResult
        """
        def load_kp(secret):
            from pykeepass import PyKeePass
            f = Gio.File.new_for_uri(self.__uri)
            if f.query_exists():
                self.__kp = PyKeePass(
                          f.get_path(),
                          password=secret.get().decode('utf-8'))

        def on_load_secret(source, result):
            try:
                secret = source.get_secret()
                if secret is not None:
                    thread = Thread(target=load_kp, args=(secret,))
                    thread.daemon = True
                    thread.start()
            except Exception as e:
                Logger.error("KeepassCollection::on_load_secret(): %s", e)
        try:
            if result is not None:
                items = App().secret_service.search_finish(result)
                if items:
                    items[0].load_secret(None,
                                         on_load_secret)
        except Exception as e:
            Logger.error("KeepassCollection::__on_secret_search(): %s", e)
