# This file is part of the CERN Indico plugins.
# Copyright (C) 2014 - 2022 CERN
#
# The CERN Indico plugins are free software; you can redistribute
# them and/or modify them under the terms of the MIT License; see
# the LICENSE file for more details.

from unittest.mock import MagicMock

import pytest
from requests.exceptions import HTTPError, Timeout

from indico.testing.util import extract_logs

from indico_ravem.plugin import RavemPlugin
from indico_ravem.util import has_access, ravem_api_call


@pytest.mark.usefixtures('db')
@pytest.mark.parametrize('method', ('get', 'post'))
def test_correct_http_method(mocker, method):
    request = mocker.patch('indico_ravem.util.requests.request')
    response = MagicMock()
    response.json.return_value = {'result': 'test'}
    response.raise_for_status.return_value = False
    request.return_value = response

    ravem_api_call('test_endpoint', method=method, param1='test1', param2='test2')

    assert request.call_count == 1
    assert request.call_args[0][0] == method


@pytest.mark.usefixtures('db')
def test_correct_auth_method(mocker):
    request = mocker.patch('indico_ravem.util.requests.request')
    response = MagicMock()
    response.json.return_value = {'result': 'test'}
    response.raise_for_status.return_value = False
    request.return_value = response

    token = 'foo'
    RavemPlugin.settings.set('access_token', token)
    ravem_api_call('test_endpoint', param1='test1', param2='test2')

    assert request.call_count == 1
    assert 'Authorization' in request.call_args[1]['headers']
    assert request.call_args[1]['headers']['Authorization'] == 'Bearer %s' % token


@pytest.mark.usefixtures('db')
def test_accepts_json(mocker):
    request = mocker.patch('indico_ravem.util.requests.request')
    response = MagicMock()
    response.json.return_value = {'result': 'test'}
    response.raise_for_status.return_value = False
    request.return_value = response

    ravem_api_call('test_endpoint', param1='test1', param2='test2')

    assert request.call_count == 1
    assert request.call_args[1]['headers']['Accept'] == 'application/json'


@pytest.mark.usefixtures('db')
@pytest.mark.parametrize(('root_endpoint', 'endpoint', 'expected_url'), (
    ('https://ravem.test/', 'final_endpoint', 'https://ravem.test/final_endpoint'),
    ('https://ravem.test/api/', 'final_endpoint', 'https://ravem.test/api/final_endpoint'),
    ('https://ravem.test/api/v2/', 'final_endpoint', 'https://ravem.test/api/v2/final_endpoint'),
    ('https://ravem.test', './final_endpoint', 'https://ravem.test/final_endpoint'),
    ('https://ravem.test/api/', './final_endpoint', 'https://ravem.test/api/final_endpoint'),
    ('https://ravem.test/api/v2/', './final_endpoint', 'https://ravem.test/api/v2/final_endpoint'),
    ('https://ravem.test', 'sub/final_endpoint', 'https://ravem.test/sub/final_endpoint'),
    ('https://ravem.test/api/', 'sub/final_endpoint', 'https://ravem.test/api/sub/final_endpoint'),
    ('https://ravem.test/api/v2/', 'sub/final_endpoint', 'https://ravem.test/api/v2/sub/final_endpoint'),
    ('https://ravem.test', './sub/final_endpoint', 'https://ravem.test/sub/final_endpoint'),
    ('https://ravem.test/api/', './sub/final_endpoint', 'https://ravem.test/api/sub/final_endpoint'),
    ('https://ravem.test/api/v2/', './sub/final_endpoint', 'https://ravem.test/api/v2/sub/final_endpoint'),
    ('https://ravem.test/', '', 'https://ravem.test/'),
    ('https://ravem.test/api/', '', 'https://ravem.test/api/'),
    ('https://ravem.test/api/v2/', '', 'https://ravem.test/api/v2/'),
))
def test_correct_api_endpoint(mocker, root_endpoint, endpoint, expected_url):
    request = mocker.patch('indico_ravem.util.requests.request')
    response = MagicMock()
    response.json.return_value = {'result': 'test'}
    response.raise_for_status.return_value = False
    request.return_value = response

    RavemPlugin.settings.set('api_endpoint', root_endpoint)
    ravem_api_call(endpoint, param1='test1', param2='test2')

    assert request.call_count == 1
    assert request.call_args[0][1] == expected_url


