from typing import Optional

import requests

from mstrio.connection import Connection
from mstrio.utils.error_handlers import ErrorHandler


@ErrorHandler(err_msg='Error while creating the package holder')
def create_package_holder(connection: Connection, project_id: Optional[str] = None,
                          error_msg: Optional[str] = None) -> requests.Response:
    """Create a new in-memory metadata package holder.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.post(
        url=f'{connection.base_url}/api/packages',
        headers={'X-MSTR-ProjectID': project_id}
    )


@ErrorHandler(err_msg='Error while updating the package holder with id: {id}')
def update_package_holder(connection: Connection, body: dict, id: str,
                          project_id: Optional[str] = None, prefer: str = "respond-async",
                          error_msg: Optional[str] = None) -> requests.Response:
    """Fill the content of the in-memory metadata package holder per supplied
    specification. Currently, it's only supported when the holder is empty.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        body (dict): dictionarized PackageConfig object (with `to_dict()`)
        id (str): ID of the package to be updated
        prefer (str, optional): API currently just supports asynchronous mode,
        not support synchronous mode, so header parameter ‘Prefer’ must be set
        to ‘respond-async’ in your request. Defaults to "respond-async".
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.put(
        url=f'{connection.base_url}/api/packages/{id}',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        },
        json=body
    )


@ErrorHandler(err_msg='Error while downloading the package with id: {id}')
def download_package(connection: Connection, id: str, project_id: Optional[str] = None,
                     error_msg: Optional[str] = None) -> requests.Response:
    """Download a package binary.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be downloaded.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.get(
        url=f'{connection.base_url}/api/packages/{id}/binary',
        headers={'X-MSTR-ProjectID': project_id}
    )


@ErrorHandler(err_msg='Error while uploading the package with id: {id}')
def upload_package(connection: Connection, id: str, file: bytes, project_id: Optional[str] = None,
                   error_msg: Optional[str] = None) -> requests.Response:
    """Upload package to sandbox directly.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be uploaded.
        file (bytes): package in a format of a binary string.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.put(
        url=f'{connection.base_url}/api/packages/{id}/binary',
        headers={'X-MSTR-ProjectID': project_id},
        files={'file': file}
    )


@ErrorHandler(err_msg='Error while getting the package holder with id: {id}')
def get_package_holder(connection: Connection, id: str, project_id: Optional[str] = None,
                       show_content: bool = True,
                       error_msg: Optional[str] = None) -> requests.Response:
    """Get definition of a package, including package status and its detail
    content.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be retrieved.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        show_content (bool, optional): Show package content or not. Defaults to
        False.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id

    return connection.get(
        url=f'{connection.base_url}/api/packages/{id}',
        headers={'X-MSTR-ProjectID': project_id},
        params={'showContent': show_content}
    )


@ErrorHandler(err_msg='Error while deleting the package holder with id: {id}')
def delete_package_holder(connection: Connection, id: str, project_id: Optional[str] = None,
                          prefer: str = 'respond-async',
                          error_msg: Optional[str] = None) -> requests.Response:
    """Delete the in-memory metadata package holder, releasing associated
    Intelligence Server resources.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be deleted.
        prefer (str, optional): API currently just supports asynchronous mode,
        not support synchronous mode, so header parameter ‘Prefer’ must be set
        to ‘respond-async’ in your request. Defaults to "respond-async".
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """

    project_id = project_id if project_id is not None else connection.project_id
    return connection.delete(
        url=f'{connection.base_url}/api/packages/{id}',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        }
    )


@ErrorHandler(err_msg='Error while creating the import for package holder with id: {id}')
def create_import(connection: Connection, id: str, project_id: Optional[str] = None,
                  generate_undo: bool = False,
                  error_msg: Optional[str] = None) -> requests.Response:
    """Create a package import process.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package for which import process will be
        created.
        generate_undo (bool, optional): Generate undo package or not. Defaults
        to False.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """

    # TODO: Change to a parameter when any other values are supported
    prefer = 'respond-async'

    project_id = project_id if project_id is not None else connection.project_id
    return connection.post(
        url=f'{connection.base_url}/api/packages/imports',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        },
        params={
            'packageId': id,
            'generateUndo': generate_undo
        },
    )


@ErrorHandler(err_msg='Error while getting the import with id: {id}')
def get_import(connection: Connection, id: str, project_id: Optional[str] = None,
               error_msg: Optional[str] = None) -> requests.Response:
    """Get result of a package import process.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): Import process ID.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.get(
        url=f'{connection.base_url}/api/packages/imports/{id}',
        headers={'X-MSTR-ProjectID': project_id}
    )


