import { Request, Response } from "express";
import speakeasy from "speakeasy";
import QRCode from "qrcode";
import InternalException from "../exceptions/internalException";
import UserService from "../service/management/userService";
import UserFactory from "../factory/admin/management/user";
import {
  TransferUserOwnerCommand,
  UpdateUserCommand,
  UpdateUserSecretCommand,
} from "../command/management/user/userCommand";
import UserCommandHandler from "../command/management/user/userCommandHandler";
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
import SettingHelper from "../helpers/settingsHelper";
import { LoginRoutineEnum } from "../enums/loginRoutineEnum";

/**
 * @description get my by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getMeById(req: Request, res: Response): Promise<any> {
  const id = req.userId;
  let user = await UserService.getById(id);

  res.json(UserFactory.mapToSingle(user));
}

/**
 * @description get my routine by id
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getMyRoutine(req: Request, res: Response): Promise<any> {
  const id = req.userId;
  let user = await UserService.getById(id);

  res.json({
    routine: user.routine,
  });
}

/**
 * @description get my totp
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getMyTotp(req: Request, res: Response): Promise<any> {
  const userId = req.userId;

  let { secret, routine } = await UserService.getUserSecretAndRoutine(userId);

  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 verify my totp
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function verifyMyTotp(req: Request, res: Response): Promise<any> {
  const userId = req.userId;
  let totp = req.body.totp;

  let { secret, routine } = await UserService.getUserSecretAndRoutine(userId);

  if (routine != LoginRoutineEnum.totp) {
    throw new ForbiddenRequestException("only allowed for totp login");
  }

  let valid = speakeasy.totp.verify({
    secret: secret,
    encoding: "base32",
    token: totp,
    window: 2,
  });

  if (!valid) {
    throw new InternalException("Token not valid or expired");
  }
  res.sendStatus(204);
}

/**
 * @description change my password
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function changeMyPassword(req: Request, res: Response): Promise<any> {
  const userId = req.userId;
  let current = req.body.current;
  let newpassword = req.body.newpassword;

  let { secret, routine } = await UserService.getUserSecretAndRoutine(userId);

  if (routine == LoginRoutineEnum.password && current != secret) {
    throw new ForbiddenRequestException("passwords do not match");
  }

  let updateUser: UpdateUserSecretCommand = {
    id: userId,
    secret: newpassword,
    routine: LoginRoutineEnum.password,
  };
  await UserCommandHandler.updateSecret(updateUser);

  res.sendStatus(204);
}

/**
 * @description get change to totp
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function getChangeToTOTP(req: Request, res: Response): Promise<any> {
  var secret = speakeasy.generateSecret({ length: 20, name: `FF Admin ${SettingHelper.getSetting("club.name")}` });

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

/**
 * @description change to totp
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function changeToTOTP(req: Request, res: Response): Promise<any> {
  const userId = req.userId;
  let otp = req.body.otp;
  let totp = req.body.totp;

  let valid = speakeasy.totp.verify({
    secret: otp,
    encoding: "base32",
    token: totp,
    window: 2,
  });

  if (!valid) {
    throw new InternalException("Token not valid or expired");
  }

  let updateUser: UpdateUserSecretCommand = {
    id: userId,
    secret: otp,
    routine: LoginRoutineEnum.totp,
  };
  await UserCommandHandler.updateSecret(updateUser);

  res.sendStatus(204);
}

/**
 * @description change to password
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function changeToPW(req: Request, res: Response): Promise<any> {
  const userId = req.userId;
  let newpassword = req.body.newpassword;

  let updateUser: UpdateUserSecretCommand = {
    id: userId,
    secret: newpassword,
    routine: LoginRoutineEnum.password,
  };
  await UserCommandHandler.updateSecret(updateUser);

  res.sendStatus(204);
}

/**
 * @description transferOwnership
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function transferOwnership(req: Request, res: Response): Promise<any> {
  const userId = req.userId;
  let toId = req.body.toId;

  let { isOwner } = await UserService.getById(userId);
  if (!isOwner) {
    throw new ForbiddenRequestException("Action only allowed to owner.");
  }

  let transfer: TransferUserOwnerCommand = {
    toId: toId,
    fromId: userId,
  };
  await UserCommandHandler.transferOwnership(transfer);

  res.sendStatus(204);
}

/**
 * @description update my data
 * @param req {Request} Express req object
 * @param res {Response} Express res object
 * @returns {Promise<*>}
 */
export async function updateMe(req: Request, res: Response): Promise<any> {
  const id = req.userId;
  let mail = req.body.mail;
  let firstname = req.body.firstname;
  let lastname = req.body.lastname;
  let username = req.body.username;

  let updateUser: UpdateUserCommand = {
    id: id,
    mail: mail,
    firstname: firstname,
    lastname: lastname,
    username: username,
  };
  await UserCommandHandler.update(updateUser);

  res.sendStatus(204);
}