// Original file created by Prof.Dr. Matthias Hopf

/*
 * Authorization
 */
var   common, Users;
const ldap   = require ('./ldap_ohm'),
      crypto = require ('./crypto'),
      dbs    = require ('./dbs');

// deactivated is not used yet
const serverVisibleSession = { user: true, name: true, type: true, mail: true, roles: true, gender: true, deactivated: true, host: true };
const clientVisibleSession = { user: true, name: true, type: true, mail: true, roles: true, gender: true };

// Return user role, query from found.mail
function getUserRole(found) {
    var roles = "user";
    var mail = found.mail;
    if (!/\d/.test(mail)) {
        // Mail contains no number
        roles += ',"author"';
    }
    return JSON.stringify('{'+roles+'}');
}

// Fill in session object
function fillSession (req, user, roles, cb) {
    if (req.session === undefined)
        next (common.genError (500, "Error"));
    // regenerate a new session-id with clean instance
    if (user !== undefined && roles !== undefined) {
        req.session.regenerate (function (err) {
            if (user !== undefined && ! err) {
                common.shallowCopy (user, serverVisibleSession, {roles: true}, req.session);
//                console.info(req.session);
                if (user._id) {
                    req.session.user = user._id;
                }
                req.session.roles = roles;
                console.info(req.session);
            }
            return cb (err);
        });
    } else {
        return cb ();
    }
}

// Save found user into DB, if not already exists
function saveFoundToDB(found, cb) {
//    console.info(found);
    Users.findById(found.user)
        .exec(function(err, result){
        if (err) {
            console.error("Error: Users collection.");
            console.error(err);
        }
        // User doesn't exist
        if (found !== undefined && !result) {
            Users.create({
                _id: found.user,
                name: found.name,
                mail: found.mail,
                type: found.type,
                gender: found.gender,
                abos: '',
                bookmarks: '',
                roles: getUserRole(found),
            }, function(err, done) {
                if (err) {
                    console.error("User creation: Failed");
                    console.error(err);
                } else {
                    console.info("User created: "+ found.user);
                }
                if (done == null) {
                    console.error("Can not create user.");
                }
                return cb(err);
            });
        } else {
            return cb(err);
        }
    });
}

const authorization = {
    // Generate Error object suitible for throwing or next()ing
    genCheckAuthorized: function (group) {
        return function (req, res, next) {
            if (req.session === undefined || req.session.user === undefined ||
                req.session.roles === undefined)
                return next (common.genError (403, "Unauthorized"));
            if (req.session.roles[group] === undefined)
                return next (common.genError (403, "Unauthorized"));
            next ();
        }
    },

    // Login route: requires .user and .pwd params
    login: function (req, res, next) {
        var user = req.body.user || '';
        var pwd = req.body.pwd || '';

        // Helper: Return valid session Object
        function returnSession () {
            // Only export client visible parts of session object
            var copy = common.shallowCopy (req.session, clientVisibleSession);
            return res.json (copy);
        }
        // Helper: Return error
        function returnError () {
            fillSession (req, undefined, undefined, function (err) {
                next (common.genError (401, "Unauthorized"));
            });
        }

        // Check whether to just validate current session ID
        if (user === '' && pwd === '') {
            console.log ("auth revalidate: " + req.session._id);
            if (req.session.user === undefined)
                return returnError();
            return returnSession ();
        }

        // check local database, then ldap
        Users.findById (req.body.user) .exec (function (err, entry) {
            // If there is a local user AND it has a password associated, test against this, and only this
            if (entry != null && entry.pwd) {
                if (crypto.checkLocalAuth (entry, req.body.pwd)) {
                    console.info(entry.roles);
                    return fillSession (req, entry, JSON.parse(entry.roles), returnSession);
                }
                return returnError ();
            }

            // check ldap
            ldap.authorize (user.toLowerCase(), pwd, function (found) {
                //console.log ("ldap authorize " + user + " returns " + JSON.stringify (found));
                // No ldap entry either -> unauthorized
                if (found == null) {
                    return returnError ();
                }
                // If there is an entry w/o password, use it for roles etc.
                if (entry) {
                    if (! entry.name || entry.name === "")
                        entry.name = found.name;
                    if (! entry.mail || entry.mail === "")
                        entry.mail = found.mail;
                    if (! entry.type || entry.type === "")
                        entry.type = found.type;
                    if (! entry.orclgender || entry.orclgender === "")
                        entry.orclgender = found.orclgender;
                    return fillSession (req, entry, entry.roles.length > 0 ? common.arrayToHash(entry.roles) : {user:true}, returnSession);
                }

                // Otherwise create standard user entry
                saveFoundToDB(found, function() {
                    return fillSession (req, found, {user:true}, returnSession);
                });
            });
        });
    },
    logout: function (req, res, next) {
        fillSession (req, undefined, undefined, function (err) {
            // Session delete, exists further in db
            req.session.destroy(function(err) {
                if (err) {
                    console.error(err);
                }
            });
//            console.info(req.session);
            return res.json ({});
        });
    },
    init: function (_common) {
        common = _common;
        ldap.init (_common);
        Users = dbs.models.Users;
    },
};

module.exports = authorization;
