# AUTOGENERATED! DO NOT EDIT! File to edit: notebooks/API_User.ipynb (unless otherwise specified).

__all__ = ["User"]

# Cell

from typing import *

# Internal Cell

import os

import pandas as pd
from fastcore.foundation import patch
import qrcode
from qrcode.image.pil import PilImage

from ..logger import get_logger, set_level
from ..helper import (
    get_data,
    post_data,
    delete_data,
    generate_df,
    get_attributes_from_instances,
    check_and_append_otp_query_param,
    standardize_phone_number,
)

from .client import Client

# Internal Cell

logger = get_logger(__name__)

# Cell


class User:
    """A class for managing users in the server.

    !!! info

        The `create`, `enable`, `disable` and `ls` method access is restricted only to the super users.
    """

    USER_COLS = [
        "uuid",
        "username",
        "email",
        "super_user",
        "is_mfa_active",
        "disabled",
        "created",
        "subscription_type",
        "first_name",
        "last_name",
        "phone_number",
        "is_phone_number_verified",
    ]

    def __init__(
        self,
        uuid: str,
        username: Optional[str] = None,
        first_name: Optional[str] = None,
        last_name: Optional[str] = None,
        email: Optional[str] = None,
        subscription_type: Optional[str] = None,
        super_user: Optional[bool] = None,
        disabled: Optional[str] = None,
        created: Optional[str] = None,
        is_mfa_active: Optional[bool] = None,
        phone_number: Optional[str] = None,
        is_phone_number_verified: Optional[bool] = None,
    ):
        """Constructs a new User instance.

        Args:
            uuid: User uuid.
            username: The username of the user.
            first_name: The first name of the user.
            last_name: The last name of the user.
            email: The email address of the user.
            subscription_type: User subscription type. Currently, the API supports only the following subscription
                types **small**, **medium** and **large**.
            super_user: Flag to indicate the user type.
            disabled: Flag to indicate the status of the user.
            created: User creation date.
            is_mfa_active: Flag to indicate the user's MFA status.
            phone_number: The registered phone number of the user. The phone number should follow the pattern of country
                code followed by your phone number. For example, **440123456789, +440123456789, 00440123456789, +44 0123456789,
                and (+44) 012 345 6789** are all valid formats for registering a UK phone number.
            is_phone_number_verified: Flag to indicate the phone number registration status.
        """

        self.uuid = uuid
        self.username = username
        self.first_name = first_name
        self.last_name = last_name
        self.email = email
        self.subscription_type = subscription_type
        self.super_user = super_user
        self.disabled = disabled
        self.created = created
        self.is_mfa_active = is_mfa_active
        self.phone_number = phone_number
        self.is_phone_number_verified = is_phone_number_verified

    @staticmethod
    def create(
        *,
        username: str,
        first_name: str,
        last_name: str,
        email: str,
        password: str,
        subscription_type: str,
        super_user: bool = False,
        phone_number: Optional[str] = None,
        otp: Optional[str] = None,
    ) -> pd.DataFrame:
        """Create a new user in the server.

        Args:
            username: The username for the new user.
            first_name: The first name for the new user.
            last_name: The last name for the new user.
            email: The email for the new user.
            password: The password for the new user.
            subscription_type: User subscription type. Currently, the API supports only the following subscription
                types **small**, **medium** and **large**.
            super_user: If set to **True**, then the new user will have super user privilages.
                If **None**, then the default value **False** will be used to create a non-super user.
            phone_number: Phone number to be added to the user account. The phone number should follow the pattern of the country
                code followed by your phone number. For example, **440123456789, +440123456789, 00440123456789, +44 0123456789,
                and (+44) 012 345 6789** are all valid formats for registering a UK phone number.
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
                A pandas DataFrame encapsulating the details of the newly created user.

        Raises:
            ConnectionError: If the server address is invalid or not reachable.
            ValueError: If the OTP is invalid.
            ValueError: If the username or email is already present in the server.

        An example to create a new user:
        ```python

        # Importing necessary libraries
        from ..client import Client, User

        # Authenticate
        Client.get_token(username="{fill in here}", password="{fill in here}")

        User.create(
            username="{fill in here}",
            first_name="{fill in here}",
            last_name="{fill in here}",
            email="{fill in here}",
            password="{fill in here}",
            super_user="{fill in here}",
            subscription_type="{fill in here}"
        )
        ```
        """

        if phone_number is not None:
            phone_number = standardize_phone_number(phone_number)

        req_json = dict(
            username=username,
            first_name=first_name,
            last_name=last_name,
            email=email,
            subscription_type=subscription_type,
            super_user=super_user,
            password=password,
            phone_number=phone_number,
            otp=otp,
        )

        response = Client._post_data(relative_url=f"/user/", json=req_json)

        return pd.DataFrame(response, index=[0])[User.USER_COLS]

    @staticmethod
    def ls(
        offset: int = 0,
        limit: int = 100,
        disabled: bool = False,
    ) -> List["User"]:
        """Return the list of User instances available in the server.

        Args:
            offset: The number of users to offset at the beginning. If **None**, then the default value **0** will be used.
            limit: The maximum number of users to return from the server. If None, then the default value 100 will be used.
            disabled: If set to **True**, then only the deleted users will be returned. Else, the default value **False** will
                be used to return only the list of active users.

        Returns:
            A list of User instances available in the server.

        Raises:
            ConnectionError: If the server address is invalid or not reachable.

        An example to get the user's list from the server:

        ```python
        User.ls()
        ```
        """
        users = Client._get_data(
            relative_url=f"/user/?disabled={disabled}&offset={offset}&limit={limit}"
        )

        ux = [
            User(
                uuid=user["uuid"],
                username=user["username"],
                first_name=user["first_name"],
                last_name=user["last_name"],
                email=user["email"],
                subscription_type=user["subscription_type"],
                super_user=user["super_user"],
                disabled=user["disabled"],
                created=user["created"],
                is_mfa_active=user["is_mfa_active"],
                phone_number=user["phone_number"],
                is_phone_number_verified=user["is_phone_number_verified"],
            )
            for user in users
        ]

        return ux

    @staticmethod
    def as_df(ux: List["User"]) -> pd.DataFrame:
        """Return the details of User instances as a pandas dataframe.

        Args:
            ux: List of user instances.

        Returns:
            Details of all the User in a dataframe.

        Raises:
            ConnectionError: If the server address is invalid or not reachable.

        An example to list the available users:

        ```python
        ux = Model.ls()
        Model.as_df(ux)
        ```
        """
        lists = get_attributes_from_instances(ux, User.USER_COLS)  # type: ignore
        return generate_df(lists, User.USER_COLS)

    @staticmethod
    def disable(user: Union[str, List[str]], otp: Optional[str] = None) -> pd.DataFrame:
        """Disable one or more users.

        Args:
            user: user_uuid/username to disabled. To disable multiple users, please pass the uuids/names as a list.
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            A pandas DataFrame encapsulating the details of the disabled user.

        Raises:
            ValueError: If the OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        An example to disable a user:

        ```python
        User.disable(user=username)
        ```
        """
        _users = user if isinstance(user, list) else [user]
        response_list = []
        for user in _users:
            user_uuid = User.details(user=user)["uuid"]
            url = f"/user/{user_uuid}"
            response = Client._delete_data(
                relative_url=check_and_append_otp_query_param(url, otp)
            )
            response_list.append(response)

        return pd.DataFrame(response_list)[User.USER_COLS]

    @staticmethod
    def enable(user: Union[str, List[str]], otp: Optional[str] = None) -> pd.DataFrame:
        """Enable one or more disabled users.

        Args:
            user: user_uuid/username to enable. To enable multiple users, please pass the uuids/names as a list.
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            A pandas DataFrame encapsulating the details of the enabled user.

        Raises:
            ValueError: If the OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        An example to enable a disabled user:

        ```python
        User.enable(user=username)
        ```
        """

        _users = user if isinstance(user, list) else [user]
        response_list = []
        for user in _users:
            user_uuid = User.details(user=user)["uuid"]
            url = f"/user/{user_uuid}/enable"
            response = Client._get_data(
                relative_url=check_and_append_otp_query_param(url, otp)
            )
            response_list.append(response)

        return pd.DataFrame(response_list)[User.USER_COLS]

    @staticmethod
    def details(user: Optional[str] = None) -> Dict[str, Union[str, bool]]:
        """Get user details

        Please do not pass the optional user parameter unless you are a super user. Only a
        super user can view details for other users.

        Args:
            user: Account user_uuid/username to get details. If not passed, then the currently logged-in
                details will be returned.

        Returns:
            A dict containing the details of the user

        Raises:
            ValueError: If the user_uuid/username is invalid or the user have insufficient permission to access other user's data
            ConnectionError: If the server address is invalid or not reachable.

        An example to get details of the currently logged-in user:

        ```python

        # Importing necessary libraries
        from ..client import User, Client

        # Authenticate
        Client.get_token(username="{fill in here}", password="{fill in here}")
        User.details()
        ```
        """
        relative_url = (
            f"/user/details?user_uuid_or_name={user}"
            if user is not None
            else f"/user/details"
        )
        return Client._get_data(relative_url=relative_url)

    @staticmethod
    def update(
        user: Optional[str] = None,
        username: Optional[str] = None,
        first_name: Optional[str] = None,
        last_name: Optional[str] = None,
        email: Optional[str] = None,
        otp: Optional[str] = None,
    ) -> pd.DataFrame:
        """Update existing user details in the server.

         Please do not pass the optional user parameter unless you are a super user. Only a
         super user can update details for other users.

        Args:
            user: Account user_uuid/username to update. If not passed, then the default
                value **None** will be used to update the currently logged-in user details.
            username: New username for the user.
            first_name: New first name for the user.
            last_name: New last name for the user.
            email: New email for the user.
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            A pandas DataFrame encapsulating the updated user details.

        Raises:
            ValueError: If the OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        An example to update the first name of the currently logged-in user:
        ```python
        # Importing necessary libraries
        from ..client import User, Client

        # Authenticate
        Client.get_token(username="{fill in here}", password="{fill in here}")
        User.update(first_name="{fill in here}")
        ```
        """
        user_uuid = User.details(user=user)["uuid"]
        req_json = dict(
            username=username,
            first_name=first_name,
            last_name=last_name,
            email=email,
            otp=otp,
        )

        response = Client._post_data(
            relative_url=f"/user/{user_uuid}/update", json=req_json
        )

        return pd.DataFrame(response, index=[0])[User.USER_COLS]

    @staticmethod
    def _get_mfa_provision_url(otp: Optional[str] = None) -> str:
        """Get MFA provisioning url

        Args:
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            The provisioning url
        """
        url = f"/user/mfa/generate"
        response = Client._get_data(
            relative_url=check_and_append_otp_query_param(url, otp)
        )
        return response["mfa_url"]

    @staticmethod
    def enable_mfa(otp: Optional[str] = None) -> PilImage:
        """Enable MFA for the user

        This method will generate a QR code which can be scanned by an authenticator app,
        such as Google Authenticator.

        Args:
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            The generated QR code

        An example to enable MFA:

        ```python
        User.enable_mfa()
        ```
        """
        qr_code = qrcode.make(User._get_mfa_provision_url(otp))

        return qr_code

    @staticmethod
    def activate_mfa(otp: int) -> pd.DataFrame:
        """Activate MFA for the user

        If not enabled, please call the `enable_mfa` method first before calling this method.

        Args:
            otp: Dynamically generated six-digit verification code from the authenticator app

        Returns:
            A pandas DataFrame encapsulating the MFA activated user details

        Raises:
            ValueError: If the user entered six-digit verification code is invalid

        An example to activate MFA:

        ```python
        User.activate_mfa(otp)
        ```
        """
        response = Client._post_data(
            relative_url="/user/mfa/activate",
            json=dict(user_otp=otp),
        )

        return pd.DataFrame(response, index=[0])

    @staticmethod
    def disable_mfa(
        user: Optional[str] = None, otp: Optional[str] = None
    ) -> pd.DataFrame:
        """Disable MFA for the user

        We currently support two types of OTPs to disable multi-factor authentication for your account.

        If you have access to the authenticator app, you can enter the dynamically generated six-digit
        verification code from the app. If you do not have access to the authentication app, you may request a
        OTP to be sent to your registered phone number by SMS.

        To get OTP by SMS, you must first call `User.send_sms_otp` method which will send the OTP to your registered
        phone number. Once you have the OTP with you, then call this method with the OTP to deactivate MFA for your account.

        Currently, we only support the above two methods for disabling MFA. In case, you don't have access to the
        authenticator app and your registered phone number, please contact your administrator.

        !!! note

            Please do not pass the user parameter unless you are a super user. Only
            a super user can disable MFA for other users.

        Args:
            user: Account user_uuid/username to disable MFA. If not passed, then the default
                value **None** will be used to disable MFA for the currently logged-in user.
            otp: Dynamically generated six-digit verification code from the authenticator app or
                the OTP you have received via SMS.

        Returns:
            A pandas DataFrame encapsulating the MFA disabled user details

        Raises:
            ValueError: If a non-super user tries to disable MFA for other users
            ValueError: If the OTP is invalid.
            ValueError: If the user_uuid/username is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        Below is an example of disabling MFA for the currently logged in user using the verification code generated by the authentication application.
        The example assumes that you have already activated the MFA on your account and have access to the authentication application.

        Example:
            ```python
            # Importing necessary libraries
            from ..client import Client, User

            # Authenticate
            Client.get_token(username="{fill in username}", password="{fill in password}", otp="{fill in otp}")

            # Please pass the verification code generated by the authenticator app in the
            # otp parameter below
            User.disable_mfa(otp="{fill in otp}")
            ```

        Below is an example of disabling MFA for the currently logged in user using the SMS OTP. The example assumes that
        you have already have a valid token and validated your phone number on our servers.

        Example:
            ```python
            # Importing necessary libraries
            from ..client import Client, User

            # Authenticate
            Client.set_token(token="{fill in token}")

            # Please do not change the message_template_name
            User.send_sms_otp(username="{fill in username}", message_template_name="disable_mfa")

            # Please run the line below only after you receive the OTP via SMS and
            # pass the same in the otp parameter below
            User.disable_mfa(otp="{fill in otp}")
            ```
        """

        user_uuid = User.details(user=user)["uuid"]
        url = f"/user/mfa/{user_uuid}/disable"
        response = Client._delete_data(
            relative_url=check_and_append_otp_query_param(url, otp)
        )

        return pd.DataFrame(response, index=[0])

    @staticmethod
    def enable_sso(
        sso_provider: str, sso_email: str, otp: Optional[str] = None
    ) -> pd.DataFrame:
        """Enable Single sign-on (SSO) for the user

        Args:
            sso_provider: Name of the Single sign-on (SSO) identity provider
            sso_email: Email id going to be used for SSO authentication
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            A pandas DataFrame encapsulating the SSO enabled user details

        An example to enable Single sign-on (SSO):

        ```python
        User.enable_sso(sso_provider, sso_email)
        ```
        """
        response = Client._post_data(
            relative_url=f"/user/sso/enable",
            json=dict(sso_provider=sso_provider, sso_email=sso_email, otp=otp),
        )
        success_msg = f"Single sign-on (SSO) is successfully enabled for {sso_provider}. Please use {response['sso_email']} as the email address while authenticating with {sso_provider}."

        return success_msg

    @staticmethod
    def disable_sso(
        sso_provider: str,
        user: Optional[str] = None,
        otp: Optional[str] = None,
    ) -> pd.DataFrame:
        """Disable Single sign-on (SSO) for the user

        Please do not pass the user parameter unless you are a super user. Only
        a super user can disable SSO for other users.

        Args:
            sso_provider: The name of the Single sign-on (SSO) provider you want to disable.
            user: Account user_uuid/username to disable SSO. If not passed, then the default
                value **None** will be used to disable SSO for the currently logged-in user.
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            A pandas DataFrame encapsulating the SSO disabled user details

        Raises:
            ValueError: If a non-super user tries to disable SSO for other users
            ValueError: If the OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        An example to disable SSO:

        ```python
        User.disable_sso(sso_provider="provider_name")
        ```
        """

        user_uuid = User.details(user=user)["uuid"]
        url = f"/user/sso/{user_uuid}/disable/{sso_provider}"
        response = Client._delete_data(
            relative_url=check_and_append_otp_query_param(url, otp)
        )

        success_msg = f"Single sign-on (SSO) is successfully disabled for {response['sso_provider']}."

        return success_msg

    @staticmethod
    def register_phone_number(
        phone_number: Optional[str] = None,
        otp: Optional[str] = None,
    ) -> Dict[str, Union[str, bool]]:
        """Register a phone number

        Registering your phone number will help you to regain access in case you forget your password and cannot access
        your account. To receive the OTP via SMS, you need to register and validate your phone number. Calling this
        method will send an OTP via SMS to the phone number and you need to call the `User.validate_phone_number` method
        with the OTP you have received to complete the registration and validation process.

        Args:
            phone_number: Phone number to register. The phone number should follow the pattern of the country
                code followed by your phone number. For example, **440123456789, +440123456789,
                00440123456789, +44 0123456789, and (+44) 012 345 6789** are all valid formats for registering a
                UK phone number. If the phone number is not passed in the arguments, then the OTP will be sent to
                the phone number that was already registered to the user's account.
            otp: Dynamically generated six-digit verification code from the authenticator app. Please pass this
                parameter only if you have activated the MFA for your account.

        Returns:
            A dict containing the updated user details

        Raises:
            ValueError: If the OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        An example to register a new phone number:

        ```python
        # Importing necessary libraries
        from ..client import Client, User

        # Authenticate
        # The below code to get the token assumes that the username, password, and server address are stored in AIRT_SERVICE_USERNAME,
        # AIRT_SERVICE_PASSWORD, and AIRT_SERVER_URL environment variables, respectively.
        Client.get_token()

        User.register_phone_number(phone_number="{fill in here}")
        ```
        """
        if phone_number is not None:
            phone_number = standardize_phone_number(phone_number)

        req_json = dict(phone_number=phone_number, otp=otp)
        return Client._post_data(
            relative_url="/user/register_phone_number", json=req_json
        )

    @staticmethod
    def validate_phone_number(
        otp: Optional[str] = None,
    ) -> Dict[str, Union[str, bool]]:
        """Validate a registered phone number

        Please call the `User.register_phone_number` method to get the OTP via SMS and then call this method
        with the OTP to complete the registration and validation process.

        Args:
            otp: The OTP you have received on your registered phone number.

        Returns:
            A dict containing the updated user details

        Raises:
            ValueError: If the OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        An example to register and validate a phone number:

        ```python
        # Importing necessary libraries
        from ..client import Client, User

        # Authenticate
        # The below code to get the token assumes that the username, password, and server address are stored in AIRT_SERVICE_USERNAME,
        # AIRT_SERVICE_PASSWORD, and AIRT_SERVER_URL environment variables, respectively.
        Client.get_token()

        # Registering the phone number
        User.register_phone_number(phone_number="{fill in here}")

        # Validating the phone number
        User.validate_phone_number(otp="{fill in here}")
        ```
        """
        url = "/user/validate_phone_number"

        return Client._get_data(relative_url=check_and_append_otp_query_param(url, otp))

    @staticmethod
    def send_sms_otp(username: str, message_template_name: str) -> str:
        """Send OTP via SMS to the user's registered phone number

        This method does not require a login. You should only call this method if you wish to reset your
        password or disable MFA using SMS OTP.

        Calling this method will only send an OTP to your registered phone number via SMS. Following this method call, you
        should explicitly call the `User.reset_password` if you want to reset the password or `User.disable_mfa` if
        you want to disable MFA for your account using the SMS OTP.

        Please remember to pass a valid message_template_name along with the request. For example, in case you want to
        reset your password then set template to **message_template_name="reset_password"** and in case you want to
        disable MFA for your account then set template to **message_template_name="disable_mfa"**. At present, the API
        supports **"reset_password"** and **"disable_mfa"** as valid message templates.

        Args:
            username: Account username to send the OTP
            message_template_name: The message template to use while sending the OTP via SMS. At present,
                the API supports **"reset_password"** and **"disable_mfa"** as valid message templates

        Returns:
            The SMS status message

        Below is an example for resetting the password using the SMS OTP. The example assumes that you have already registered
        and validated your phone number on our servers.

        Example:
            ```python
            # Importing necessary libraries
            from ..client import User

            # Please do not change the message_template_name
            User.send_sms_otp(username="{fill in username}", message_template_name="reset_password")

            # Please run the line below only after you receive the OTP via SMS and
            # pass the same in the otp parameter below
            User.reset_password(username="{fill in username}", new_password="{fill in new_password}", otp="{fill in otp}")
            ```

        Below is an example of disabling MFA for the currently logged in user using the SMS OTP. The example assumes that
        you have already have a valid token and validated your phone number on our servers.

        Example:
            ```python
            # Importing necessary libraries
            from ..client import Client, User

            # Authenticate
            Client.set_token(token="{fill in token}")

            # Please do not change the message_template_name
            User.send_sms_otp(username="{fill in username}", message_template_name="disable_mfa")

            # Please run the line below only after you receive the OTP via SMS and
            # pass the same in the otp parameter below
            User.disable_mfa(otp="{fill in otp}")
            ```
        """
        url = f"/user/send_sms_otp?username={username}&message_template_name={message_template_name}"

        return Client._get_data(relative_url=url)

    @staticmethod
    def reset_password(username: str, new_password: str, otp: str) -> str:
        """Resets the password of an account either using a TOTP or SMS OTP.

        We currently support two types of OTPs to reset the password for your account and you don't have to be logged
        in to call this method

        If you have already activated the MFA for your account, then you can either pass the dynamically generated
        six-digit verification code from the authenticator app (TOTP) or you can also request an OTP via SMS to your registered phone number.

        If the MFA is not activated already, then you can only request the OTP via SMS to your registered phone number.

        To get OTP by SMS, you must first call `User.send_sms_otp` method which will send the OTP to your registered
        phone number. Once you have the OTP with you, then call this method with the OTP to reset your password.

        Currently, we only support the above two methods for resetting the password. In case, you don't have MFA enabled or don't
        have access to your registered phone number, please contact your administrator.

        Args:
            username: Account username to reset the password
            new_password: New password to set for the account
            otp: Dynamically generated six-digit verification code from the authenticator app or the OTP you have received
                via SMS.

        Returns:
            The password reset status message

        Raises:
            ValueError: If the username or OTP is invalid.
            ConnectionError: If the server address is invalid or not reachable.

        Below is an example for resetting the password using the verification code generated by the authentication application.
        The example assumes that you have already activated the MFA on your account and have access to the authentication application.

        Example:
            ```python
            # Importing necessary libraries
            from ..client import User

            # Please pass the verification code generated by the authenticator app in the
            # otp parameter below
            User.reset_password(username="{fill in username}", new_password="{fill in new_password}", otp="{fill in otp}")
            ```

        Below is an example for resetting the password using the SMS OTP. The example assumes that you have already registered
        and validated your phone number on our servers.

        Example:
            ```python
            # Importing necessary libraries
            from ..client import User

            # Please do not change the message_template_name
            User.send_sms_otp(username="{fill in username}", message_template_name="reset_password")

            # Please run the line below only after you receive the OTP via SMS and
            # pass the same in the otp parameter below
            User.reset_password(username="{fill in username}", new_password="{fill in new_password}", otp="{fill in otp}")
            ```
        """
        req_json = dict(username=username, new_password=new_password, otp=otp)
        return Client._post_data(relative_url="/user/reset_password", json=req_json)  # type: ignore
