const utils = require("@strapi/utils");

const _ = require("lodash");
const { sanitize } = utils;
const { ApplicationError, ValidationError } = utils.errors;
const {
  validateRegisterBody,
} = require("@strapi/plugin-users-permissions/server/controllers/validation/auth");
const { getService } = require("@strapi/plugin-users-permissions/server/utils");

const sanitizeUser = (user, ctx) => {
  const { auth } = ctx.state;
  const userSchema = strapi.getModel("plugin::users-permissions.user");

  return sanitize.contentAPI.output(user, userSchema, { auth });
};

const userPermissionExtension = (plugin) => {
  /** Example of overriding and adding a new endpoint, check the section where we have registered this as a route below. */
  plugin.controllers.user.updateMe = (ctx) => {
    ctx.params.id = ctx.state.user.id;
    return plugin.controllers.user.update(ctx);
  };

  plugin.controllers.user.startEndUserOtpLogin = async (ctx) => {
    const { emailAddress, mobileNumber } = ctx.request.body;
    if (!emailAddress || !mobileNumber) {
      throw new ValidationError(
        "Please specify both the email address & mobile numbers."
      );
    }

    const pluginStore = await strapi.store({
      type: "plugin",
      name: "users-permissions",
    });
    const emailSettings = await pluginStore.get({ key: "email" });

    // Find the channel partner first.
    // const endUser = await strapi.query("api::end-user.end-user").findOne({
    //   populate: ["user"],
    //   where: {
    //     $and: [
    //       { publishedAt: { $notNull: true } },
    //       { mobileNo: mobileNumber },
    //     ],
    //   },
    // });

    const endUser = await strapi.query("api::end-user.end-user").findOne({
      populate: ["user"],
      where: {
        $and: [{ publishedAt: { $notNull: true } }, { mobileNo: mobileNumber }],
      },
    });

    if (!endUser) {
      throw new ValidationError(
        "No end user registered with specified email address, mobile number combination."
      );
    }

    // Find the linked user next.
    const user = await strapi
      .query("plugin::users-permissions.user")
      .findOne({ where: { id: endUser.user.id } });
    if (!user || user.blocked) {
      throw new ValidationError("Unable to resolve user linked to end user.");
    }

    const resetPasswordSettings = _.get(
      emailSettings,
      "reset_password.options",
      {}
    );
    const oneTimePassword = Math.floor(100000 + Math.random() * 900000);

    const emailToSend = {
      to: user.email,
      from: `contact@hiranandani.net`,
      oneTimePassword: oneTimePassword,
      replyTo: resetPasswordSettings.response_email,
      subject: `Your one time password is: ${oneTimePassword}`,
      text: `Hello ${endUser.fullName}, Your one time password to login to your partner portal is ${oneTimePassword}`,
      html: `<p>Dear ${endUser.fullName},
        Your OTP for Hiranandani Exclusive website login is <strong>${oneTimePassword}</strong> . Valid for 10 minutes. Please do not
        share this OTP.
        
        Regards,
        Hiranandani Team.`,
    };
    const finalData = {
      emailToSend: emailToSend,
      mobileNo: mobileNumber,
      fullName: endUser.fullName,
    };
    // NOTE: Update the user before sending the email so an Admin can generate the link if the email fails

    // const headers = { "Content-Type": "application/json" };
    // const otpDetails = {
    //   api_key: process.env.SPERTO_API_KEY,
    //   from_name: "Hiranandani",
    //   from_mail: emailToSend.from,
    //   to: emailToSend.to,
    //   subject: emailToSend.subject,
    //   body: emailToSend.html,
    // };
    // await strapi.plugin("email").service("email").send(emailToSend);
    // await axios.get(
    //   `http://vas.themultimedia.in/domestic/sendsms/bulksms.php?username=OSAPI&password=os123456&type=TEXT&sender=HROTPs&entityId=1101407690000029629&templateId=1507166789848358346&mobile=${mobileNumber}&message=Dear%20${endUser.fullName}%0AYour%20OTP%20for%20Hiranandani%20Exclusive%20website%20login%20is%20${oneTimePassword}%0AValid%20for%2010%20minute%20Please%20do%20not%20share%20this%20OTP.%0ARegards%2C%0AHiranandani%20Team.`
    // );
    try {
      // const spertoResponse = await axios.post(
      //   "https://net4hgc.sperto.co.in/_api/api_auth_send_mail.php",
      //   otpDetails,
      //   { headers: headers }
      // );
      const spretoOTP = await strapi
        .service("api::end-user.end-user")
        .sendOTPToSpreto(finalData);
      // console.log("spretoOTP", spretoOTP);
      //  EMAIL RESPONSE
      ctx.request.body.httpRequestEmailHeaders = JSON.stringify(
        spretoOTP.spertoEmailResponse.headers
      );
      ctx.request.body.httpRequestEmailMethod =
        spretoOTP.spertoEmailResponse.config.method;
      ctx.request.body.httpRequestEmailUrl =
        spretoOTP.spertoEmailResponse.config.url;
      ctx.request.body.httpsRequestEmailBody =
        spretoOTP.spertoEmailResponse.config.data;
      ctx.request.body.httpResposneEmailBody = JSON.stringify(
        spretoOTP.spertoEmailResponse.data
      );

      // SMS RESPONSE

      ctx.request.body.httpSMSRequestHeaders = JSON.stringify(
        spretoOTP.spertoSMSResponse.headers
      );
      ctx.request.body.httpSMSRequestMethod =
        spretoOTP.spertoSMSResponse.config.method;
      ctx.request.body.httpSMSRequestUrl =
        spretoOTP.spertoSMSResponse.config.url;
      // ctx.request.body.httpsSMSRequestBody = spretoOTP.spertoSMSResponse.config.data;
      ctx.request.body.httpSMSResposneBody = JSON.stringify(
        spretoOTP.spertoSMSResponse.data
      );

      ctx.request.body.thirdPartyApiError = false;
      // console.log("spretoOTP", spretoOTP);

      // return spretoOTP;
    } catch (error) {
      // Email errors
      ctx.request.body.httpRequestEmailHeaders = JSON.stringify(
        error.config.headers
      );
      ctx.request.body.httpRequestEmailMethod = error.config.method;
      ctx.request.body.httpRequestEmailUrl = error.config.url;
      ctx.request.body.httpsRequestEmailBody = error.config.data;
      ctx.request.body.httpResposneEmailBody = JSON.stringify(error.message);

      // SMS headers
      ctx.request.body.httpSMSRequestHeaders = JSON.stringify(
        error.config.headers
      );
      ctx.request.body.httpSMSRequestMethod = error.config.method;
      ctx.request.body.httpSMSRequestUrl = error.config.url;
      // ctx.request.body.httpsSMSRequestBody = error.config.data;
      ctx.request.body.httpSMSResposneBody = JSON.stringify(error.message);
      ctx.request.body.thirdPartyApiError = true;
    }
    const updateUser = await getService("user").edit(user.id, {
      oneTimePassword: `${oneTimePassword}`,
    });

    await strapi.entityService.update("api::end-user.end-user", endUser.id, {
      data: {
        httpRequestIsVerifiedHeaders:
          ctx.request.body.httpRequestIsVerifiedHeaders,
        httpsRequestIsVerifiedBody: ctx.request.body.httpsRequestIsVerifiedBody,
        httpRequestIsVerifiedUrl: ctx.request.body.httpRequestIsVerifiedUrl,
        httpRequestIsVerifiedMethod:
          ctx.request.body.httpRequestIsVerifiedMethod,
        httpResposneIsVerifiedBody: ctx.request.body.httpResposneIsVerifiedBody,

        httpRequestIsVerifiedHeaders:
          ctx.request.body.httpRequestIsVerifiedHeaders,
        // httpsRequestIsVerifiedBody: ctx.request.body.httpsRequestIsVerifiedBody,
        httpSMSRequestUrl: ctx.request.body.httpSMSRequestUrl,
        httpSMSRequestMethod: ctx.request.body.httpSMSRequestMethod,
        httpSMSResposneBody: ctx.request.body.httpSMSResposneBody,
        httpSMSRequestHeaders: ctx.request.body.httpSMSRequestHeaders,
      },
    });

    ctx.send({ ok: true, message: "otp sent" });

    //  const updateUser = await getService("user").edit(user.id, {
    //   oneTimePassword: `${oneTimePassword}`,
    // });

    // Send an email to the user.
    // await getService("user").sendOTPOnEmail(emailToSend);

    // TODO: Send SMS.
  };

  plugin.controllers.user.finishEndUserOtpLogin = async (ctx) => {
    const { oneTimePassword, emailAddress, mobileNumber } = ctx.request.body;
    console.log("ctx.request.body", ctx.request.body);
    if (!oneTimePassword || !mobileNumber || !emailAddress) {
      throw new ValidationError(
        "Please specify the oneTimePassword, email address and mobile numbers."
      );
    }

    // Find the channel partner first.
    const endUser = await strapi.query("api::end-user.end-user").findOne({
      populate: ["user"],
      where: {
        $and: [
          { publishedAt: { $notNull: true } },
          // { user: { email: emailAddress } },
          { mobileNo: mobileNumber },
        ],
      },
    });

    if (!endUser) {
      throw new ValidationError(
        "No end user registered with specified email address, mobile number combination."
      );
    }

    // Find the linked user next.
    const user = await strapi.query("plugin::users-permissions.user").findOne({
      where: {
        $and: [{ id: endUser.user.id }, { oneTimePassword: oneTimePassword }],
      },
    });
    if (!user || user.blocked) {
      throw new ValidationError("Code provided is not valid.");
    }

    await getService("user").edit(user.id, {
      oneTimePassword: null,
      password: oneTimePassword,
    });

    ctx.send({ ok: true, message: "otp updated" });
  };

  plugin.controllers.user.startChannelPartnerOtpLogin = async (ctx) => {
    const { mahareraNumber, mobileNumber } = ctx.request.body;
    if (!mahareraNumber) {
      throw new ValidationError("Please specify the maharera number.");
    }

    // const pluginStore = await strapi.store({
    //   type: "plugin",
    //   name: "users-permissions",
    // });
    // const emailSettings = await pluginStore.get({ key: "email" });

    // // Find the channel partner first.
    // const channelPartner = await strapi
    //   .query("api::channel-partner.channel-partner")
    //   .findOne({
    //     populate: ["user"],
    //     where: {
    //       reraNumber: mahareraNumber,
    //     },
    //   });

    // if (!channelPartner) {
    //   throw new ValidationError(
    //     "No channel partner registered with specified maharera number."
    //   );
    // }

    // // // Find the linked user next.
    // const user = await strapi
    //   .query("plugin::users-permissions.user")
    //   .findOne({ where: { id: channelPartner.user.id } });

    // if (!user || user.blocked) {
    //   throw new ValidationError(
    //     "Unable to resolve user linked to channel partner."
    //   );
    // }

    // const resetPasswordSettings = _.get(
    //   emailSettings,
    //   "reset_password.options",
    //   {}
    // );

    let oneTimePassword;
    let responseBody;

    try {
      // console.log("entered catch",ctx.request.body);
      const spertoCPOtpData = await strapi
        .service("api::channel-partner.channel-partner")
        .getCPDataFromSperto(ctx.request.body);
      responseBody = spertoCPOtpData;
      // console.log("spertoCPOtpData >>>>>", spertoCPOtpData);
      ctx.request.body.httpThirdPartyOTPApiRequestHeaders = JSON.stringify(
        spertoCPOtpData.headers
      );
      ctx.request.body.token = spertoCPOtpData.data.token;
      ctx.request.body.httpThirdPartyOTPApiRequestMethod =
        spertoCPOtpData.config.method;
      ctx.request.body.httpThirdPartyOTPApiRequestUrl =
        spertoCPOtpData.config.url;
      ctx.request.body.httpsThirdPartyOTPApiRequestBody =
        spertoCPOtpData.config.data;
      ctx.request.body.httpThirdPartyOTPApiResposneBody = JSON.stringify(
        spertoCPOtpData.data
      );
      ctx.request.body.httpThirdPartyOTPApiError = false;
      // console.log(">>>>>",ctx.request.body.httpThirdPartyOTPApiError);
      oneTimePassword = spertoCPOtpData.data.otp;
    } catch (error) {
      ctx.request.body.data.httpThirdPartyOTPApiRequestHeaders = JSON.stringify(
        error.config.headers
      );
      ctx.request.body.httpThirdPartyOTPApiRequestMethod = error.config.method;
      ctx.request.body.httpThirdPartyOTPApiRequestUrl = error.config.url;
      ctx.request.body.httpsThirdPartyOTPApiRequestBody = error.config.data;
      ctx.request.body.httpThirdPartyOTPApiResposneBody = JSON.stringify(
        error.message
      );
      ctx.request.body.httpThirdPartyOTPApiError = true;
    }
    const emailToSend = {
      oneTimePassword: oneTimePassword,
      // to: user.email,
      from: `contact@hiranandani.net`,
      // replyTo: resetPasswordSettings.response_email,
      subject: `Your one time password is: ${oneTimePassword}`,
      // text: `Hello ${channelPartner.contactPersonName}, Your one time password to login to your partner portal is ${oneTimePassword}`,
      // html: `<p>Hello ${channelPartner.contactPersonName}, <br></br>Your one time password to login to your partner portal is ${oneTimePassword}</p><br /> Best Regards, <br /> Team Hiranandani.`,
    };
    const finalData = {
      emailToSend: emailToSend,
      mobileNo: mobileNumber,
      fullName: '',
    };
    try {
      // const spertoResponse = await axios.post(
      //   "https://net4hgc.sperto.co.in/_api/api_auth_send_mail.php",
      //   otpDetails,
      //   { headers: headers }
      // );
      const spretoOTP = await strapi
        .service("api::end-user.end-user")
        .sendOTPToSpreto(finalData);
      // console.log("spretoOTP", spretoOTP);
      //  EMAIL RESPONSE
      ctx.request.body.httpEmailRequestHeaders = JSON.stringify(
        spretoOTP.spertoEmailResponse.headers
      );
      ctx.request.body.httpEmailRequestMethod =
        spretoOTP.spertoEmailResponse.config.method;
      ctx.request.body.httpEmailRequestUrl =
        spretoOTP.spertoEmailResponse.config.url;
      ctx.request.body.httpsEmailRequestBody =
        spretoOTP.spertoEmailResponse.config.data;
      ctx.request.body.httpEmailResposneBody = JSON.stringify(
        spretoOTP.spertoEmailResponse.data
      );

      // SMS RESPONSE

      ctx.request.body.httpSMSRequestHeaders = JSON.stringify(
        spretoOTP.spertoSMSResponse.headers
      );
      ctx.request.body.httpSMSRequestMethod =
        spretoOTP.spertoSMSResponse.config.method;
      ctx.request.body.httpSMSRequestUrl =
        spretoOTP.spertoSMSResponse.config.url;
      // ctx.request.body.httpsSMSRequestBody = spretoOTP.spertoSMSResponse.config.data;
      ctx.request.body.httpSMSResposneBody = JSON.stringify(
        spretoOTP.spertoSMSResponse.data
      );

      ctx.request.body.thirdPartyApiError = false;
      // console.log("spretoOTP", spretoOTP);

      // return spretoOTP;
    } catch (error) {
      // Email errors
      ctx.request.body.httpEmailRequestHeaders = JSON.stringify(
        error.config.headers
      );
      ctx.request.body.httpEmailRequestMethod = error.config.method;
      ctx.request.body.httpEmailRequestUrl = error.config.url;
      ctx.request.body.httpsEmailRequestBody = error.config.data;
      ctx.request.body.httpEmailResposneBody = JSON.stringify(error.message);

      // SMS headers
      ctx.request.body.httpSMSRequestHeaders = JSON.stringify(
        error.config.headers
      );
      ctx.request.body.httpSMSRequestMethod = error.config.method;
      ctx.request.body.httpSMSRequestUrl = error.config.url;
      // ctx.request.body.httpsSMSRequestBody = error.config.data;
      ctx.request.body.httpSMSResposneBody = JSON.stringify(error.message);
      ctx.request.body.thirdPartyApiError = true;
    }
    // NOTE: Update the user before sending the email so an Admin can generate the link if the email fails
    // await getService("user").edit(user.id, {
    //   oneTimePassword: `${oneTimePassword}`,
    // });
    // await strapi.entityService.update(
    //   "api::channel-partner.channel-partner",
    //   channelPartner.id,
    //   {
    //     data: {
    //       // httpRequestIsVerifiedHeaders:
    //       //   ctx.request.body.httpRequestIsVerifiedHeaders,
    //       // httpsRequestIsVerifiedBody: ctx.request.body.httpsRequestIsVerifiedBody,
    //       // httpRequestIsVerifiedUrl: ctx.request.body.httpRequestIsVerifiedUrl,
    //       // httpRequestIsVerifiedMethod:
    //       //   ctx.request.body.httpRequestIsVerifiedMethod,
    //       // httpResposneIsVerifiedBody: ctx.request.body.httpResposneIsVerifiedBody,

    //       // httpRequestIsVerifiedHeaders:
    //       //   ctx.request.body.httpRequestIsVerifiedHeaders,
    //       // httpsRequestIsVerifiedBody: ctx.request.body.httpsRequestIsVerifiedBody,
    //       httpSMSRequestUrl: ctx.request.body.httpSMSRequestUrl,
    //       httpSMSRequestMethod: ctx.request.body.httpSMSRequestMethod,
    //       httpSMSResposneBody: ctx.request.body.httpSMSResposneBody,
    //       httpSMSRequestHeaders: ctx.request.body.httpSMSRequestHeaders,

    //       httpsEmailRequestBody: ctx.request.body.httpsEmailRequestBody,
    //       httpEmailRequestUrl: ctx.request.body.httpEmailRequestUrl,
    //       httpEmailRequestMethod: ctx.request.body.httpEmailRequestMethod,
    //       httpEmailResposneBody: ctx.request.body.httpEmailResposneBody,
    //       httpEmailRequestHeaders: ctx.request.body.httpEmailRequestHeaders,

    //       httpThirdPartyOTPApiRequestHeaders:
    //         ctx.request.body.httpThirdPartyOTPApiRequestHeaders,
    //       token: ctx.request.body.token,
    //       httpThirdPartyOTPApiRequestMethod:
    //         ctx.request.body.httpThirdPartyOTPApiRequestMethod,
    //       httpThirdPartyOTPApiRequestUrl:
    //         ctx.request.body.httpThirdPartyOTPApiRequestUrl,
    //       httpsThirdPartyOTPApiRequestBody:
    //         ctx.request.body.httpsThirdPartyOTPApiRequestBody,
    //       httpThirdPartyOTPApiResposneBody:
    //         ctx.request.body.httpThirdPartyOTPApiResposneBody,
    //     },
    //   }
    // );
    // Send an email to the user.
    // await strapi.plugin("email").service("email").send(emailToSend);

    // TODO: Send SMS.
    console.log("responseBody", responseBody);
    ctx.send({ data: responseBody.data });
  };

  plugin.controllers.user.finishChannelPartnerOtpLogin = async (ctx) => {
    const { oneTimePassword, mahareraNumber, mobileNumber } = ctx.request.body;
    if (!oneTimePassword || !mahareraNumber) {
      throw new ValidationError(
        "Please specify the oneTimePassword, maharera number and mobile numbers."
      );
    }
    console.log("{ oneTimePassword, mahareraNumber, mobileNumber }", {
      oneTimePassword,
      mahareraNumber,
    });
    // Find the channel partner first.
    const channelPartner = await strapi
      .query("api::channel-partner.channel-partner")
      .findOne({
        populate: ["user"],
        where: {
          $and: [
            { publishedAt: { $notNull: true } },
            { reraNumber: mahareraNumber },
          ],
        },
      });

    if (!channelPartner) {
      throw new ValidationError(
        "No channel partner registered with specified maharera number, mobile number combination."
      );
    }

    // Find the linked user next.
    const user = await strapi.query("plugin::users-permissions.user").findOne({
      where: {
        $and: [
          { id: channelPartner.user.id },
          { oneTimePassword: oneTimePassword },
        ],
      },
    });
    if (!user || user.blocked) {
      throw new ValidationError("Code provided is not valid.");
    }

    await getService("user").edit(user.id, {
      oneTimePassword: null,
      password: oneTimePassword,
    });
    console.log("ctx >>>>>>>>>", user);
    ctx.send({ ok: true, message: "otp updated", data: user });
  };

  /** Example of overriding an existing route. */
  plugin.controllers.auth.register = async (ctx) => {
    const pluginStore = await strapi.store({
      type: "plugin",
      name: "users-permissions",
    });

    const settings = await pluginStore.get({ key: "advanced" });

    if (!settings.allow_register) {
      throw new ApplicationError("Register action is currently disabled");
    }

    const params = {
      ..._.omit(ctx.request.body, [
        "confirmed",
        "blocked",
        "confirmationToken",
        "resetPasswordToken",
        "provider",
      ]),
      provider: "local",
    };

    await validateRegisterBody(params);

    // We have added the ability to choose the role.
    // This is the customisation that we wanted to do to make this possible
    const newUserRole = params?.role ? params?.role : settings.default_role;

    // the query was also changed to apply a query on "name" rather than the default "type".
    const role = await strapi
      .query("plugin::users-permissions.role")
      .findOne({ where: { name: newUserRole } });

    if (!role) {
      throw new ApplicationError("Impossible to find the default role");
    }
    console.log("HERE ARE PARAMS >>>>>", params);
    // @ts-ignore
    const { email, username, provider } = params;

    const identifierFilter = {
      $or: [
        // { email: email.toLowerCase() },
        // { username: email.toLowerCase() },
        { username },
        // { email: username },
      ],
    };

    const conflictingUserCount = await strapi
      .query("plugin::users-permissions.user")
      .count({
        where: { ...identifierFilter, provider },
      });
      
    if (conflictingUserCount > 0) {
      throw new ApplicationError("RERA Number already in use.");
    }

    // if (settings.unique_email) {
    //   const conflictingUserCount = await strapi
    //     .query("plugin::users-permissions.user")
    //     .count({
    //       where: { ...identifierFilter },
    //     });

    //   if (conflictingUserCount > 0) {
    //     throw new ApplicationError("Email or Username are already taken");
    //   }
    // }

    let newUser = {
      ...params,
      role: role.id,
      email: email.toLowerCase(),
      username,
      confirmed: !settings.email_confirmation,
    };
    console.log("newUser 123456789>>>>>", newUser);
    const user = await strapi
      .plugin("users-permissions")
      .service("user")
      .add(newUser);

    const sanitizedUser = await sanitizeUser(user, ctx);
    // console.log("SANITIZE sanitizedUser", sanitizedUser);

    // if (settings.email_confirmation) {
    //   try {
    //     await strapi
    //       .plugin("users-permissions")
    //       .service("user")
    //       .sendConfirmationEmail(sanitizedUser);
    //   } catch (err) {
    //     throw new ApplicationError(err.message);
    //   }
    //   return ctx.send({ user: sanitizedUser });
    // }

    const jwt = strapi
      .plugin("users-permissions")
      .service("jwt")
      .issue(_.pick(user, ["id"]));
    console.log("jwt");
    return ctx.send({
      jwt,
      user: sanitizedUser,
    });
  };

  /** Endpoint used to allow edits on a user done by currently logged in user only their own record. */
  plugin.routes["content-api"].routes.push({
    method: "PUT",
    path: "/users/me",
    handler: "user.updateMe",
  });

  /** Endpoints used to facilitate channel partner login with otp */
  plugin.routes["content-api"].routes.push({
    method: "POST",
    path: "/users/channel-partner/start-otp-login",
    handler: "user.startChannelPartnerOtpLogin",
  });
  plugin.routes["content-api"].routes.push({
    method: "POST",
    path: "/users/channel-partner/finish-otp-login",
    handler: "user.finishChannelPartnerOtpLogin",
  });

  /** Endpoints used to facilitate end user login with otp */
  plugin.routes["content-api"].routes.push({
    method: "POST",
    path: "/users/end-user/start-otp-login",
    handler: "user.startEndUserOtpLogin",
  });
  plugin.routes["content-api"].routes.push({
    method: "POST",
    path: "/users/end-user/finish-otp-login",
    handler: "user.finishEndUserOtpLogin",
  });

  return plugin;
};

module.exports = userPermissionExtension;