from requests import Response, Session
from PrismaCloudIAM import Client, get_mapping_fields
from IAMApiModule import *

APP_USER_OUTPUT = {
    "user_id": "mock_id",
    "user_name": "mock_user_name",
    "first_name": "mock_first_name",
    "last_name": "mock_last_name",
    "active": "true",
    "email": "testdemisto2@paloaltonetworks.com"
}

USER_APP_DATA = IAMUserAppData("mock_id", "mock_user_name", is_active=True, app_data=APP_USER_OUTPUT)

APP_DISABLED_USER_OUTPUT = {
    "user_id": "mock_id",
    "user_name": "mock_user_name",
    "first_name": "mock_first_name",
    "last_name": "mock_last_name",
    "active": "false",
    "email": "testdemisto2@paloaltonetworks.com"
}

DISABLED_USER_APP_DATA = IAMUserAppData("mock_id", "mock_user_name", is_active=False, app_data=APP_DISABLED_USER_OUTPUT)


# helpers methods
def mock_client(mocker):
    mocker.patch.object(Client, 'login', return_value={})
    client = Client(base_url='https://test.com',
                    username='test',
                    password='test',
                    customer_name='test',
                    headers={})
    return client


def get_outputs_from_user_profile(user_profile):
    entry_context = user_profile.to_entry()
    outputs = entry_context.get('Contents')
    return outputs


