const config = require('../config');
const providers = config.providers;
const helpers = require('../helpers');
const db = require('../db/models');

const passport = require('passport');
const JWTstrategy = require('passport-jwt').Strategy;
const ExtractJWT = require('passport-jwt').ExtractJwt;
const GoogleStrategy = require('passport-google-oauth2').Strategy;
const MicrosoftStrategy = require('passport-microsoft').Strategy;
const UsersDBApi = require('../db/api/users');
const { sessionTokenFromRequest } = require('../security/cookieAuth');
const { isTokenRevoked } = require('../security/tokenRevocation');


passport.use(new JWTstrategy({
  passReqToCallback: true,
  secretOrKey: config.secret_key,
  jwtFromRequest: ExtractJWT.fromExtractors([
    ExtractJWT.fromAuthHeaderAsBearerToken(),
    sessionTokenFromRequest,
  ])
}, async (req, token, done) => {
  try {
    if (isTokenRevoked(token)) {
      return done(null, false, { message: 'Session has been revoked' });
    }

    const user = await UsersDBApi.findBy( {email: token.user.email});

    if (user && user.disabled) {
      return done(null, false, { message: 'User is disabled' });
    }

    if (!user) {
      return done(null, false, { message: 'User does not exist' });
    }

    if (user.sessionInvalidatedAt && token.iat * 1000 <= new Date(user.sessionInvalidatedAt).getTime()) {
      return done(null, false, { message: 'Session has been invalidated' });
    }

    req.currentUser = user;

    return done(null, user);
  } catch (error) {
    done(error);
  }
}));

if (config.google.clientId && config.google.clientSecret) {
  passport.use(new GoogleStrategy({
    clientID: config.google.clientId,
    clientSecret: config.google.clientSecret,
    callbackURL: config.apiUrl + '/auth/signin/google/callback',
    passReqToCallback: true
  },
  function (request, accessToken, refreshToken, profile, done) {
    socialStrategy(profile.email, profile, providers.GOOGLE, done);
  }
  ));
}


if (config.microsoft.clientId && config.microsoft.clientSecret) {
  passport.use(new MicrosoftStrategy({
    clientID: config.microsoft.clientId,
    clientSecret: config.microsoft.clientSecret,
    callbackURL: config.apiUrl + '/auth/signin/microsoft/callback',
    passReqToCallback: true
  },
  function (request, accessToken, refreshToken, profile, done) {
    const email = profile._json.mail || profile._json.userPrincipalName;
    socialStrategy(email, profile, providers.MICROSOFT, done);
  }
  ));
}

function socialStrategy(email, profile, provider, done) {
  db.users.findOrCreate({where: {email, provider}}).then(([user]) => {
    const body = {
      id: user.id,
      email: user.email,
      name: profile.displayName,
    };
    const token = helpers.jwtSign({user: body});
    return done(null, {token});
  });
}