@ErrorHandler(err_msg='Error while deleting the import with id: {id}')
def delete_import(connection: Connection, id: str, project_id: Optional[str] = None,
                  error_msg: Optional[str] = None) -> requests.Response:
    """Closes an existing import process previously created.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): Import process ID.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    # TODO: Change to a parameter when any other values are supported
    prefer = 'respond-async'

    project_id = project_id if project_id is not None else connection.project_id
    return connection.delete(
        url=f'{connection.base_url}/api/packages/imports/{id}',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        }
    )


@ErrorHandler(err_msg='Error while creating the undo for import with id: {id}')
def create_undo(connection: Connection, id: str, project_id: Optional[str] = None,
                error_msg: Optional[str] = None) -> requests.Response:
    """Download undo package binary for this import process.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): Import process ID.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.get(
        url=f'{connection.base_url}/api/packages/imports/{id}/undoPackage/binary',
        headers={'X-MSTR-ProjectID': project_id}
    )

from typing import Optional

import requests

from mstrio.connection import Connection
from mstrio.utils.error_handlers import ErrorHandler


@ErrorHandler(err_msg='Error while creating the package holder')
def create_package_holder(connection: Connection, project_id: Optional[str] = None,
                          error_msg: Optional[str] = None) -> requests.Response:
    """Create a new in-memory metadata package holder.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.post(
        url=f'{connection.base_url}/api/packages',
        headers={'X-MSTR-ProjectID': project_id}
    )


@ErrorHandler(err_msg='Error while updating the package holder with id: {id}')
def update_package_holder(connection: Connection, body: dict, id: str,
                          project_id: Optional[str] = None, prefer: str = "respond-async",
                          error_msg: Optional[str] = None) -> requests.Response:
    """Fill the content of the in-memory metadata package holder per supplied
    specification. Currently, it's only supported when the holder is empty.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        body (dict): dictionarized PackageConfig object (with `to_dict()`)
        id (str): ID of the package to be updated
        prefer (str, optional): API currently just supports asynchronous mode,
        not support synchronous mode, so header parameter ‘Prefer’ must be set
        to ‘respond-async’ in your request. Defaults to "respond-async".
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.put(
        url=f'{connection.base_url}/api/packages/{id}',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        },
        json=body
    )


@ErrorHandler(err_msg='Error while downloading the package with id: {id}')
def download_package(connection: Connection, id: str, project_id: Optional[str] = None,
                     error_msg: Optional[str] = None) -> requests.Response:
    """Download a package binary.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be downloaded.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.get(
        url=f'{connection.base_url}/api/packages/{id}/binary',
        headers={'X-MSTR-ProjectID': project_id}
    )


@ErrorHandler(err_msg='Error while uploading the package with id: {id}')
def upload_package(connection: Connection, id: str, file: bytes, project_id: Optional[str] = None,
                   error_msg: Optional[str] = None) -> requests.Response:
    """Upload package to sandbox directly.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be uploaded.
        file (bytes): package in a format of a binary string.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.put(
        url=f'{connection.base_url}/api/packages/{id}/binary',
        headers={'X-MSTR-ProjectID': project_id},
        files={'file': file}
    )


