# This Python file uses the following encoding: utf-8
import toml
from platformdirs import user_config_dir
from pathlib import Path
from PySide6.QtCore import QObject, Slot, Signal
from .Vermasseln import Vermasseln
import shutil
from urllib.parse import urlparse
from .DB.DbManager import DbManager
import os
from Crypto.Random import get_random_bytes
from base64 import b64encode
from .DB.UserManager import UserManager
from .PyqcrmFlags import PyqcrmFlags




class ConfigLoader(QObject):
    __config = None
    __version = "0.1-alpha"
    __check_enc_key = True


    dbConnectionError = Signal(str, bool)
    adminUserError = Signal(str, bool)
    adminNotAvailable = Signal()
    configurationReady = Signal()
    backupEncryptionKey = Signal()
    invalidEncryptionKey = Signal()

    def __init__(self):
        super().__init__()
        # print(f"In {__file__} file, __init__()")
        self.config_dir = user_config_dir() + '/pyqcrm'
        config_dir = Path(self.config_dir)
        if config_dir.exists():
            self.__configLoad()
            if self.__config:
                self.checkEncryptionKey()
        else:
            config_dir.mkdir(0o750, True, True)

    @Slot(dict, result= bool)
    def setConfig(self, app_config):
        # print(f"In {__file__} file, setConfig()")
        if not self.__config:
            base_conf = self.__initializeConfig()
            conf = self.__checkDbConnection(app_config)
            app_config = toml.dumps(app_config)
            if  conf:
                app_config = base_conf + app_config
                self.__config = toml.loads(app_config)
                self.__saveConfig()
                conf = self.__checkAdminUser()
                if conf:
                    self.configurationReady.emit()

    def __initializeConfig(self):
        # print(f"In {__file__} file, __initializeConfig()")
        self.__encrypt_key = b64encode(get_random_bytes(32)).decode("utf-8")
        conf = f"[pyqcrm]\nVERSION = \"{self.__version}\"\n"
        conf = conf + f"ENCRYPTION_KEY_VALID = \"No\"\n"
        conf = conf + f"ENCRYPTION_KEY = \"{self.__encrypt_key}\"\n\n"
        return conf

    def __checkDbConnection(self, db_config):
        # print(f"In {__file__} file, __checkDbConnection()")
        con = DbManager(db_config['database']).getConnection()
        if con:
            self.dbConnectionError.emit("Connection OK", True)
            return True
        else:
            self.dbConnectionError.emit("Connection fehlgeschlagen", False)
        return False


    def __saveConfig(self):
        # print(f"In {__file__} file, saveConfig()")
        try:
            with open (self.config_dir + '/pyqcrm.toml', 'w') as f:
                # print(self.__config)
                config = Vermasseln().oscarVermasseln(toml.dumps(self.__config))
                f.write(config)
        except FileNotFoundError:
            print("Konnte die Konfiguration nicht speichern.")


    def __checkAdminUser(self):
        # print(f"In {__file__} file, __checkAdminUser()")
        result = UserManager().checkAdmin()
        if not result:
            #if not result[0][0] == 1:
            self.adminUserError.emit("Kein Admin vorhanden", False)
            return False
        else:
            self.adminUserError.emit("Admin vorhanden", True)
        return True

    @Slot(dict, result= bool)
    def addAdminUser(self, user_config):
        # print(f"In {__file__} file, addAdminUser()")
        admin = UserManager(user_config["user"], PyqcrmFlags.ADMIN).createUser()
        if not admin:
            #self.adminNotAvailable.emit()
            self.adminUserError.emit("Benutzername nich verfügbar", False)
        else:
            self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] = 'Yes'
            self.__saveConfig()
            self.backupEncryptionKey.emit()
        return admin

    @Slot(str, str)
    def saveRecoveryKey(self, recovery_file, recovery_password):
        # print(f"In {__file__} file, saveRecoveryKey()")
        local = False
        rp = self.__setRecoveryPassword(recovery_password)
        rf = rp[1] + self.__encrypt_key + rp[0]
        rf = Vermasseln().oscarVermasseln(rf, local)
        rec_file = urlparse(recovery_file)
        if os.name == "nt":
            rec_file = rec_file [1:]
        else:
            rec_file = rec_file.path + ".pyqrec"
        try:
            with open(rec_file, "w") as f:
                f.write(rf)
                self.configurationReady.emit()
        except Exception as e:
            print(str(e))

    @Slot(str, str)
    def getRecoveryKey(self, recovery_file, recovery_password):
        # print(f"In {__file__} file, getRecoveryKey()")
        local = False
        rec_file = urlparse(recovery_file)
        rec_file = rec_file.path
        if os.name == "nt":
            rec_file = rec_file [1:]
        try:
            with open(rec_file, "r") as f:
                rf = f.read()
                rf = Vermasseln().entschluesseln(rf, local)
                ek = rf[128:]
                ek = ek[:-32]
                password = rf[:128]
                salt = rf[-32:]
                ok = self.__checkRecoveryPassword(recovery_password, password, salt)
                if ok:
                    self.__setEncryptionKey(ek)
                    self.configurationReady.emit()
                else:
                    self.__invalidateEncryptionKey()
                    self.invalidEncryptionKey.emit()
        except Exception as e:
            print(str(e))

    def __invalidateEncryptionKey(self):
        # print(f"In {__file__} file, __invalidateEncryptionKey()")
        self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] = 'No'
        self.__saveConfig()

    @Slot()
    def checkEncryptionKey(self):
        # print(f"In {__file__} file, __checkEncryptionKey()")
        if self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] == 'No':
            self.invalidEncryptionKey.emit()

    def __checkRecoveryPassword(self, recovery_password, password, salt):
        # print(f"In {__file__} file, __checkRecoveryPassword()")
        rp = self.__setRecoveryPassword(recovery_password, salt)
        return rp[1] == password

    @Slot(str, str) # todo: non local encryption
    def importConfig(self, confile, password):
        # print(f"In {__file__} file, importConfig()")
        confile = urlparse(confile)
        confile = confile.path

        if os.name == "nt":
            confile = confile[1:]
        shutil.copyfile(confile, self.config_dir+ '/pyqcrm.toml')

    def __configLoad(self):
        # print(f"In {__file__} file, __configLoad()")
        try:
            with open (self.config_dir + '/pyqcrm.toml', 'r') as f:
                config = f.read()
                self.__config = toml.loads(Vermasseln().entschluesseln(config))
                self.configurationReady.emit()
        except FileNotFoundError:
            print("Konnte die Konfiguration nicht laden.")
        except TypeError:
            print(f"Invalid Configuration: {__file__}")
        except Exception as e:
            print(str(e))

    def getConfig(self):
        # print(f"In {__file__} file, getConfig()")
        # print(self.__config)
        return self.__config

    def __setRecoveryPassword(self, key, salt = None):
        # print(f"In {__file__} file, __setRecoveryPassword()")
        key = Vermasseln.userPasswordHash(key, salt)
        return key.split("$")

    @Slot(str)
    def __setEncryptionKey(self, enc_key):
        # print(f"In {__file__} file, __setEncryptionKey()")
        self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] = 'Yes'
        self.__config['pyqcrm']['ENCRYPTION_KEY'] = enc_key
        self.__saveConfig()

