// session.js
// Copyright (C) 2021 Digital Telepresence, LLC
// License: Apache-2.0

'use strict';

const util = require('util');

const passport = require('passport');

const { SiteError, SiteService } = require('../../lib/site-lib');

class SessionService extends SiteService {

  constructor (dtp) {
    super(dtp, module.exports);
  }

  async start ( ) {
    await super.start();
    this.log.info(`starting ${module.exports.name} service`);
    passport.serializeUser(this.serializeUser.bind(this));
    passport.deserializeUser(this.deserializeUser.bind(this));
  }

  async stop ( ) {
    this.log.info(`stopping ${module.exports.name} service`);
    await super.stop();
  }

  middleware ( ) {
    return async (req, res, next) => {
      res.locals.util = util;

      res.locals.user = req.user;
      res.locals.query = req.query;

      res.locals.config = this.dtp.config;
      if (req.session) {
        res.locals.session = req.session;
      }

      return next();
    };
  }

  authCheckMiddleware (options) {
    options = Object.assign({
      requireLogin: true,
      requireAdmin: false,
      useRedirect: true,
      loginUri: '/welcome/login',
    }, options);
    return async (req, res, next) => {
      if (options.requireLogin && !req.user) {
        if (options.useRedirect) {
          req.session.loginReturnTo = req.url;
          await this.saveSession(req);
          this.log.info('redirecting to login', { returnTo: req.url });
          return res.redirect(options.loginUri);
        }
        return next(new SiteError(403, 'Must sign in to continue'));
      }
      if (options.requireAdmin && (!req.user || !req.user.flags.isAdmin)) {
        return next(new SiteError(403, 'Administrator privileges are required'));
      }
      return next();
    };
  }

  async serializeUser (user, done) {
    if (user.coreUserId) {
      return done(null, `core:${user._id}`);
    }
    return done(null, `local:${user._id}`);
  }

  async deserializeUser (userTag, done) {
    const { coreNode: coreNodeService, user: userService } = this.dtp.services;
    const [serviceName, userId] = userTag.split(':');
    try {
      let user;
      switch (serviceName) {
        case 'core':
          user = await coreNodeService.getUserByLocalId(userId);
          user.type = 'CoreUser';
          delete user.core.oauth;
          delete user.core.__v;
          delete user.__v;
          delete user.flags._id;
          delete user.permissions._id;
          delete user.stats._id;
          delete user.optIn._id;
          break;
        case 'local':
          user = await userService.getUserAccount(userId);
          user.type = 'User';
          break;
      }
      return done(null, user);
    } catch (error) {
      this.log.error('failed to deserialize user from session', { error });
      return done(null, null);
    }
  }
}

module.exports = {
  slug: 'session',
  name: 'session',
  create: (dtp) => { return new SessionService(dtp); },
};