@pytest.mark.usefixtures('db')
@pytest.mark.parametrize('params', (
    {},
    {'p1': '1stparam'},
    {'p1': '1stparam', 'p2': '2ndparam'}
))
def test_params_generated(mocker, params):
    request = mocker.patch('indico_ravem.util.requests.request')
    response = MagicMock()
    response.json.return_value = {'result': 'test'}
    response.raise_for_status.return_value = False
    request.return_value = response

    ravem_api_call('test_endpoint', params=params)

    assert request.call_count == 1
    assert request.call_args[1]['params'] == params


@pytest.mark.usefixtures('db')
def test_raises_timeout(mocker):
    request = mocker.patch('indico_ravem.util.requests.request')
    request.side_effect = Timeout('Timeout test error message', request=request)

    with pytest.raises(Timeout) as excinfo:
        ravem_api_call('test_endpoint')

    assert str(excinfo.value) == "Timeout while contacting the room."
    assert request.call_count == 1


@pytest.mark.usefixtures('db')
@pytest.mark.parametrize(('method', 'params'), (
    ('get', {}),
    ('post', {}),
    ('get', {'p1': '1stparam'}),
    ('post', {'p1': '1stparam'}),
    ('get', {'p1': '1stparam', 'p2': '2ndparam'}),
    ('post', {'p1': '1stparam', 'p2': '2ndparam'})
))
def test_unexpected_exception_is_logged(mocker, caplog, method, params):
    request = mocker.patch('indico_ravem.util.requests.request')
    request.side_effect = IndexError('this is unexpected')

    with pytest.raises(IndexError) as excinfo:
        ravem_api_call('test_endpoint', method=method, **params)

    assert str(excinfo.value) == 'this is unexpected'
    log = extract_logs(caplog, one=True, name='indico.plugin.ravem')
    assert log.message == "failed call: {} {} with {}: {}".format(method.upper(), 'test_endpoint', params,
                                                                  'this is unexpected')
    assert request.call_count == 1


@pytest.mark.usefixtures('db')
@pytest.mark.parametrize(('method', 'params'), (
    ('get', {}),
    ('post', {}),
    ('get', {'p1': '1stparam'}),
    ('post', {'p1': '1stparam'}),
    ('get', {'p1': '1stparam', 'p2': '2ndparam'}),
    ('post', {'p1': '1stparam', 'p2': '2ndparam'})
))
def test_http_error_is_logged(mocker, caplog, method, params):
    request = mocker.patch('indico_ravem.util.requests.request')
    request.method = method.upper()
    request.url = RavemPlugin.settings.get('api_endpoint') + 'test_endpoint'
    response = MagicMock()
    response.raise_for_status.side_effect = HTTPError('Well this is embarrassing')
    response.request = request
    response.url = response.request.url
    request.return_value = response

    with pytest.raises(HTTPError) as excinfo:
        ravem_api_call('test_endpoint', method=method, **params)

    assert str(excinfo.value) == 'Well this is embarrassing'
    log = extract_logs(caplog, one=True, name='indico.plugin.ravem')
    assert log.message == '{} {} failed with {}'.format(
        method.upper(), RavemPlugin.settings.get('api_endpoint') + 'test_endpoint', 'Well this is embarrassing')

    assert request.call_count == 1


@pytest.mark.usefixtures('db')
def test_unlinked_event_vc_room_has_no_access():
    event_vc_room = MagicMock()
    event_vc_room.link_object = None

    assert not has_access(event_vc_room)


@pytest.mark.usefixtures('db', 'request_context')
def test_unlinked_room_has_no_access(mocker):
    session = mocker.patch('indico_ravem.util.session')
    session.user = 'Guinea Pig'

    event_vc_room = MagicMock()
    event_vc_room.link_object.room = None

    assert not has_access(event_vc_room)


