class AccessManager {

    name = 'am'
    init(app){
        ['user', 'editor', 'admin'].forEach(f => {
            this[f] = this.oneOfThese([f]);
        });
    }

    start(app){

    }

    getIp(req){
        return req.headers['x-forwarded-for'] || req.socket.remoteAddress;
    }

    async audit(req, action, objectId, custom){
        let data = {
            t: Math.floor(Date.now() / 1000),
            s: req.session?.id,
            i: this.getIp(req),
            r: req.headers.referer,
            ua: req.headers['user-agent'],
            l: req?.lang?.code,
            u: req.user?._id && this.app.db.ObjectId(req.user._id),
            a: action,
            o: objectId,
            c: custom
        }
        await this.app.db.create('log', data);
    }

    async addToHistory(id, collection, action){
        let o;
        if (typeof id == 'string') o = await this.app.db.get(collection, {'_id': this.app.db.ObjectId(id)});
        else if (typeof id == 'number') o = await this.app.db.get(collection, {id});
        else o = id;
        o._oid = this.app.db.ObjectId(o._id);
        o._from = collection;
        o._action = action;
        await this.app.db.create('history', o);
        return o;
    }

    async getObjectHistory(collection, match){
        let object = await this.app.db.aggregate( collection, [
            { $match: match },
            { $lookup: {from: 'users', foreignField: '_id', localField: '_meta.user', as: '_user'} },
            { $project: {action:'$_meta.action', time:'$_meta.time', user:"$_user.email", type:'current'} },
            { $unwind: {path:'$user', preserveNullAndEmptyArrays:true}}
          ]);
        let history = await this.app.db.aggregate('history',[
            { $match: {_oid:object._id, _from:collection}},
            { $lookup: {from: 'users', foreignField: '_id', localField: '_meta.user', as: '_user'} },
            { $project: {action:'$_meta.action', time:'$_meta.time', user:"$_user.email", type:'history'} },
            { $sort: { time:-1 }},
            { $unwind: {path:'$user', preserveNullAndEmptyArrays:true}}
        ]);
        return {object, history};
    }

    setMeta(m, action, user, time, custom){
        delete m.revert;
        m.user = user && user._id && this.app.db.ObjectId(user._id);
        m.creator = m.creator || m.user;
        m.time = time || Math.floor(Date.now() / 1000);
        m.ctime = m.ctime || m.time;
        m.action = action;
        custom && Object.assign(m, custom);
    }

    async processSocialLogin(p1, p2, profile, done){
        //console.log(p1, p2, profile);
        let externalProfile = {
          loginProvider: profile.provider,
          providerKey: profile.id
        };
        let user;
        let dbUser = await this.app.db.get(collection, {social: externalProfile});
        if (dbUser){
            user = this.getUserProfile(dbUser);
        }else{
            let dbUser = {
                email: (profile.emails && profile.emails[0].value) || ((profile.username || profile.id) + "@" + profile.provider),
                firstName: (profile.name && profile.name.givenName) || profile.displayName,
                lastName: profile.name && profile.name.familyName,
                displayName: profile.displayName || (profile.name && profile.name.givenName),
                profilePicture: 
                    (profile.photos && profile.photos[0] && profile.photos[0].value) 
                    || (profile._json.data && profile._json.data.profile_picture),
                status: 1,
                social:[
                    externalProfile
                ],
                roles:['user']
            };
            let r = await this.app.db.create(collection, dbUser);
            user = this.getUserProfile(dbUser);
            user._id = r.insertedId;
        }
        done(null, user);
    }

    getUserProfile(dbUser){
        return {
            _id: dbUser._id,
            email: dbUser.email,
            roles: dbUser.roles || ['user'],
            groups: dbUser.groups || [],
            firstName: dbUser.firstName,
            lastName: dbUser.lastName,
            displayName: dbUser.displayName,
            profilePicture: dbUser.profilePicture,
            status: dbUser.status,
            gameData: dbUser.gameData
        }
    }

    getSocialCallback(provider){
        const am = this;
        return function(req, res, next) {
            passport.authenticate(provider, function(err, user, info) {
              if (err) { return next(err); }
              let lang = req.cookies.lang || am.app.config.langs[0].code;
              if (!user) { return res.redirect(`/${lang}/user/signin`); }
              req.logIn(user, function(err) {
                if (err) { return next(err); }
                return res.redirect(`/${lang}`);
              });
            })(req, res, next);
        }
    }

    is(user, role){
        return user && user.roles.indexOf(role) > -1;
    }

    oneOfThese(roles){
        return function(req, res, next){
            let result = false;
            roles.forEach(r=>{
                if (req.user && (req.user.roles?.indexOf(r)>-1 || req.user.groups?.filter(g=>g.startsWith(r+'.')).length)
                    || r == 'guest' ){
                    result = true;
                }
            });
            if (result) next();
            else res.status(401).json({status:'error', error:'noPermissions'}).end();
        }
    }

}

export { AccessManager }