import { Request, Response } from "express";
import { JWTHelper } from "../helpers/jwtHelper";
import InternalException from "../exceptions/internalException";
import RefreshCommandHandler from "../command/refreshCommandHandler";
import { CreateRefreshCommand } from "../command/refreshCommand";
import speakeasy from "speakeasy";
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
import QRCode from "qrcode";
import { CreateResetCommand, DeleteResetCommand } from "../command/resetCommand";
import ResetCommandHandler from "../command/resetCommandHandler";
import MailHelper from "../helpers/mailHelper";
import ResetService from "../service/resetService";
import UserService from "../service/management/userService";
import { UpdateUserSecretCommand } from "../command/management/user/userCommand";
import UserCommandHandler from "../command/management/user/userCommandHandler";
import SettingHelper from "../helpers/settingsHelper";
import { LoginRoutineEnum } from "../enums/loginRoutineEnum";

/**
 * @description request totp reset
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function startReset(req: Request, res: Response): Promise<any> {
  let origin = req.headers.origin;
  let username = req.body.username;

  let { mail } = await UserService.getByUsername(username);

  var secret = speakeasy.generateSecret({ length: 20, name: `FF Admin ${SettingHelper.getSetting("club.name")}` });

  let createReset: CreateResetCommand = {
    username: username,
    mail: mail,
    secret: secret.base32,
  };
  let token = await ResetCommandHandler.create(createReset);

  // sendmail
  await MailHelper.sendMail(
    mail,
    `Email Bestätigung für Mitglieder Admin-Portal von ${SettingHelper.getSetting("club.name")}`,
    `Öffne folgenden Link: ${origin}/reset/reset?mail=${mail}&token=${token}`
  );

  res.sendStatus(204);
}

/**
 * @description verify reset link
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function verifyReset(req: Request, res: Response): Promise<any> {
  let mail = req.body.mail;
  let token = req.body.token;

  let { secret } = await ResetService.getByMailAndToken(mail, token);

  const url = `otpauth://totp/FF Admin ${SettingHelper.getSetting("club.name")}?secret=${secret}`;

  QRCode.toDataURL(url)
    .then((result) => {
      res.json({
        dataUrl: result,
        otp: secret,
      });
    })
    .catch((err) => {
      throw new InternalException("QRCode not created", err);
    });
}

/**
 * @description finishReset
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function finishReset(req: Request, res: Response): Promise<any> {
  let mail = req.body.mail;
  let routine = req.body.routine;
  let token = req.body.token;
  let passedSecret = req.body.secret;

  let { secret, username } = await ResetService.getByMailAndToken(mail, token);

  let valid = false;
  if (routine == LoginRoutineEnum.totp) {
    valid = speakeasy.totp.verify({
      secret: secret,
      encoding: "base32",
      token: passedSecret,
      window: 2,
    });
  } else {
    valid = passedSecret != "";
  }

  if (!valid) {
    throw new UnauthorizedRequestException("Credentials not valid or expired");
  }

  let { id } = await UserService.getByUsername(username);

  let updateUserSecret: UpdateUserSecretCommand = {
    id,
    secret: routine == LoginRoutineEnum.totp ? secret : passedSecret,
    routine,
  };
  await UserCommandHandler.updateSecret(updateUserSecret);

  let accessToken = await JWTHelper.buildToken(id);

  let refreshCommand: CreateRefreshCommand = {
    userId: id,
  };
  let refreshToken = await RefreshCommandHandler.create(refreshCommand);

  let deleteReset: DeleteResetCommand = {
    mail: mail,
    token: token,
  };
  await ResetCommandHandler.deleteByTokenAndMail(deleteReset);

  res.json({
    accessToken,
    refreshToken,
  });
}