const { LogType } = require("loguix")
const fs = require("fs")
const path = require("path")
const { __glob } = require("./global-variables")
const ulog = new LogType("Users")
const keygen = require("./keygen")
const uuid = require("uuid")

var usersList = new Map()

setup()

function setup() {

    if(!fs.existsSync(__glob.USERS)) {
        ulog.log("Création du fichier utilisateur dans : " + __glob.USERS)
        fs.writeFileSync(__glob.USERS, JSON.stringify([], null, 2))
    }
}

module.exports.getAllUsers = async function() {
    return new Promise(async (resolve, reject) => {
        const users = await this.fetchUsers()

        // Remove for every people the password & the tokens

        for(var user of users) {
            user = user[1]
            user.password = null
            user.tokens = null
        }
        

        resolve(JSON.stringify(Array.from(users)))
    })
   
    
}

/**
 * Get all users from Users Data Base
 */
module.exports.fetchUsers = function () {
    
    ulog.step.init("fetch_user", "Récupération de tous les utilisateurs inscrit dans la base de donnée")
    const userFile = getFile()
    usersList = new Map()
    for(var userFetched of userFile) {
        const user = new this.User({
            username: userFetched.username,
            password: userFetched.password,
            display_name: userFetched.display_name,
            permission: userFetched.permission,
            tokens: userFetched.tokens,
            lastLogin: userFetched.lastLogin,
            picture: userFetched.picture

        })

        usersList.set(user.username, user)    
    }

    if(usersList.size == 0) {
        const adminUser = new this.User({
            "username": "admin",
            "password": "neutral",
            "display_name": "Administrateur",
            "permission": [
                "FILES_EXPLORER",
                "SERVICES",
                "SERVERS",
                "PIPELINES",
                "METRICS",
                "USERS",
                "LINKS",
                "SETTINGS"
            ],
            "tokens": [],
            "lastLogin": "DEFAULT ACCOUNT",
            "picture": "/images/users/default.jpg"
          })
    
    
          adminUser.register()
      

    }

    
    ulog.step.end("fetch_user")

    return usersList
}

/**
 * User Class is used to access to default user's properties and methods
 * @param {object} properties User properties with : username, password, display_name, permission... 
 */