# tests classes
class TestGetUserCommand:
    """
    Class to group the get user commands test
    """
    def test_get_user_command__existing_user(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains an email of a user
        When:
            - The user exists in the application
            - Calling function get_user_command
        Then:
            - Ensure the resulted User Profile object holds the correct user details
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com'}}

        mocker.patch.object(client, 'get_user', return_value=USER_APP_DATA)
        mocker.patch.object(IAMUserProfile, 'update_with_app_data', return_value={})

        user_profile = IAMCommand().get_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.GET_USER
        assert outputs.get('success') is True
        assert outputs.get('active') is True
        assert outputs.get('id') == 'mock_id'
        assert outputs.get('username') == 'mock_user_name'
        assert outputs.get('details', {}).get('first_name') == 'mock_first_name'
        assert outputs.get('details', {}).get('last_name') == 'mock_last_name'

    def test_get_user_command__non_existing_user(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains an email a user
        When:
            - The user does not exist in the application
            - Calling function get_user_command
        Then:
            - Ensure the resulted User Profile object holds information about an unsuccessful result.
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com'}}

        mocker.patch.object(client, 'get_user', return_value=None)

        user_profile = IAMCommand().get_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.GET_USER
        assert outputs.get('success') is False
        assert outputs.get('errorCode') == IAMErrors.USER_DOES_NOT_EXIST[0]
        assert outputs.get('errorMessage') == IAMErrors.USER_DOES_NOT_EXIST[1]

    def test_get_user_command__bad_response(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains an email of a non-existing user in the application
        When:
            - Calling function get_user_command
            - A bad response (500) is returned from the application API
        Then:
            - Ensure the resulted User Profile object holds information about the bad response.
        """
        import demistomock as demisto

        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com'}}

        bad_response = Response()
        bad_response.headers = {'x-redlock-status': json.dumps([{'i18nKey': 'bad request'}])}
        bad_response.status_code = 500

        mocker.patch.object(demisto, 'error')
        mocker.patch.object(Session, 'request', return_value=bad_response)

        user_profile = IAMCommand().get_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.GET_USER
        assert outputs.get('success') is False
        assert outputs.get('errorCode') == 500
        assert 'Error in API call [500] - bad request' in outputs.get('errorMessage')


class TestCreateUserCommand:
    """
    Class to group the create user commands test
    """

    def test_create_user_command__success(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains an email of a non-existing user in the application
        When:
            - Calling function create_user_command
        Then:
            - Ensure a User Profile object with the user data is returned
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com'}}

        mocker.patch.object(client, 'get_user', return_value=None)
        mocker.patch.object(IAMUserProfile, 'map_object', return_value={})
        mocker.patch.object(client, 'create_user', return_value=USER_APP_DATA)

        user_profile = IAMCommand().create_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.CREATE_USER
        assert outputs.get('success') is True
        assert outputs.get('active') is True
        assert outputs.get('id') == 'mock_id'
        assert outputs.get('username') == 'mock_user_name'
        assert outputs.get('details', {}).get('first_name') == 'mock_first_name'
        assert outputs.get('details', {}).get('last_name') == 'mock_last_name'

    def test_create_user_command__user_already_exists(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains an email of a user
        When:
            - The user already exists in the application and disabled
            - allow-enable argument is false
            - Calling function create_user_command
        Then:
            - Ensure the command is considered successful and the user is still disabled
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com'}, 'allow-enable': 'false'}

        mocker.patch.object(client, 'get_user', return_value=DISABLED_USER_APP_DATA)
        mocker.patch.object(client, 'update_user', return_value=DISABLED_USER_APP_DATA)

        user_profile = IAMCommand().create_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.UPDATE_USER
        assert outputs.get('success') is True
        assert outputs.get('active') is False
        assert outputs.get('id') == 'mock_id'
        assert outputs.get('username') == 'mock_user_name'
        assert outputs.get('details', {}).get('first_name') == 'mock_first_name'
        assert outputs.get('details', {}).get('last_name') == 'mock_last_name'


class TestUpdateUserCommand:
    """
    Class to group the update user commands test
    """

    def test_update_user_command__non_existing_user(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains user data
        When:
            - The user does not exist in the application
            - create-if-not-exists parameter is checked
            - Create User command is enabled
            - Calling function update_user_command
        Then:
            - Ensure the create action is executed
            - Ensure a User Profile object with the user data is returned
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com', 'givenname': 'mock_first_name'}}

        mocker.patch.object(client, 'get_user', return_value=None)
        mocker.patch.object(IAMUserProfile, 'map_object', return_value={})
        mocker.patch.object(client, 'create_user', return_value=USER_APP_DATA)

        user_profile = IAMCommand(create_if_not_exists=True).update_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.CREATE_USER
        assert outputs.get('success') is True
        assert outputs.get('active') is True
        assert outputs.get('id') == 'mock_id'
        assert outputs.get('username') == 'mock_user_name'
        assert outputs.get('details', {}).get('first_name') == 'mock_first_name'
        assert outputs.get('details', {}).get('last_name') == 'mock_last_name'

    def test_update_user_command__command_is_disabled(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains user data
        When:
            - Update User command is disabled
            - Calling function update_user_command
        Then:
            - Ensure the command is considered successful and skipped
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com', 'givenname': 'mock_first_name'}}

        mocker.patch.object(client, 'get_user', return_value=None)
        mocker.patch.object(IAMUserProfile, 'map_object', return_value={})
        mocker.patch.object(client, 'update_user', return_value=USER_APP_DATA)

        user_profile = IAMCommand(is_update_enabled=False).update_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.UPDATE_USER
        assert outputs.get('success') is True
        assert outputs.get('skipped') is True
        assert outputs.get('reason') == 'Command is disabled.'

    def test_update_user_command__allow_enable(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains user data
        When:
            - The user is disabled in the application
            - allow-enable argument is true
            - Calling function update_user_command
        Then:
            - Ensure the user is enabled at the end of the command execution.
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com', 'givenname': 'mock_first_name'},
                'allow-enable': 'true'}

        mocker.patch.object(client, 'get_user', return_value=DISABLED_USER_APP_DATA)
        mocker.patch.object(IAMUserProfile, 'map_object', return_value={})
        mocker.patch.object(client, 'enable_user', return_value=USER_APP_DATA)
        mocker.patch.object(client, 'update_user', return_value=USER_APP_DATA)

        user_profile = IAMCommand().update_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.UPDATE_USER
        assert outputs.get('success') is True
        assert outputs.get('active') is True
        assert outputs.get('id') == 'mock_id'
        assert outputs.get('username') == 'mock_user_name'
        assert outputs.get('details', {}).get('first_name') == 'mock_first_name'
        assert outputs.get('details', {}).get('last_name') == 'mock_last_name'


class TestDisableUserTest:
    """
    Class to group the disable user commands test
    """
    def test_disable_user_command__non_existing_user(self, mocker):
        """
        Given:
            - An app client object
            - A user-profile argument that contains an email of a user
        When:
            - create-if-not-exists parameter is unchecked
            - The user does not exist in the application
            - Calling function disable_user_command
        Then:
            - Ensure the command is considered successful and skipped
        """
        client = mock_client(mocker)
        args = {'user-profile': {'email': 'testdemisto2@paloaltonetworks.com'}}

        mocker.patch.object(client, 'get_user', return_value=None)

        user_profile = IAMCommand().disable_user(client, args)
        outputs = get_outputs_from_user_profile(user_profile)

        assert outputs.get('action') == IAMActions.DISABLE_USER
        assert outputs.get('success') is True
        assert outputs.get('skipped') is True
        assert outputs.get('reason') == IAMErrors.USER_DOES_NOT_EXIST[1]


class TestGetMappingFieldsTest:
    """
    Class to group the disable user commands test
    """
    def test_get_mapping_fields_command(self, mocker):
        """
        Given:
            - An app client object
        When:
            - User schema in the application contains the fields 'field1' and 'field2'
            - Calling function get_mapping_fields_command
        Then:
            - Ensure a GetMappingFieldsResponse object that contains the application fields is returned
        """
        client = mock_client(mocker)
        mocker.patch.object(client, 'get_app_fields', return_value={'field1': 'desc1', 'field2': 'desc2'})

        mapping_response = get_mapping_fields(client)
        mapping = mapping_response.extract_mapping()

        assert mapping.get(IAMUserProfile.DEFAULT_INCIDENT_TYPE, {}).get('field1') == 'desc1'
        assert mapping.get(IAMUserProfile.DEFAULT_INCIDENT_TYPE, {}).get('field2') == 'desc2'
