import { Router } from "express";
import Stacker from "../../../middlewares/stacker";
import { GetUserMiddleware } from "../../../middlewares/user";
import TwoFactor, {
   TFATypes as TwoFATypes,
   IBackupCode,
} from "../../../../models/twofactor";
import RequestError, { HttpStatusCode } from "../../../../helper/request_error";
import moment = require("moment");
import { upgradeToken } from "../helper";
import * as crypto from "crypto";
import Logging from "@hibas123/nodelogging";

const BackupCodeRoute = Router();

// TODO: Further checks if this is good enough randomness
function generateCode(length: number) {
   let bytes = crypto.randomBytes(length);
   let nrs = "";
   bytes.forEach((b, idx) => {
      let nr = Math.floor((b / 255) * 9.9999);
      if (nr > 9) nr = 9;
      nrs += String(nr);
   });
   return nrs;
}

BackupCodeRoute.post(
   "/",
   Stacker(GetUserMiddleware(true, true), async (req, res) => {
      //Generating new
      let codes = Array(10).map(() => generateCode(8));
      console.log(codes);
      let twofactor = TwoFactor.new(<IBackupCode>{
         user: req.user._id,
         type: TwoFATypes.OTC,
         valid: true,
         data: codes,
         name: "",
      });
      await TwoFactor.save(twofactor);
      res.json({
         codes,
         id: twofactor._id,
      });
   })
);

BackupCodeRoute.put(
   "/",
   Stacker(
      GetUserMiddleware(true, false, undefined, false),
      async (req, res) => {
         let { login, special } = req.token;
         let { id, code }: { id: string; code: string } = req.body;

         let twofactor: IBackupCode = await TwoFactor.findById(id);

         if (
            !twofactor ||
            !twofactor.valid ||
            !twofactor.user.equals(req.user._id) ||
            twofactor.type !== TwoFATypes.OTC
         ) {
            throw new RequestError(
               "Invalid Method!",
               HttpStatusCode.BAD_REQUEST
            );
         }

         if (twofactor.expires && moment().isAfter(twofactor.expires)) {
            twofactor.valid = false;
            await TwoFactor.save(twofactor);
            throw new RequestError(
               "Invalid Method!",
               HttpStatusCode.BAD_REQUEST
            );
         }
         code = code.replace(/\s/g, "");
         let valid = twofactor.data.find((c) => c === code);

         if (valid) {
            twofactor.data = twofactor.data.filter((c) => c !== code);
            await TwoFactor.save(twofactor);
            let [login_exp, special_exp] = await Promise.all([
               upgradeToken(login),
               upgradeToken(special),
            ]);
            res.json({ success: true, login_exp, special_exp });
         } else {
            throw new RequestError(
               "Invalid or already used code!",
               HttpStatusCode.BAD_REQUEST
            );
         }
      }
   )
);

export default BackupCodeRoute;
