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

__all__ = ["Client"]

# Cell

from typing import *

# Internal Cell

import os

import pandas as pd
from fastcore.foundation import patch

import airt
from ..logger import get_logger, set_level
from ..helper import get_data, post_data, delete_data, get_base_url
from ..env import SERVICE_USERNAME, SERVICE_PASSWORD, SERVER_URL

# Internal Cell

logger = get_logger(__name__)

# Internal Cell


def _get_credentials(
    username: Optional[str] = None, password: Optional[str] = None
) -> Tuple[(str, str)]:
    """Returns the value for username and password.

    If username is **None**, retrive the value from AIRT_SERVICE_USERNAME environment variable.
    If password is **None**, retrive the value from AIRT_SERVICE_PASSWORD environment variable.

    Args:
        username: Username for your developer account.
        password: Password for your developer account.

    Returns:
        The values for username and password as a tuple.

    Raises:
        Key Error, if the environment variables are not set.
    """

    username = username if username is not None else os.environ.get(SERVICE_USERNAME)

    password = password if password is not None else os.environ.get(SERVICE_PASSWORD)

    if not username and not password:
        raise KeyError(
            f"The username and password are neither passed as parameters nor set in the environment variables "
            f"`{SERVICE_USERNAME}` and `{SERVICE_PASSWORD}`."
        )

    elif not username:
        raise KeyError(
            f"The username is neither passed as parameter nor set in the environment variable {SERVICE_USERNAME}."
        )

    elif not password:
        raise KeyError(
            f"The password is neither passed as parameter nor set in the environment variable {SERVICE_PASSWORD}."
        )

    return (username, password)


# Cell


class Client:
    """A class for authenticating and accessing the airt service.

    To access the airt service, you must create a developer account. Please contact us by email info@airt.ai to get one.

    Upon successful verification, you will receive the username/password for the developer account in an email.

    Finally, you need an application token to access all the APIs in airt service. Please call the `get_token` method with the username/password to get one. You
    can either pass the username, password, and server address as parameters to the `get_token` method or store the same in the **AIRT_SERVICE_USERNAME**,
    **AIRT_SERVICE_PASSWORD**, and **AIRT_SERVER_URL** environment variables.

    Upon successful authentication, the airt services will be available to access.
    """

    server: Optional[str] = None
    auth_token: Optional[str] = None

    def __init__(self, server: str, auth_token: str):
        Client.server = server
        Client.auth_token = auth_token

    @classmethod
    def get_token(
        cls, *, username: str = None, password: str = None, server: str = None
    ):
        """Get application token for airt service from a username/password pair.

        This methods validates the developer credentials and returns an auth token. The returned auth
        token is implicitly used in all the interactions with the server.



        Args:
            username: Username for the developer account. If None (default value), then the value from
                **AIRT_SERVICE_USERNAME** environment variable is used.
            password: Password for the developer account. If None (default value), then the value from
                **AIRT_SERVICE_PASSWORD** environment variable is used.
            server: The airt server uri. If None (default value), then the value from **AIRT_SERVER_URL** environment variable
                is used. If the variable is not set as well, then the default public server will be used. Please leave this
                setting to default unless you are running the service in your own server (please email us to info@airt.ai
                for that possibility).

        Raises:
            ValueException: If the username/password pair does not match.
            ConnectionError: If the server address is invalid or not reachable.
            KeyError: If username/password is neither passed as parameters nor stored in environment variables.

        """
        cls.server = get_base_url(server)

        username, password = _get_credentials(username, password)

        response = post_data(
            url=f"{cls.server}/token",
            data=dict(username=username, password=password),
            token=None,
        )

        cls.auth_token = response["access_token"]

    @staticmethod
    def version() -> pd.Series:
        """Return the server and client versions.

        Returns:
            A pandas series containing the server and client versions.

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

        An example to get the server and client versions:

        ```python
        Client.version()
        ```
        """

        response = Client._get_data(relative_url=f"/version")

        version = {
            "airt-client": airt.__version__,
            "airt-service": response["airt_service"],
        }

        return pd.Series(version, name="Version")

    @classmethod
    def _get_server_url_and_token(cls) -> Tuple[Optional[str], Optional[str]]:
        """Fetch the server URL and the auth token.

        Returns:
            A tuple containing server URL and auth token.
        """

        cls.server = get_base_url(cls.server)

        return cls.server, cls.auth_token

    @classmethod
    def _post_data(
        cls, relative_url: str, data: Optional[Dict[str, Any]]
    ) -> Dict[str, Any]:
        """Make a POST request.

        This method will implicitly add the server base URL and the token for every request.

        Args:
            relative_url: The relative URL of the server's API endpoint.
            data: A Dictionary object to send in the body of the POST request.

        Returns:
            Response body as a dictionary.

        Raises:
            ConnectionError: If the server is not reachable.
            ValueError: If the response code is not in range of 200 - 399.
        """

        server, auth_token = Client._get_server_url_and_token()

        return post_data(
            url=f"{server}{relative_url}",
            data=data,
            token=auth_token,
        )

    @classmethod
    def _get_data(cls, relative_url: str) -> Any:
        """Make a GET request.

        This method will implicitly add the server base URL and the token for every request.

        Args:
            relative_url: The relative URL of the API endpoint.

        Returns:
            A dictionary that encapsulates the response body.

        Raises:
            ConnectionError: If the server is not reachable.
            ValueError: If the response code is not in range of 200 - 399.
        """

        server, auth_token = Client._get_server_url_and_token()

        return get_data(url=f"{server}{relative_url}", token=auth_token)

    @classmethod
    def _delete_data(cls, relative_url: str) -> Dict[str, Any]:
        """Make a DELETE request.

        This method will implicitly add the server base URL and the token for every request.

        Args:
            relative_url: The relative URL of the API endpoint.

        Returns:
            A dictionary that encapsulates the response body.

        Raises:
            ConnectionError: If the server is not reachable.
            ValueError: If the response code is not in range of 200 - 399.
        """

        server, auth_token = Client._get_server_url_and_token()

        return delete_data(url=f"{server}{relative_url}", token=auth_token)