@pytest.mark.usefixtures('db', 'request_context')
def test_check_if_current_user_is_room_owner(mocker):
    session = mocker.patch('indico_ravem.util.session')
    session.user = 'Guinea Pig'
    request = mocker.patch('indico_ravem.util.request')
    request.remote_addr = '111.222.123.123'
    retrieve_principal = mocker.patch('indico_ravem.util._retrieve_principal')
    retrieve_principal.side_effect = lambda x: session.user

    event_vc_room = MagicMock()
    event_vc_room.link_object.room.has_equipment = MagicMock(return_value=True)
    event_vc_room.link_object.room.get_attribute_value.return_value = request.remote_addr
    event_vc_room.vc_room.data.get.return_value = 'User:123'
    event_vc_room.event.can_manage.return_value = False

    assert has_access(event_vc_room)


@pytest.mark.usefixtures('db', 'request_context')
def test_check_if_current_user_can_modify(mocker):
    request = mocker.patch('indico_ravem.util.request')
    request.remote_addr = '111.222.123.123'
    session = mocker.patch('indico_ravem.util.session')
    session.user = 'Guinea Pig'
    mocker.patch('indico_ravem.util._retrieve_principal')

    event_vc_room = MagicMock()
    event_vc_room.link_object.room.has_equipment = MagicMock(return_value=True)
    event_vc_room.link_object.room.get_attribute_value.return_value = request.remote_addr
    event_vc_room.event.can_manage.return_value = True

    assert has_access(event_vc_room)
    event_vc_room.event.can_manage.assert_called_once_with(session.user)

[(19, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (20, 'pytest.mark.parametrize', 'pytest.mark.parametrize', False, 'import pytest\n'), (34, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (51, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (65, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (66, 'pytest.mark.parametrize', 'pytest.mark.parametrize', False, 'import pytest\n'), (97, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (98, 'pytest.mark.parametrize', 'pytest.mark.parametrize', False, 'import pytest\n'), (116, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (128, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (129, 'pytest.mark.parametrize', 'pytest.mark.parametrize', False, 'import pytest\n'), (151, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (152, 'pytest.mark.parametrize', 'pytest.mark.parametrize', False, 'import pytest\n'), (181, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (189, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (200, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (218, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', False, 'import pytest\n'), (23, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (28, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (37, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (43, 'indico_ravem.plugin.RavemPlugin.settings.set', 'RavemPlugin.settings.set', False, 'from indico_ravem.plugin import RavemPlugin\n'), (44, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (54, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (59, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (85, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (90, 'indico_ravem.plugin.RavemPlugin.settings.set', 'RavemPlugin.settings.set', False, 'from indico_ravem.plugin import RavemPlugin\n'), (91, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (105, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (110, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (119, 'requests.exceptions.Timeout', 'Timeout', False, 'from requests.exceptions import HTTPError, Timeout\n'), (145, 'indico.testing.util.extract_logs', 'extract_logs', False, 'from indico.testing.util import extract_logs\n'), (164, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (165, 'requests.exceptions.HTTPError', 'HTTPError', False, 'from requests.exceptions import HTTPError, Timeout\n'), (174, 'indico.testing.util.extract_logs', 'extract_logs', False, 'from indico.testing.util import extract_logs\n'), (183, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (194, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (209, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (210, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (215, 'indico_ravem.util.has_access', 'has_access', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (226, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (227, 'unittest.mock.MagicMock', 'MagicMock', False, 'from unittest.mock import MagicMock\n'), (231, 'indico_ravem.util.has_access', 'has_access', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (121, 'pytest.raises', 'pytest.raises', False, 'import pytest\n'), (122, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (141, 'pytest.raises', 'pytest.raises', False, 'import pytest\n'), (142, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (163, 'indico_ravem.plugin.RavemPlugin.settings.get', 'RavemPlugin.settings.get', False, 'from indico_ravem.plugin import RavemPlugin\n'), (170, 'pytest.raises', 'pytest.raises', False, 'import pytest\n'), (171, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (186, 'indico_ravem.util.has_access', 'has_access', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (197, 'indico_ravem.util.has_access', 'has_access', False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (176, 'indico_ravem.plugin.RavemPlugin.settings.get', 'RavemPlugin.settings.get', False, 'from indico_ravem.plugin import RavemPlugin\n')]