import ObjectID from "../../Types/ObjectID";
import UserMiddleware from "../Middleware/UserAuthorization";
import UserWebAuthnService, {
  Service as UserWebAuthnServiceType,
} from "../Services/UserWebAuthnService";
import {
  ExpressRequest,
  ExpressResponse,
  NextFunction,
  OneUptimeRequest,
} from "../Utils/Express";
import BaseAPI from "./BaseAPI";
import UserWebAuthn from "../../Models/DatabaseModels/UserWebAuthn";
import BadDataException from "../../Types/Exception/BadDataException";
import Response from "../Utils/Response";
import { JSONObject } from "../../Types/JSON";
import CommonAPI from "./CommonAPI";
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";

export default class UserWebAuthnAPI extends BaseAPI<
  UserWebAuthn,
  UserWebAuthnServiceType
> {
  public constructor() {
    super(UserWebAuthn, UserWebAuthnService);

    this.router.post(
      `${new this.entityType().getCrudApiPath()?.toString()}/generate-registration-options`,
      UserMiddleware.getUserMiddleware,
      async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
        try {
          const userId: ObjectID = (req as OneUptimeRequest).userAuthorization!
            .userId;

          const result: { options: any; challenge: string } =
            await UserWebAuthnService.generateRegistrationOptions({
              userId: userId,
            });

          return Response.sendJsonObjectResponse(req, res, result);
        } catch (err) {
          next(err);
        }
      },
    );

    this.router.post(
      `${new this.entityType().getCrudApiPath()?.toString()}/verify-registration`,
      UserMiddleware.getUserMiddleware,
      async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
        try {
          const data: JSONObject = req.body;

          const databaseProps: DatabaseCommonInteractionProps =
            await CommonAPI.getDatabaseCommonInteractionProps(req);

          const credential: any = data["credential"];
          const name: string = data["name"] as string;

          await UserWebAuthnService.verifyRegistration({
            credential: credential,
            name: name,
            props: databaseProps,
          });

          return Response.sendEmptySuccessResponse(req, res);
        } catch (err) {
          next(err);
        }
      },
    );

    this.router.post(
      `${new this.entityType().getCrudApiPath()?.toString()}/generate-authentication-options`,
      async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
        try {
          const data: JSONObject = req.body["data"] as JSONObject;

          if (!data) {
            throw new BadDataException("Data is required");
          }

          const email: string | undefined = data["email"] as string | undefined;

          if (!email) {
            throw new BadDataException("Email is required");
          }

          const result: { options: any; challenge: string; userId: string } =
            await UserWebAuthnService.generateAuthenticationOptions({
              email: email,
            });

          return Response.sendJsonObjectResponse(req, res, result);
        } catch (err) {
          next(err);
        }
      },
    );
  }
}
