from decimal import Decimal

from google.protobuf.json_format import MessageToDict

from dmac.core import DarsmanAccount, ServiceClient
from dmac.exceptions import ResourceNotFoundError


class AccountSDK:
    def __init__(self, url="127.0.0.1:50055"):
        self.darsman = DarsmanAccount(url=url)

    def authenticate_user(self, api_key, **kwargs):
        """
        Authenticates a user using the provided credentials.

        This method sends an authentication request to the user service using the provided `api_key`
        and user credentials.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            Optional fields that can include:
            - mobile (str): The mobile phone number of the user.
            - password (str): The password of the user.

        :return: dict
            A dictionary representation of the response from the user service, containing user data or any
            error information if the authentication failed.
        """
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)

        response = service_client.authenticate_request(
            "user", api_key=api_key, **kwargs
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def retrieve_account(self, api_key, token):
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)

        response = service_client.retrieve_request(
            "user",
            api_key=api_key,
            token=token,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def create_account(self, api_key, **kwargs):
        """
        Creates a new user account in the system.

        This method sends a request to the user service to create a new account using the provided `api_key`
        and user details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
           - data (dict)
                - mobile (str): The user's mobile phone number.
                - email (str): The user's email address.
                - user_type (str): The type or role of the user (e.g., admin, regular user).
                - first_name (str): The user's first name.
                - last_name (str): The user's last name.
                - address (str): The user's physical address.
                - about (str): A brief description or bio of the user.
                - extra_data (google.protobuf.Struct): Additional structured data related to the user.
                - avatar (bytes): The avatar image data.
                - avatar_extension (str): The file extension of the avatar image.
           - password (str): The password for the new user account.

        :return: dict
            A dictionary representation of the response from the user service, containing the newly created user data or any
            error information if the creation failed.
        """
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)

        response = service_client.create_request("user", api_key=api_key, **kwargs)

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def update_user(self, api_key, token, **kwargs):
        """
        Updates an existing user account in the system.

        This method sends a request to the user service to update user details using the provided `api_key` and `token`.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token identifying the user to be updated.
        :param kwargs: dict
            - uuid_id (str): The UUID of the user.
           - data (dict)
                - mobile (str): The user's mobile phone number.
                - email (str): The user's email address.
                - user_type (str): The type or role of the user (e.g., admin, regular user).
                - first_name (str): The user's first name.
                - last_name (str): The user's last name.
                - address (str): The user's physical address.
                - about (str): A brief description or bio of the user.
                - extra_data (google.protobuf.Struct): Additional structured data related to the user.
                - avatar (bytes): The avatar image data.
                - avatar_extension (str): The file extension of the avatar image.

        :return: dict
            A dictionary representation of the response from the user service, containing the updated user data or any
            error information if the update failed.
        """
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)

        response = service_client.update_request(
            "user", api_key=api_key, token=token, **kwargs
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def delete_user(self, api_key, token):
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)

        response = service_client.delete_request("user", api_key=api_key, token=token)

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def validate_user(self, api_key, **kwargs):
        """
        Validates the existence of a user in the system.

        This method sends a validation request to the user service using the provided `api_key`.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            Optional fields that can include:
            - uuid_id (str): The UUID of the user to be validated.

        :return: dict
            A dictionary representation of the response from the user service, containing validation data or any
            error information if the validation failed.
        """
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.validate_request("user", api_key=api_key, **kwargs)
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def validate_user_jwt(self, api_key, token):
        service_factory = self.darsman.create_service_factory("user")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.validate_jwt_request(
            "user",
            api_key=api_key,
            token=token,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def login(self, api_key, **kwargs):
        """
        Logs in a user using a mobile number and password.

        This method sends a login request to the login service using the provided `api_key`
        along with the user's mobile number, password, and additional optional details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            - mobile (str): The user's mobile phone number.
            - password (str): The user's password.
            - data (dict)
                - ip (str): The IP address from which the request is made.
                - browser (str): The browser used for the login request.
                - status (str): The status of the login attempt.
                - extra_data (google.protobuf.Struct): Additional structured data related to the login attempt.

        :return: dict
            A dictionary representation of the response from the login service, containing login data or any
            error information if the login failed.
        """
        service_factory = self.darsman.create_service_factory("login")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.create_request(
            "login",
            api_key=api_key,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def login_with_otp(self, api_key, **kwargs):
        """
        Sends an OTP to the user for login via mobile or email.

        This method sends a request to the login service to generate and send an OTP to the user's mobile number
        or email address for authentication purposes.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            - data (dict)
                - ip (str): The IP address from which the request is made.
                - browser (str): The browser used for the OTP request.
                - status (str): The status of the OTP request.
                - extra_data (google.protobuf.Struct): Additional structured data related to the OTP request.
            - mobile (str): The user's mobile phone number.
            - email (str): The user's email address.

        :return: dict
            A dictionary representation of the response from the login service, containing any
            error information if the OTP request failed.
        """
        service_factory = self.darsman.create_service_factory("login")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.send_otp(
            "login",
            api_key=api_key,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def verify_login_with_otp(self, api_key, **kwargs):
        """
        Verifies the OTP sent to the user for login.

        This method sends a request to the login service to verify the OTP provided by the user along with their
        mobile number or email address.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            - data (dict)
                - mobile (str): The user's mobile phone number.
                - email (str): The user's email address (optional, if mobile is not used).
                - otp (str): The OTP code sent to the user.

        :return: dict
            A dictionary representation of the response from the login service, containing the verification result or any
            error information if the verification failed.
        """
        service_factory = self.darsman.create_service_factory("login")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.verify_otp(
            "login",
            api_key=api_key,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def verify_login_with_password(self, api_key, **kwargs):
        """
        Verifies user password for login.

        This method sends a request to the login service to verify the password provided by the user along with their
        mobile number or email address.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            - data (dict)
                - mobile (str): The user's mobile phone number.
                - email (str): The user's email address (optional, if mobile is not used).
                - password (str): The user password.

        :return: dict
            A dictionary representation of the response from the login service, containing the verification result or any
            error information if the verification failed.
        """
        service_factory = self.darsman.create_service_factory("login")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.verify_password(
            "login",
            api_key=api_key,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def refresh_token(self, api_key, **kwargs):
        """
        Verifies the OTP sent to the user for login.

        This method sends a request to the login service to verify the OTP provided by the user along with their
        mobile number or email address.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            - data (dict)
                - mobile (str): The user's mobile phone number.
                - email (str): The user's email address (optional, if mobile is not used).
                - otp (str): The OTP code sent to the user.

        :return: dict
            A dictionary representation of the response from the login service, containing the verification result or any
            error information if the verification failed.
        """
        service_factory = self.darsman.create_service_factory("login")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.refresh_token(
            "login",
            api_key=api_key,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def retrieve_wallet(self, api_key, token):
        """
        Retrieves details of a specific wallet.

        This method sends a request to the wallet service to retrieve the wallet information using the provided `api_key`
        and `token`.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token identifying the wallet to retrieve.

        :return: dict
            A dictionary representation of the response from the wallet service, including wallet details or any
            error information if the retrieval failed.
        """
        service_factory = self.darsman.create_service_factory("wallet")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.retrieve_request(
            "wallet",
            api_key=api_key,
            token=token,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def create_wallet(self, api_key, token, **kwargs):
        """
        Creates a new wallet with the specified details.

        This method sends a request to the wallet service to create a new wallet using the provided `api_key`, `token`,
        and additional wallet details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token associated with the wallet creation request.
        :param kwargs: dict
          - data (dict)
            - inventory (float): The initial inventory amount for the wallet.
            - is_lock (bool): Indicates whether the wallet is locked or not.

        :return: dict
            A dictionary representation of the response from the wallet service, including details of the newly created
            wallet or any error information if the creation failed.
        """
        service_factory = self.darsman.create_service_factory("wallet")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.create_request(
            "wallet",
            api_key=api_key,
            token=token,
            **kwargs,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def increase_wallet(self, api_key, token, **kwargs):
        """
        Increases the balance of a specific wallet.

        This method sends a request to the wallet service to increase the wallet balance using the provided `api_key`,
        `token`, and additional transaction details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token identifying the wallet to be updated.
        :param kwargs: dict
            - data (dict)
                - amount (float): The amount to be added to the wallet.
                - order_identifier (str): An identifier for the transaction.
            - transaction_data (dict): Additional transaction details.

        :return: dict
            A dictionary representation of the response from the wallet service, including details of the updated wallet
            or any error information if the update failed.
        """
        service_factory = self.darsman.create_service_factory("wallet")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.increase_wallet(
            "wallet",
            api_key=api_key,
            token=token,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def decrease_wallet(self, api_key, token, **kwargs):
        """
        Decreases the balance of a specific wallet.

        This method sends a request to the wallet service to decrease the wallet balance using the provided `api_key`,
        `token`, and additional transaction details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token identifying the wallet to be updated.
        :param kwargs: dict
            - data (dict)
                - amount (float): The amount to be deducted from the wallet.
                - order_identifier (str): An identifier for the transaction.
            - transaction_data (dict): Additional transaction details.

        :return: dict
            A dictionary representation of the response from the wallet service, including details of the updated wallet
            or any error information if the update failed.
        """
        service_factory = self.darsman.create_service_factory("wallet")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.decrease_wallet(
            "wallet",
            api_key=api_key,
            token=token,
            **kwargs,
        )

        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def retrieve_certificate(self, api_key, token, **kwargs):
        """
        Retrieves a specific certificate.

        This method sends a request to the certificate service to retrieve the certificate details using the provided
        `api_key`, `token`, and additional parameters.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token identifying the certificate to retrieve.
        :param kwargs: dict
            - uuid_id (str): The unique identifier of the certificate to retrieve.

        :return: dict
            A dictionary representation of the response from the certificate service, including certificate details
            or any error information if the retrieval failed.
        """
        service_factory = self.darsman.create_service_factory("certificate")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.retrieve_request(
            "certificate",
            api_key=api_key,
            token=token,
            **kwargs,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def create_certificate(self, api_key, **kwargs):
        """
        Creates a new certificate.

        This method sends a request to the certificate service to create a new certificate using the provided `api_key`
        and additional certificate details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param kwargs: dict
            - data (dict):
                - media_identifier (str): The identifier for the media associated with the certificate.
                - course_identifier (str): The identifier for the course associated with the certificate.
                - expire_date (str): The expiration date of the certificate.
                - extra_data (dict): Additional structured data related to the certificate.

        :return: dict
            A dictionary representation of the response from the certificate service, including details of the newly created
            certificate or any error information if the creation failed.
        """
        service_factory = self.darsman.create_service_factory("certificate")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.create_request(
            "certificate",
            api_key=api_key,
            **kwargs,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def list_certificate(self, api_key, token, **kwargs):
        """
        Lists certificates based on the specified criteria.

        This method sends a request to the certificate service to list certificates using the provided `api_key`,
        `token`, and additional parameters.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token to use for the listing request.
        :param kwargs: dict
            - per_page (int): The number of certificates to return per page.
            - page (int): The page number to retrieve.

        :return: dict
            A dictionary representation of the response from the certificate service, including a list of certificates
            or any error information if the listing failed.
        """
        service_factory = self.darsman.create_service_factory("certificate")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.list_request(
            "certificate", api_key=api_key, token=token, **kwargs
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def validate_certificate(self, api_key, token, **kwargs):
        """
        Validates the specified certificate.

        This method sends a request to the certificate service to validate a certificate using the provided `api_key`,
        `token`, and additional parameters.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token used for validation.
        :param kwargs: dict
            - uuid_id (str): The unique identifier of the certificate to validate.

        :return: dict
            A dictionary representation of the response from the certificate service, including validation results or any
            error information if the validation failed.
        """
        service_factory = self.darsman.create_service_factory("certificate")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.validate_request(
            "certificate",
            api_key=api_key,
            token=token,
            **kwargs,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def add_activity(self, api_key, token, **kwargs):
        """
        Adds a new activity.

        This method sends a request to the activity service to create a new activity using the provided `api_key`,
        `token`, and additional activity details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token used for authentication.
        :param kwargs: dict
            - data (dict):
                - service_name (str): The name of the service associated with the activity.
                - arguments (str): Arguments for the activity.
                - response (str): The response received from the activity.
                - status_code (int): The HTTP status code for the activity.
                - created_at (str): Timestamp when the activity was created.
                - updated_at (str): Timestamp when the activity was last updated.

        :return: dict
            A dictionary representation of the response from the activity service, including details of the newly created
            activity or any error information if the creation failed.
        """
        service_factory = self.darsman.create_service_factory("activity")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.create_request(
            "activity",
            api_key=api_key,
            token=token,
            **kwargs,
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def add_user_asset(self, api_key, token=None, user_id=None, **kwargs):
        """
        Adds a new user asset.

        This method sends a request to the asset service to create a new asset using the provided `api_key`,
        `token`, and additional asset details.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token used for authentication.
        :param kwargs: dict
            - data (dict):
                - type (str): The type of the asset.
                - asset_id (str): The unique identifier for the asset.
                - asset_name (str): The name of the asset.
                - expire_date (str): The expiration date of the asset.
                - extra_data (dict): Any additional data related to the asset.
                - owner_id (str): The ID of the asset owner.

        :return: dict
            A dictionary representation of the response from the asset service, including details of the newly created
            asset or any error information if the creation failed.
        """
        service_factory = self.darsman.create_service_factory("asset")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.create_request(
            "asset", user_id=user_id, api_key=api_key, token=token, **kwargs
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def list_user_asset(self, api_key, token=None, user_id=None, **kwargs):
        """
        Lists user assets based on the specified criteria.

        This method sends a request to the asset service to list assets using the provided `api_key`,
        `token`, and additional parameters.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token used for authentication.
        :param kwargs: dict
            - per_page (int, optional): The number of assets to return per page.
            - page (int, optional): The page number to retrieve.

        :return: dict
            A dictionary representation of the response from the asset service, including a list of assets
            or any error information if the listing failed.
        """
        service_factory = self.darsman.create_service_factory("asset")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.list_request(
            "asset", user_id=user_id, api_key=api_key, token=token, **kwargs
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)

    def validate_user_asset(self, api_key, token, **kwargs):
        """
        Validates the specified user asset.

        This method sends a request to the asset service to validate an asset using the provided `api_key`,
        `token`, and additional parameters.

        :param api_key: str
            The API key required for authentication when making the request.
        :param token: str
            The token used for authentication.
        :param kwargs: dict
            - uuid_id (str): The unique identifier of the asset to validate.

        :return: dict
            A dictionary representation of the response from the asset service, including validation results or any
            error information if the validation failed.
        """
        service_factory = self.darsman.create_service_factory("asset")
        service_client = ServiceClient(self.darsman, service_factory)
        response = service_client.validate_request(
            "asset", api_key=api_key, token=token, **kwargs
        )
        if str(response.code) != "0":
            print(response)

        return MessageToDict(response, preserving_proto_field_name=True)
