var fs = require('fs');
module.exports = function(s,config,lang){
    //Authenticator functions
    s.api = {}
    s.superUsersApi = {}
    s.factorAuth = {}
    s.failedLoginAttempts = {}
    //
    var getUserByUid = function(params,columns,callback){
        if(!columns)columns = '*'
        s.sqlQuery(`SELECT ${columns} FROM Users WHERE uid=? AND ke=?`,[params.uid,params.ke],function(err,r){
            if(!r)r = []
            var user = r[0]
            callback(err,user)
        })
    }
    var getUserBySessionKey = function(params,callback){
        s.sqlQuery('SELECT * FROM Users WHERE auth=? AND ke=?',[params.auth,params.ke],function(err,r){
            if(!r)r = []
            var user = r[0]
            callback(err,user)
        })
    }
    var loginWithUsernameAndPassword = function(params,columns,callback){
        if(!columns)columns = '*'
        s.sqlQuery(`SELECT ${columns} FROM Users WHERE mail=? AND (pass=? OR pass=?) LIMIT 1`,[params.username,params.password,s.createHash(params.password)],function(err,r){
            if(!r)r = []
            var user = r[0]
            callback(err,user)
        })
    }
    var getApiKey = function(params,columns,callback){
        if(!columns)columns = '*'
        s.sqlQuery(`SELECT ${columns} FROM API WHERE code=? AND ke=?`,[params.auth,params.ke],function(err,r){
            if(!r)r = []
            var apiKey = r[0]
            callback(err,apiKey)
        })
    }
    var loginWithApiKey = function(params,callback){
        getApiKey(params,'*',function(err,apiKey){
            var isSessionKey = false
            if(apiKey){
                var sessionKey = params.auth
                createSession(apiKey,{
                    auth: sessionKey,
                    permissions: s.parseJSON(apiKey.details),
                    details: {}
                })
                getUserByUid(apiKey,'mail,details',function(err,user){
                    if(user){
                        try{
                            editSession({
                                auth: sessionKey
                            },{
                                mail: user.mail,
                                details: s.parseJSON(user.details),
                                lang: s.getLanguageFile(user.details.lang)
                            })
                        }catch(er){
                            console.log('FAILED TO EDIT',er)
                        }
                    }
                    callback(err,s.api[params.auth])
                })
            }else{
                getUserBySessionKey(params,function(err,user){
                    if(user){
                        isSessionKey = true
                        createSession(apiKey,{
                            details: JSON.parse(user.details),
                            permissions: {}
                        })
                        callback(err,user,isSessionKey)
                    }
                })
            }
        })
    }
    var createSession = function(user,additionalData){
        if(user){
            var generatedId
            if(!additionalData)additionalData = {}
            if(!user.ip)user.ip = '0.0.0.0'
            if(!user.auth && !user.code){
                generatedId = s.gid(20)
            }else{
                generatedId = user.auth || user.code
            }
            user.details = s.parseJSON(user.details)
            user.permissions = {}
            s.api[generatedId] = Object.assign(user,additionalData)
            return generatedId
        }
    }
    var editSession = function(user,additionalData){
        if(user){
            if(!additionalData)additionalData = {}
            Object.keys(additionalData).forEach(function(value,key){
                s.api[user.auth][key] = value
            })
        }
    }
    var failHttpAuthentication = function(res,req,message){
        if(!message)message = lang['Not Authorized']
        res.end(s.prettyPrint({
            ok: false,
            msg: message
        }))
    }
    var resetActiveSessionTimer = function(activeSession){
        if(activeSession){
            clearTimeout(activeSession.timeout)
            activeSession.timeout = setTimeout(function(){
                delete(activeSession)
            },1000 * 60 * 5)
        }
    }
    s.auth = function(params,onSuccessComplete,res,req){
        if(req){
            //express (http server) use of auth function
            params.ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress
            var onFail = function(message){
                failHttpAuthentication(res,req,message)
            }
        }else{
            //socket.io use of auth function
            var onFail = function(){
                //maybe log
            }
        }
        var onSuccess = function(user){
            var activeSession = s.api[params.auth]
            if(
                activeSession &&
                (
                    activeSession.ip.indexOf('0.0.0.0') > -1 ||
                    activeSession.ip.indexOf(params.ip) > -1
                )
            ){
                if(!user.lang){
                    var details = s.parseJSON(user.details).lang
                    user.lang = s.getDefinitonFile(user.details.lang) || s.copySystemDefaultLanguage()
                }
                onSuccessComplete(user)
            }else{
                onFail()
            }
        }
        if(s.group[params.ke] && s.group[params.ke].users && s.group[params.ke].users[params.auth]){
            var activeSession = s.group[params.ke].users[params.auth]
            activeSession.permissions = {}
            if(!activeSession.lang){
                activeSession.lang = s.copySystemDefaultLanguage()
            }
            onSuccessComplete(activeSession)
        }else{
            if(s.api[params.auth] && s.api[params.auth].details){
                var activeSession = s.api[params.auth]
                onSuccess(activeSession)
                if(activeSession.timeout){
                   resetActiveSessionTimer(activeSession)
                }
            }else{
                if(params.username && params.username !== '' && params.password && params.password !== ''){
                    loginWithUsernameAndPassword(params,'*',function(err,user){
                        if(user){
                            params.auth = user.auth
                            createSession(user)
                            resetActiveSessionTimer(s.api[params.auth])
                            onSuccess(user)
                        }else{
                            onFail()
                        }
                    })
                }else{
                    loginWithApiKey(params,function(err,user,isSessionKey){
                        if(isSessionKey)resetActiveSessionTimer(s.api[params.auth])
                        if(user){
                            createSession(user,{
                                auth: params.auth
                            })
                            onSuccess(s.api[params.auth])
                        }else{
                            onFail()
                        }
                    })
                }
            }
        }
    }
    //super user authentication handler
    s.superAuth = function(params,callback,res,req){
        var userFound = false
        var userSelected = false
        var adminUsersSelected = null
        try{
            var success = function(){
                var chosenConfig = config
                if(req && res){
                    chosenConfig = s.getConfigWithBranding(req.hostname)
                    res.setHeader('Content-Type', 'application/json')
                    var ip = req.headers['cf-connecting-ip']||req.headers["CF-Connecting-IP"]||req.headers["'x-forwarded-for"]||req.connection.remoteAddress;
                    var resp = {
                        ok: userFound,
                        ip: ip
                    }
                    if(userFound === false){
                        resp.msg = lang['Not Authorized']
                        res.end(s.prettyPrint(resp))
                    }
                    if(userSelected){
                        resp.$user = userSelected
                    }
                    if(adminUsersSelected){
                        resp.users = adminUsersSelected
                    }
                }
                callback({
                    ip : ip,
                    $user: userSelected,
                    users: adminUsersSelected,
                    config: chosenConfig,
                    lang:lang
                })
            }
            var foundUser = function(){
                if(params.users === true){
                    s.sqlQuery('SELECT * FROM Users WHERE details NOT LIKE ?',['%"sub"%'],function(err,r) {
                        adminUsersSelected = r
                        success()
                    })
                }else{
                    success()
                }
            }
            if(params.auth && s.superUsersApi[params.auth]){
                userFound = true
                userSelected = s.superUsersApi[params.auth].$user
                foundUser()
            }else{
                var superUserList = JSON.parse(fs.readFileSync(s.location.super))
                superUserList.forEach(function(superUser,n){
                    if(
                        userFound === false &&
                        (
                            params.auth && superUser.tokens && superUser.tokens[params.auth] || //using API key (object)
                            params.auth && superUser.tokens && superUser.tokens.indexOf && superUser.tokens.indexOf(params.auth) > -1 || //using API key (array)
                            (
                                params.mail && params.mail.toLowerCase() === superUser.mail.toLowerCase() && //email matches
                                (
                                    params.pass === superUser.pass || //user give it already hashed
                                    superUser.pass === s.createHash(params.pass) || //hash and check it
                                    superUser.pass.toLowerCase() === s.md5(params.pass).toLowerCase() //check if still using md5
                                )
                            )
                        )
                    ){
                        userFound = true
                        userSelected = superUser
                        foundUser()
                    }
                })
            }
        }catch(err){
            console.log('The following error may mean your super.json is not formatted correctly.')
            console.log(err)
        }
        if(userFound === true){
            return true
        }else{
            if(res)res.end(s.prettyPrint({
                ok: false,
                msg: lang['Not Authorized']
            }))
            return false
        }
    }
    s.basicOrApiAuthentication = function(username,password,callback){
        var splitUsername = username.split('@')
        if(splitUsername[1] && splitUsername[1].toLowerCase().indexOf('shinobi') > -1){
            getApiKey({
                auth: splitUsername[0],
                ke: password
            },'ke,uid',callback)
        }else{
            loginWithUsernameAndPassword({
                username: username,
                password: password
            },'ke,uid',callback)
        }
    }
}