module.exports.User =  class {
    username = null
    password = null;
    display_name = null
    permission = []
    tokens = [] 
    lastLogin = new Date()
    picture = "/images/users/default.jpg"
    
    constructor(properties) {

        if(properties) {

            this.username = properties.username
            this.password = keygen.encrypt(properties.password)
            this.display_name = properties.display_name
            this.permission = properties.permission
            this.tokens = properties.tokens
            this.lastLogin = properties.lastLogin
            this.picture = properties.picture

            const userFile = getFile()
        
            for(var userFetched of userFile) {
                if(properties.username == userFetched.username) { 
                    ulog.log("Récupération dans la base de donnée, de l'utilisateur : " + userFetched.username)
                    this.username = userFetched.username
                    this.password = userFetched.password
                    this.display_name = userFetched.display_name
                    this.permission = userFetched.permission
                    this.tokens = userFetched.tokens
                    this.lastLogin = userFetched.lastLogin
                }
            }
            
           

           
      
        }

     
        if(this.username == null) {
            ulog.error("One of user is without username ! [IMPORANT_FIELD_IS_MISSING]")
            this.username = Math.random()
        }
        if(this.password == null) {
            ulog.error("'" + this.username + "' is without password ! Password reset to 'default' [IMPORANT_FIELD_IS_MISSING]")
            this.password = keygen.encrypt("default")
        }
        if(this.display_name == null) {
            ulog.warn("'" + this.username + "' is without display name !")
            this.display_name = this.username
        }
        if(this.permission == null) {
            ulog.warn("'" + this.username + "' has no longer permission !")
        
        }
        if(this.tokens == null) {
            this.tokens = [] 
        }
        if(this.permission == null) {
            this.permission = [] 
        }
        if(this.lastLogin == null) {
            this.lastLogin = new Date()
        }
        if(this.picture == null) {
            this.picture = "/images/users/default.jpg"
        }
       
     
    }
    
    register() {
         
        var alreadyExist = false
        const userFile = getFile()
        
        for(var userFetched of userFile) {
            if(userFetched.username == this.username) { 
                userFile.splice(userFile.indexOf(userFetched), 1)
                ulog.log("Mise à jour dans la base de donnée, de l'utilisateur : " + this.username)
                alreadyExist = true
            }
        }
        if(!alreadyExist) {
            ulog.log("Création dans la base de donnée de l'utilisateur : " + this.username)
        }
        userFile.push(this)
        updateFile(userFile)
    
        usersList.set(this.username, this)
    }

    unregister() {
 
        var alreadyExist = false
        const userFile = getFile()
        
        for(var userFetched of userFile) {
            if(userFetched.username == this.username) { 
                userFile.splice(userFile.indexOf(userFetched), 1)
                ulog.log("Mise à jour dans la base de donnée, de l'utilisateur : " + this.username)
                alreadyExist = true
            }
        }
        if(!alreadyExist) {
            ulog.log("L'utilisateur n'est pas enregistré dans la base de donnée : " + this.username)
        }

        updateFile(userFile)
        usersList.delete(this.username)
    }

    checkPermission(name) {
        this.#sync()
        if(this.permission.includes(name)) {
            return true
        } else {

            return false
        }

    }

    addPermission(name) {
        this.#sync()
        for(var perms of this.permission) {
            if(name == perms) {
                ulog.warn("'" + this.username + "' a déjà la permission : " + name)
                return false
            }
        }
        this.permission.push(name)
        this.register()
    }

    removePermission(name) {
        this.#sync()
        var havePermission = false
        for(var perms of this.permission) {
            if(name == perms) {
                havePermission = true
            }
        }
        if(havePermission) {
            this.permission.splice(this.permission.indexOf(name), 1)
            this.register()
        } else {

            ulog.warn("'" + this.username + "' n'a pas la permission : " + name)
            return false
        }
        
    }

    setPassword(newPassword) {
        this.#sync()
        this.password = keygen.encrypt(newPassword)
        this.register()
        ulog.log("Le mot de passe de l'utilisateur a été modifié : " + this.username) 

    }
    getPassword() {
        this.#sync()
        return keygen.decrypt(this.password)
    }   

    generateToken() {
        this.#sync()
        const gToken = uuid.v4().toString()
        this.tokens.push(gToken)
        this.register()
        return gToken

    }

    removeToken(token) {
        this.#sync()
        var haveToken = false
        for(var aToken of this.tokens) {
            if(token == aToken) {
                haveToken = true
            }
        }
        if(haveToken) {
            this.tokens.splice(this.tokens.indexOf(token), 1)
            this.register()
        } else {

            ulog.warn("'" + this.username + "' n'a pas le token : " + token)
            return false
        }

    }

    setPicture(text) {
        this.#sync()
        this.picture = text
        this.register()
        ulog.log("La photo de l'utilisateur a été modifié : " + this.username) 

    }

    setDisplayName(text) {
        this.#sync()
        this.display_name = text
        this.register()
        ulog.log("Le nom d'affichage de l'utilisateur a été modifié : " + this.username) 

    }

    setNewUsername(text) {
        this.#sync()
        module.exports.deleteUser(this.username)
        this.username = text
        this.register()

        ulog.log("Le nom d'utilisateur de l'utilisateur a été modifié : " + this.username) 

    }

    setLastLogin(text) {
        this.#sync()
        this.lastLogin = text
        this.register()
    }

    setPicture(file) {
        this.#sync()
        var pictureDir = __glob.USERS_IMAGES + path.sep + uuid.v4().toString() + ".png"
        fs.writeFileSync(pictureDir, file)

        this.picture = pictureDir.replace(__glob.USERS_IMAGES + path.sep, "/images/users/")
        this.register()
    }

    setPermissions(permissions) {
        this.#sync()
        this.permission = permissions
        this.register()
    }

    #sync() {

        for(var userGet of usersList.keys()) {
            const userFetched = usersList.get(userGet)
            if(this.username == userFetched.username) { 
                this.username = userFetched.username
                this.password = userFetched.password
                this.display_name = userFetched.display_name
                this.permission = userFetched.permission
                this.tokens = userFetched.tokens
                this.lastLogin = userFetched.lastLogin
            }
        }
            
    }
}

