""" Set of functions to define the rules for access control
"""

from django.contrib.auth.models import User

from core_main_app.components.workspace import api as workspace_api
from core_main_app.utils.access_control.exceptions import AccessControlError
from core_main_app.utils.raw_query.mongo_raw_query import add_access_criteria


def has_perm_administration(func, *args, **kwargs):
    """ Is the given user has administration rights.

        Args:
            func:
            *args:
            **kwargs:

        Returns:

        """
    try:
        if args[0].is_superuser:
            return func(*args, **kwargs)
    except Exception:
        pass
    raise AccessControlError("The user doesn't have enough rights to access this data.")


def can_read_or_write_data_workspace(func, workspace, user):
    """ Can user read or write in workspace.

    Args:
        func:
        workspace:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(workspace, user)

    _check_can_read_or_write_workspace(workspace, user)
    return func(workspace, user)


def can_write_data_workspace(func, data, workspace, user):
    """ Can user write data in workspace.

    Args:
        func:
        data:
        workspace:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(data, workspace, user)

    _check_can_write_data(data, user)
    if workspace is not None:
        _check_can_write_workspace(workspace, user)
    return func(data, workspace, user)


def _check_can_write_workspace(workspace, user):
    """ Check that user can write in the workspace.

    Args:
        workspace:
        user:

    Returns:

    """
    accessible_workspaces = workspace_api.get_all_workspaces_with_write_access_by_user(user)
    if workspace not in accessible_workspaces:
        raise AccessControlError("The user does not have the permission to write into this workspace.")


def _check_can_read_or_write_workspace(workspace, user):
    """ Check that user can read or write in the workspace.

    Args:
        workspace:
        user:

    Returns:

    """
    accessible_write_workspaces = workspace_api.get_all_workspaces_with_write_access_by_user(user)
    accessible_read_workspaces = workspace_api.get_all_workspaces_with_read_access_by_user(user)
    if workspace not in list(accessible_write_workspaces) + list(accessible_read_workspaces):
        raise AccessControlError("The user does not have the permission to write into this workspace.")


def can_read_list_data_id(func, list_data_id, user):
    """ Can read list of data.

    Args:
        func:
        list_data_id:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(list_data_id, user)

    list_data = func(list_data_id, user)
    _check_can_read_data_list(list_data, user)

    return list_data


def can_read_data_id(func, data_id, user):
    """ Can read data.

    Args:
        func:
        data_id:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(data_id, user)

    data = func(data_id, user)
    _check_can_read_data(data, user)
    return data


def can_write_data(func, data, user):
    """ Can write data.

    Args:
        func:
        data:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(data, user)

    _check_can_write_data(data, user)
    return func(data, user)


def can_read_data(func, data, user):
    """ Can read data.

    Args:
        func:
        data:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(data, user)

    _check_can_read_data(data, user)
    return func(data, user)


def can_read_data_query(func, query, user):
    """ Can read a data, given a query.

    Args:
        func:
        query:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(query, user)

    # update the query
    query = _update_can_read_query(query, user)
    # get list of data
    data_list = func(query, user)
    # TODO: check if necessary because it is time consuming (checking that user has access to list of returned data)
    # check that user can access the list of data
    _check_can_read_data_list(data_list, user)
    return data_list


def can_read_user(func, user):
    """ Can read data, given a user.

    Args:
        func:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(user)

    # get list of data
    data_list = func(user)
    # check that the user can access the list of data
    _check_can_read_data_list(data_list, user)
    # return list of data
    return data_list


def can_change_owner(func, data, new_user, user):
    """ Can user change data's owner.

    Args:
        func:
        data:
        new_user:
        user:

    Returns:

    """
    if user.is_superuser:
        return func(data, new_user, user)

    if data.user_id != str(user.id):
        raise AccessControlError("The user doesn't have enough rights to access this data.")

    return func(data, new_user, user)


def _check_can_write_data(data, user):
    """ Check that the user can write a data.

    Args:
        data:
        user:

    Returns:

    """
    if data.user_id != str(user.id):
        if hasattr(data, 'workspace') and data.workspace is not None:
            # get list of accessible workspaces
            accessible_workspaces = workspace_api.get_all_workspaces_with_write_access_by_user(user)
            # check that accessed data belongs to an accessible workspace
            if data.workspace not in accessible_workspaces:
                raise AccessControlError("The user doesn't have enough rights to access this data.")
        # workspace is not set
        else:
            raise AccessControlError("The user doesn't have enough rights to access this data.")


def _check_can_read_data(data, user):
    """ Check that the user can read a data.

    Args:
        data:
        user:

    Returns:

    """
    # workspace case
    if data.user_id != str(user.id):
        # workspace is set
        if hasattr(data, 'workspace') and data.workspace is not None:
            # get list of accessible workspaces
            accessible_workspaces = workspace_api.get_all_workspaces_with_read_access_by_user(user)
            # check that accessed data belongs to an accessible workspace
            if data.workspace not in accessible_workspaces:
                raise AccessControlError("The user doesn't have enough rights to access this data.")
        # workspace is not set
        else:
            raise AccessControlError("The user doesn't have enough rights to access this data.")


def _check_can_read_data_list(data_list, user):
    """ Check that the user can read each data of the list.

    Args:
        data_list:
        user:

    Returns:

    """
    if len(data_list) > 0:
        # get list of accessible workspaces
        accessible_workspaces = workspace_api.get_all_workspaces_with_read_access_by_user(user)
        # check access is correct
        for data in data_list:
            # user is data owner
            if data.user_id == str(user.id):
                continue
            # user is not owner or data not in accessible workspace
            if data.workspace is None or data.workspace not in accessible_workspaces:
                raise AccessControlError("The user doesn't have enough rights to access this data.")


def _update_can_read_query(query, user):
    """ Update query with access control parameters.

    Args:
        query:
        user:

    Returns:

    """

    # workspace case
    # list accessible workspaces
    accessible_workspaces = [workspace.id for workspace in
                             workspace_api.get_all_workspaces_with_read_access_by_user(user)]
    # update query with workspace criteria
    query = add_access_criteria(query, accessible_workspaces, user)
    return query
