import express from 'express';
import mongoose from 'mongoose';
import bcrypt from 'bcryptjs';

import UserValidate from './validate/user';
import UserModel from '../models/user';
import mail from '../helpers/mail';

const router = express.Router();


router.get('/users', (req, res) => {
  if (!req.auth(res, ['admin'], 'basic')) return;

  UserModel.find({}).lean().exec(  (err, data:any) => {
    res.json(data.map(e => UserValidate.output(e)).filter(e => e !== null));  // validate all and filter null values from validation errors
  });
});

router.get('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {  // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new. See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
  req.params.username = req.params[0];
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
  let username = req.authDetails.username;
  if (req.params.username !== undefined) {
    if (!req.auth(res, ['admin'], 'basic')) return;
    username = req.params.username;
  }

  UserModel.findOne({name: username}).lean().exec(  (err, data:any) => {
    if (err) return next(err);
    if (data) {
      res.json(UserValidate.output(data));  // validate all and filter null values from validation errors
    }
    else {
      res.status(404).json({status: 'Not found'});
    }
  });
});

router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {  // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new
  req.params.username = req.params[0];
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
  let username = req.authDetails.username;
  if (req.params.username !== undefined) {
    if (!req.auth(res, ['admin'], 'basic')) return;
    username = req.params.username;
  }
  const {error, value: user} = UserValidate.input(req.body, 'change' + (req.authDetails.level === 'admin'? 'admin' : ''));
  if (error) {
    res.status(400).json({status: 'Invalid body format'});
    return;
  }

  if (user.hasOwnProperty('pass')) {
    user.pass = bcrypt.hashSync(user.pass, 10);
  }

  // check that user does not already exist if new name was specified
  if (user.hasOwnProperty('name') && user.name !== username) {
    UserModel.find({name: user.name}).lean().exec(  (err, data:any) => {
      if (err) return next(err);
      if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
        res.status(400).json({status: 'Username already taken'});
        return;
      }

      UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec(  (err, data:any) => {
        if (err) return next(err);
        if (data) {
          res.json(UserValidate.output(data));
        }
        else {
          res.status(404).json({status: 'Not found'});
        }
      });
    });
  }
  else {
    UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec(  (err, data:any) => {
      if (err) return next(err);
      if (data) {
        res.json(UserValidate.output(data));  // validate all and filter null values from validation errors
      }
      else {
        res.status(404).json({status: 'Not found'});
      }
    });
  }
});

router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {  // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new. See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
  req.params.username = req.params[0];
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
  let username = req.authDetails.username;
  if (req.params.username !== undefined) {
    if (!req.auth(res, ['admin'], 'basic')) return;
    username = req.params.username;
  }

  UserModel.findOneAndDelete({name: username}).lean().exec(  (err, data:any) => {
    if (err) return next(err);
    if (data) {
      res.json({status: 'OK'})
    }
    else {
      res.status(404).json({status: 'Not found'});
    }
  });
});

router.get('/user/key', (req, res, next) => {
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;

  UserModel.findOne({name: req.authDetails.username}).lean().exec(  (err, data:any) => {
    if (err) return next(err);
    res.json({key: data.key});
  });
});

router.post('/user/new', (req, res, next) => {
  if (!req.auth(res, ['admin'], 'basic')) return;

  // validate input
  const {error, value: user} = UserValidate.input(req.body, 'new');
  if (error) {
    res.status(400).json({status: 'Invalid body format'});
    return;
  }

  // check that user does not already exist
  UserModel.find({name: user.name}).lean().exec(  (err, data:any) => {
    if (err) return next(err);
    if (data.length > 0  || UserValidate.isSpecialName(user.name)) {
      res.status(400).json({status: 'Username already taken'});
      return;
    }

    user.key = mongoose.Types.ObjectId();  // use object id as unique API key
    bcrypt.hash(user.pass, 10, (err, hash) => {  // password hashing
      user.pass = hash;
      new UserModel(user).save((err, data) => {  // store user
        if (err) return next(err);
        res.json(UserValidate.output(data.toObject()));
      });
    });
  });
});

router.post('/user/passreset', (req, res, next) => {
  // check if user/email combo exists
  UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
    if (err) return next(err);
    if (data.length === 1) {  // it exists
      const newPass = Math.random().toString(36).substring(2);
      bcrypt.hash(newPass, 10, (err, hash) => {  // password hashing
        if (err) return next(err);
        UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}, err => {  // write new password
          if (err) return next(err);
          mail(data[0].email, 'Your new password for the DFOP database', 'Hi, <br><br> You requested to reset your password.<br>Your new password is:<br><br>' + newPass + '<br><br>If you did not request a password reset, talk to the sysadmin quickly!<br><br>Have a nice day.<br><br>The DFOP team', err => {
            if (err) return next(err);
            res.json({status: 'OK'});
          });
        });
      });
    }
    else {
      res.status(404).json({status: 'Not found'});
    }
  });
});


module.exports = router;