@ErrorHandler(err_msg='Error while getting the package holder with id: {id}')
def get_package_holder(connection: Connection, id: str, project_id: Optional[str] = None,
                       show_content: bool = True,
                       error_msg: Optional[str] = None) -> requests.Response:
    """Get definition of a package, including package status and its detail
    content.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be retrieved.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        show_content (bool, optional): Show package content or not. Defaults to
        False.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id

    return connection.get(
        url=f'{connection.base_url}/api/packages/{id}',
        headers={'X-MSTR-ProjectID': project_id},
        params={'showContent': show_content}
    )


@ErrorHandler(err_msg='Error while deleting the package holder with id: {id}')
def delete_package_holder(connection: Connection, id: str, project_id: Optional[str] = None,
                          prefer: str = 'respond-async',
                          error_msg: Optional[str] = None) -> requests.Response:
    """Delete the in-memory metadata package holder, releasing associated
    Intelligence Server resources.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package to be deleted.
        prefer (str, optional): API currently just supports asynchronous mode,
        not support synchronous mode, so header parameter ‘Prefer’ must be set
        to ‘respond-async’ in your request. Defaults to "respond-async".
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """

    project_id = project_id if project_id is not None else connection.project_id
    return connection.delete(
        url=f'{connection.base_url}/api/packages/{id}',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        }
    )


@ErrorHandler(err_msg='Error while creating the import for package holder with id: {id}')
def create_import(connection: Connection, id: str, project_id: Optional[str] = None,
                  generate_undo: bool = False,
                  error_msg: Optional[str] = None) -> requests.Response:
    """Create a package import process.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): ID of the package for which import process will be
        created.
        generate_undo (bool, optional): Generate undo package or not. Defaults
        to False.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """

    # TODO: Change to a parameter when any other values are supported
    prefer = 'respond-async'

    project_id = project_id if project_id is not None else connection.project_id
    return connection.post(
        url=f'{connection.base_url}/api/packages/imports',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        },
        params={
            'packageId': id,
            'generateUndo': generate_undo
        },
    )


@ErrorHandler(err_msg='Error while getting the import with id: {id}')
def get_import(connection: Connection, id: str, project_id: Optional[str] = None,
               error_msg: Optional[str] = None) -> requests.Response:
    """Get result of a package import process.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): Import process ID.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.get(
        url=f'{connection.base_url}/api/packages/imports/{id}',
        headers={'X-MSTR-ProjectID': project_id}
    )


@ErrorHandler(err_msg='Error while deleting the import with id: {id}')
def delete_import(connection: Connection, id: str, project_id: Optional[str] = None,
                  error_msg: Optional[str] = None) -> requests.Response:
    """Closes an existing import process previously created.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): Import process ID.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    # TODO: Change to a parameter when any other values are supported
    prefer = 'respond-async'

    project_id = project_id if project_id is not None else connection.project_id
    return connection.delete(
        url=f'{connection.base_url}/api/packages/imports/{id}',
        headers={
            'X-MSTR-ProjectID': project_id,
            'Prefer': prefer
        }
    )


@ErrorHandler(err_msg='Error while creating the undo for import with id: {id}')
def create_undo(connection: Connection, id: str, project_id: Optional[str] = None,
                error_msg: Optional[str] = None) -> requests.Response:
    """Download undo package binary for this import process.

    Args:
        connection (Connection): Object representation of connection to
            MSTR Server.
        id (str): Import process ID.
        project_id (Optional[str]): Optional ID of a project. Defaults to None.
        error_msg (Optional[str]): Optional error message. Defaults to None.

    Returns:
        requests.Response: Response object containing all of the information
        returned by the server.
    """
    project_id = project_id if project_id is not None else connection.project_id
    return connection.get(
        url=f'{connection.base_url}/api/packages/imports/{id}/undoPackage/binary',
        headers={'X-MSTR-ProjectID': project_id}
    )

Create a new in-memory metadata package holder.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Fill the content of the in-memory metadata package holder per supplied     specification. Currently, it's only supported when the holder is empty.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         body (dict): dictionarized PackageConfig object (with `to_dict()`)         id (str): ID of the package to be updated         prefer (str, optional): API currently just supports asynchronous mode,         not support synchronous mode, so header parameter ‘Prefer’ must be set         to ‘respond-async’ in your request. Defaults to "respond-async".         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Download a package binary.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): ID of the package to be downloaded.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Upload package to sandbox directly.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): ID of the package to be uploaded.         file (bytes): package in a format of a binary string.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Get definition of a package, including package status and its detail     content.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): ID of the package to be retrieved.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         show_content (bool, optional): Show package content or not. Defaults to         False.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Delete the in-memory metadata package holder, releasing associated     Intelligence Server resources.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): ID of the package to be deleted.         prefer (str, optional): API currently just supports asynchronous mode,         not support synchronous mode, so header parameter ‘Prefer’ must be set         to ‘respond-async’ in your request. Defaults to "respond-async".         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Create a package import process.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): ID of the package for which import process will be         created.         generate_undo (bool, optional): Generate undo package or not. Defaults         to False.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. # TODO: Change to a parameter when any other values are supported Get result of a package import process.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): Import process ID.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. Closes an existing import process previously created.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): Import process ID.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server. # TODO: Change to a parameter when any other values are supported Download undo package binary for this import process.      Args:         connection (Connection): Object representation of connection to             MSTR Server.         id (str): Import process ID.         project_id (Optional[str]): Optional ID of a project. Defaults to None.         error_msg (Optional[str]): Optional error message. Defaults to None.      Returns:         requests.Response: Response object containing all of the information         returned by the server.