# 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

# 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("AIRT_SERVICE_USERNAME")
    )

    password = (
        password if password is not None else os.environ.get("AIRT_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"`AIRT_SERVICE_USERNAME` and `AIRT_SERVICE_PASSWORD`."
        )

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

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

    return (username, password)


# Cell


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

    Before you can use the service, you must acquire a username and password for your developer account. Please contact us by email **info@airt.ai** to get one.

    The username, password, and server address can either be passed explicitly while calling the `get_token` method or stored
    in environment variables **AIRT_SERVICE_USERNAME**, **AIRT_SERVICE_PASSWORD**, and **AIRT_SERVER_URL**.

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

    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 method calls the AIRT services for validating the supplied username and password. Upon successful
        authentication, an auth token will be returned and will be implicitly used in all the subsequent interactions with the server.

        Args:
            username: Username for your developer account. If not set (default value **None**), it will try to
                use the value from environment variable **AIRT_SERVICE_USERNAME**.
            password: Password for your developer account. If not set (default value **None**), it will try to
                use the value from environment variable **AIRT_SERVICE_PASSWORD**.
            server: Server address used to connect to. If not set (default value **None**), it will try to
                use the value from environment variable **AIRT_SERVER_URL**. If the variable is not set as well,
                then the default public server will be used. You should leave this to default value unless you
                are running your own server (please contact us for that possibility by email info@airt.ai).

        Raises:
            ValueException: If the username/password pair does not match the one for sevice hosted at server.
            ConnectionError: If the server address is invalid or not reachable.
            KeyError: if username or password are None and there are not 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 version of airt client and airt service

        Returns:
            A pandas series containing the version of both airt client and airt service

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

        An example to get the version of airt-client and airt-service:

        ```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)
