var passport = require('passport');
var LocalStrategy = require('passport-local');
var crypto = require('crypto');
const router = require('express').Router()
const {config} = require("../config.js");
const keys = require("../.Keys.js");

/* Configure password authentication strategy.
 *
 * The `LocalStrategy` authenticates users by verifying a username and password.
 * The strategy parses the username and password from the request and calls the
 * `verify` function.
 *
 * The `verify` function queries the database for the user record and verifies
 * the password by hashing the password supplied by the user and comparing it to
 * the hashed password stored in the database.  If the comparison succeeds, the
 * user is authenticated; otherwise, not.
 */
const { UserDb }= require('../services/userDb');

passport.use(new LocalStrategy({
      usernameField: 'email',
      passwordField: 'password'
    },async function verify(email, password, cb) {
  try {
    const userDb = await UserDb.init();
    const user = await userDb.getByEmail(email);
    if (!user) {
      return cb(null, false, { message: 'Incorrect username or password.' });
    }
    
    crypto.pbkdf2(password, user.salt, 310000, 32, 'sha256', async function(err, hashedPassword) {
      if (err) { return cb(err); }
      if (!crypto.timingSafeEqual(Buffer.from(user.hashed_password, 'hex'), Buffer.from(hashedPassword, 'hex'))) {
        return cb(null, false, { message: 'Incorrect username or password.' });
      }
      return cb(null, user);
    });
  } catch (err) {
    return cb(err);
  }
}));

  /* Configure session management.
 *
 * When a login session is established, information about the user will be
 * stored in the session.  This information is supplied by the `serializeUser`
 * function, which is yielding the user ID and username.
 *
 * As the user interacts with the app, subsequent requests will be authenticated
 * by verifying the session.  The same user information that was serialized at
 * session establishment will be restored when the session is authenticated by
 * the `deserializeUser` function.
 *
 * Since every request to the app needs the user ID and username, in order to
 * fetch todo records and render the user element in the navigation bar, that
 * information is stored in the session.
 */
passport.serializeUser(function(user, cb) {    
    process.nextTick(function() {
      cb(null, { id: user._id, username: user.email });
    });
  });
  
passport.deserializeUser(function(user, cb) {
  process.nextTick(function() {
    return cb(null, user);
  });
});


// JWT
var jwt = require('jsonwebtoken');
var JwtStrategy = require('passport-jwt').Strategy,
    ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.jwt;
opts.issuer = config.jwtOptions.issuer;
opts.audience = config.jwtOptions.audience;
passport.use(new JwtStrategy(opts, async function(jwt_payload, done) {
    const userDb = await UserDb.init();
    try {
        const user = await userDb.get(jwt_payload.sub);
        if (user) {
          return done(null, user);
        } else {
          return done(null, false);
          // or you could create a new account
        }
    } catch (err) {
       return done(err, false);
    }
}));

router.post('/authenticate', async function(req, res) {
    passport.authenticate('local', async function(err, user, info) {        
      if (err) { return res.status(500).json({message: err.message}); }
      if (!user) { return res.status(401).json({message: 'Incorrect email or password.'}); }

      console.log('/authenticate: '+user.username);

      // User found, generate a JWT for the user
      var token = jwt.sign({ sub: user._id, email: user.email }, opts.secretOrKey, {
        issuer: opts.issuer,
        audience: opts.audience,
        expiresIn: 86400 * 30 // 30 days
      });
      console.log('Token: '+token);
      res.json({ token: token });

      //return res.status(500).json({message: 'Incorrect email or password.'})

    })(req, res);
  });
    
  module.exports = router;