module.exports.addUser = function(settings) {

   if(settings.username == '') {
       ulog.error("Le nom d'utilisateur est manquant")
       return "USERNAME_MISSING"
   } else if(settings.password == '') {
         ulog.error("Le mot de passe est manquant")
            return "PASSWORD_MISSING"
    } else if(settings.display_name == '') {
        ulog.error("Le nom d'affichage est manquant")
        return "DISPLAY_NAME_MISSING"
    } else if(this.getUser(settings.username)) {
       ulog.error("L'utilisateur existe déjà : " + settings.username)
       return "ALREADY_EXIST"
    } else {
        ulog.step.init("add_user", "Ajout d'un utilisateur dans la base de donnée : " + settings.username)
        var pictureDir = null
    
    
        if(settings.picture == null) {
            pictureDir = "/images/users/default.jpg"
        } else {
            pictureDir = __glob.USERS_IMAGES + path.sep + uuid.v4().toString() + ".png"
            fs.writeFileSync(pictureDir, settings.picture)
  
           
        }
    
        const user = new this.User({
            username: settings.username,
            display_name: settings.display_name,
            permission: settings.permissions,
            picture: pictureDir.replace(__glob.USERS_IMAGES + path.sep, "/images/users/")
    
        })
    
        user.setPassword(settings.password)
        user.register()
        ulog.step.end("add_user")
    }
   
}

module.exports.deleteUser = function(username) {
    ulog.step.init("delete_user", "Suppression d'un utilisateur dans la base de donnée : " + username)
    const user = this.getUser(username)
    user.unregister()
    ulog.step.end("delete_user")
    return "OK"
}


module.exports.getUser = function(username) {
    return usersList.get(username)
}

module.exports.editUser = function(settings) {
    if(settings.username == '') {
        ulog.error("Le nom d'utilisateur est manquant")
        return "USERNAME_MISSING"
    } else if(settings.display_name == '') {
         ulog.error("Le nom d'affichage est manquant")
         return "DISPLAY_NAME_MISSING"
     } else {
        ulog.step.init("edit_user", "Modification d'un utilisateur dans la base de donnée : " + settings.username)
        const user = this.fetchUsers().get(settings.username)
        if(user) {
            console.log(settings)
            if(settings.newusername && settings.newusername != settings.username) {
                if(this.getUser(settings.newusername)) {
                    ulog.error("L'utilisateur existe déjà : " + settings.username)
                    return "ALREADY_EXIST"
                } else {

                    user.setNewUsername(settings.newusername)
                }
            }
            if(settings.display_name) {
                user.setDisplayName(settings.display_name)
            }
         
            if(settings.password) {
                user.setPassword(settings.password)
            }
            
            if(settings.picture) {
                user.setPicture(settings.picture)
            }
            if(settings.permissions) {

                user.setPermissions(settings.permissions)
            } 
            ulog.step.end("edit_user")
            return "OK"
        } else {
            ulog.step.end("edit_user")
            return "NOT_EXIST"
        }

    }
}


/**
 * 
 * @returns User File
 */
function getFile() {
    const file = JSON.parse(fs.readFileSync(__glob.USERS))
    return file
}

/**
 * Update le fichier utilisateur avec un object
 * @param {Array} file 
 */
function updateFile(file) {
    if(fs.existsSync(__glob.USERS)) {
        ulog.log("Mise à jour du fichier utilisateur dans : " + __glob.USERS)
        fs.writeFileSync(__glob.USERS, JSON.stringify(file, null, 2))
    }
}

