const fs = require('fs').promises;
const config = require('config');

const ModelPath = '../models/';
const Key = require(ModelPath + 'Key.js');
const User = require(ModelPath + 'User.js');

const wrap = require('./wrap.js');
const verifyScope = require('./verifyScope.js');
const rateLimit = require('express-rate-limit');

const checkSession = (req, scope, status) => {
    if (req.isAuthenticated()) {
        status.authenticated = true;
        if (!scope || verifyScope(req.session.passport.scope, scope)) {
            req.username = req.session.passport.user;
            req.displayname = req.session.passport.displayname;
            req.scope = req.session.passport.scope;
            req.key = null;
            status.permission = true;
        }
    }
};

const checkKey = async (req, scope, status) => {
    if (req.body.key) {
        const key = await Key.findOne({key: req.body.key});
        if (key) {
            status.authenticated = true;
            if (!scope || verifyScope(key.scope, scope)) {
                req.username = key.issuer;
                req.displayname = key.issuer;
                req.scope = key.scope;
                req.key = key.key;
                status.permission = true;
            }
        } else {
            // Log failure
            await fs.appendFile('auth.log', `${new Date().toISOString()} key ${req.ip}\n`);
        }
    }
};


const apiLimiter = config.get('RateLimit.enable')
    ? rateLimit({
        windowMs: config.get('RateLimit.api.window') * 1000,
        max: config.get('RateLimit.api.max'),
        skip: (req, res) => res.statusCode !== 401 && res.statusCode !== 403
    })
    : (req, res, next) => { next(); };
// Middleware that checks for authentication by either API key or session
// sets req.username, req.displayname, req.scope, and req.key if authenticated properly,
// otherwise throws an error code.
// If the user is banned, also throw an error.
const requireAuth = scope => (req, res, next) => {
    apiLimiter(req, res, wrap(async () => {

        const status = {
            authenticated: false,
            permission: false
        };

        // First, check the session
        checkSession(req, scope, status);
        // If not authenticated yet, check for a key
        if (!status.authenticated)
            await checkKey(req, scope, status);

        if (!status.authenticated)
            return res.status(401).json({message: 'Unauthorized.'});
        else if (!status.permission)
            return res.status(403).json({message: 'Forbidden.'});

        // Check if the user is banned
        const user = await User.findOne({username: req.username});
        if (user && user.banned)
            return res.status(403).json({message: 'Forbidden.'});

        next();
    }));
};

module.exports.checkSession = checkSession;
module.exports.checkKey = checkKey;
module.exports.requireAuth = requireAuth;
