from polly.auth import Polly
from polly.errors import InvalidParameterException, error_handler, InvalidPathException
from polly import helpers
from polly import constants as const
import logging
import pandas as pd
import json
import os
from polly.help import example, doc


class Workspaces:
    """
    This class contains functions to interact with workspaces on Polly. Users can create a workspace, fetch list\
 of workspaces, upload data to workspace and download data from workspace. To get started, users need to \
initialize a object that can use all function and methods of Workspaces class.

    ``Args:``
        |  ``token (str):`` token copy from polly.


    .. code::


            # to initialize a obj
            workspaces = Workspaces(token)
            # from there you can other methods
            workspaces.fetch_my_workspaces()

    If you are authorised then you can initialize object without token to know about \
:ref:`authentication <auth>`.
    """

    example = classmethod(example)
    doc = classmethod(doc)

    def __init__(self, token=None, env="", default_env="polly") -> None:
        # check if COMPUTE_ENV_VARIABLE present or not
        # if COMPUTE_ENV_VARIABLE, give priority
        env = helpers.get_platform_value_from_env(
            const.COMPUTE_ENV_VARIABLE, default_env, env
        )
        self.session = Polly.get_session(token, env=env)
        self.base_url = f"https://v2.api.{self.session.env}.elucidata.io"
        self.resource_url = f"{self.base_url}/workspaces"
        if self.session.env == "polly":
            self.env_string = "prod"
        elif self.session.env == "testpolly":
            self.env_string = "test"
        else:
            self.env_string = "devenv"

    def create_workspace(self, name: str, description=None):
        """
        This function create workspace on Polly.

        ``Args:``
            |  ``name (str):`` name of the workspace.
            |  ``description (str):`` general information about workspace.

        ``Returns:``
            |  It will return a object like this.
            .. code::



                        {
                        'id': 9999,
                        'name': 'rrrrr',
                        'active': True,
                        'description': 'for docu',
                        'created_time': '2022-03-16 11:08:47.127260',
                        'last_modified': '2022-03-16 11:08:47.127260',
                        'creator': 1127,
                        'project_property': {
                            'type': 'workspaces',
                            'labels': ''
                        },
                        'organisation': 1
                        }


        .. code::


                # to create a obj
                workspaces = Workspaces(token)
                # from there you can other methods
                workspaces.create_workspace("name_of_workspace")

        """
        url = self.resource_url
        payload = {
            "data": {
                "type": "workspaces",
                "attributes": {
                    "name": name,
                    "description": description,
                    "project_property": {"type": "workspaces", "labels": ""},
                },
            }
        }
        response = self.session.post(url, data=json.dumps(payload))
        error_handler(response)
        attributes = response.json()["data"]["attributes"]
        logging.basicConfig(level=logging.INFO)
        logging.info("Workspace Created !")
        return attributes

    def fetch_my_workspaces(self):
        """
        This function fetch workspaces from Polly.

        ``Args:``
            |  None

        ``Returns:``
            |  it will return a table with attributes.


        .. code::


                # create a obj
                workspaces = Workspaces(token)
                # from there you can other methods
                workspaces.fetch_my_workspaces()
        """
        url = self.resource_url
        response = self.session.get(url)
        error_handler(response)
        pd.set_option("display.max_columns", 20)
        dataframe = pd.DataFrame.from_dict(
            pd.json_normalize(response.json()["data"]), orient="columns"
        )
        return dataframe

    def upload_to_workspaces(
        self, workspace_id: int, workspace_path: str, local_path: str
    ) -> None:
        """
        Function to upload files/folders to workspaces.

        ``Args:``
            |  ``workspace_id :`` id of the where file need to uploaded.
            |  ``workspace_path :`` file path on workspace if folder does not exist it will create.
            |  ``local_path :`` uploaded file path.

        ``Returns:``
            |  None

        ``Errors:``
            |  ``InvalidParameterException:`` when the parameter like workspace id is invalid.
            |  ``InvalidPathException:`` when the file to path is invalid.


        .. code::


                # create a obj
                workspaces = Workspaces(token)
                # from there you can other methods
                workspaces.upload_to_workspaces(workspace_id, workspace_path, local_path)

        """
        if not (workspace_id and isinstance(workspace_id, int)):
            raise InvalidParameterException("workspace_id")
        if not (local_path and isinstance(local_path, str)):
            raise InvalidParameterException("local_path")
        if not (workspace_path and isinstance(workspace_path, str)):
            raise InvalidParameterException("workspace_path")
        isExists = os.path.exists(local_path)
        if not isExists:
            raise InvalidPathException
        sts_url = f"{self.base_url}/projects/{workspace_id}/credentials/files"
        creds = self.session.get(sts_url)
        error_handler(creds)
        credentials = helpers.get_sts_creds(creds.json())
        bucket = f"mithoo-{self.env_string}-project-data-v1"
        s3_path = f"{bucket}/{workspace_id}/"
        s3_path = f"s3://{helpers.make_path(s3_path, workspace_path)}"
        helpers.upload_to_S3(s3_path, local_path, credentials)
        logging.basicConfig(level=logging.INFO)
        logging.info(f"Upload successful on workspace-id={workspace_id}.")

    def download_from_workspaces(self, workspace_id: int, workspace_path: str) -> None:
        """
        Function to download files/folders from workspaces.

        ``Args:``
            |  ``workspace_id :`` id of the where file need to uploaded.
            |  ``workspace_path :`` downloaded file on workspace.

        ``Returns:``
            |  None

        ``Error:``
            |  ``InvalidPathException :`` for invalid path.
            |  ``OperationFailedException :`` when downloading fails.
            |  ``InvalidParameterException:`` when the parameter like workspace id is invalid.


        .. code::


                # create a obj
                workspaces = Workspaces(token)
                # from there you can other methods
                workspaces.download_from_workspaces(workspace_id, workspace_path)

        """
        if not (workspace_id and isinstance(workspace_id, int)):
            raise InvalidParameterException("workspace_id")
        if not (workspace_path and isinstance(workspace_path, str)):
            raise InvalidParameterException("workspace_path")
        sts_url = f"{self.base_url}/projects/{workspace_id}/credentials/files"
        creds = self.session.get(sts_url)
        error_handler(creds)
        credentials = helpers.get_sts_creds(creds.json())
        bucket = f"mithoo-{self.env_string}-project-data-v1"
        s3_path = f"{bucket}/{workspace_id}/"
        s3_path = f"s3://{helpers.make_path(s3_path, workspace_path)}"
        helpers.download_from_S3(s3_path, workspace_path, credentials)
