import { Request, Response } from "express";
import User, { IUser } from "../../models/user";
import { randomBytes } from "crypto";
import moment = require("moment");
import LoginToken from "../../models/login_token";
import promiseMiddleware from "../../helper/promiseMiddleware";
import TwoFactor, { TFATypes, TFANames } from "../../models/twofactor";
import * as crypto from "crypto";
import Logging from "@hibas123/nodelogging";

const Login = promiseMiddleware(async (req: Request, res: Response) => {
   let type = req.query.type as string;
   if (type === "username") {
      let { username, uid } = req.query as { [key: string]: string };
      let user = await User.findOne(
         username ? { username: username.toLowerCase() } : { uid: uid }
      );
      if (!user) {
         res.json({ error: req.__("User not found") });
      } else {
         res.json({ salt: user.salt, uid: user.uid });
      }
      return;
   } else if (type === "password") {
      const sendToken = async (user: IUser, tfa?: any[]) => {
         let ip =
            req.headers["x-forwarded-for"] || req.connection.remoteAddress;
         let client = {
            ip: Array.isArray(ip) ? ip[0] : ip,
            browser: req.headers["user-agent"],
         };

         let token_str = randomBytes(16).toString("hex");
         let tfa_exp = moment().add(5, "minutes").toDate();
         let token_exp = moment().add(6, "months").toDate();
         let token = LoginToken.new({
            token: token_str,
            valid: true,
            validTill: tfa ? tfa_exp : token_exp,
            user: user._id,
            validated: tfa ? false : true,
            ...client,
         });
         await LoginToken.save(token);

         let special_str = randomBytes(24).toString("hex");
         let special_exp = moment().add(30, "minutes").toDate();
         let special = LoginToken.new({
            token: special_str,
            valid: true,
            validTill: tfa ? tfa_exp : special_exp,
            special: true,
            user: user._id,
            validated: tfa ? false : true,
            ...client,
         });
         await LoginToken.save(special);

         res.json({
            login: { token: token_str, expires: token.validTill.toUTCString() },
            special: {
               token: special_str,
               expires: special.validTill.toUTCString(),
            },
            tfa,
         });
      };

      let { username, password, uid, date } = req.body;

      let user = await User.findOne(
         username ? { username: username.toLowerCase() } : { uid: uid }
      );
      if (!user) {
         res.json({ error: req.__("User not found") });
      } else {
         let upw = user.password;
         if (date) {
            if (
               !moment(date).isBetween(
                  moment().subtract(1, "minute"),
                  moment().add(1, "minute")
               )
            ) {
               res.json({
                  error: req.__(
                     "Invalid timestamp. Please check your devices time!"
                  ),
               });
               return;
            } else {
               upw = crypto
                  .createHash("sha512")
                  .update(upw + date.toString())
                  .digest("hex");
            }
         }
         if (upw !== password) {
            res.json({ error: req.__("Password or username wrong") });
         } else {
            let twofactor = await TwoFactor.find({
               user: user._id,
               valid: true,
            });
            let expired = twofactor.filter((e) =>
               e.expires ? moment().isAfter(moment(e.expires)) : false
            );
            await Promise.all(
               expired.map((e) => {
                  e.valid = false;
                  return TwoFactor.save(e);
               })
            );
            twofactor = twofactor.filter((e) => e.valid);
            if (twofactor && twofactor.length > 0) {
               let tfa = twofactor.map((e) => {
                  return {
                     id: e._id,
                     name: e.name || TFANames.get(e.type),
                     type: e.type,
                  };
               });
               await sendToken(user, tfa);
            } else {
               await sendToken(user);
            }
         }
      }
   } else {
      res.json({ error: req.__("Invalid type!") });
   }
});

export default Login;
