# 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', (['"""db"""'], {}), False, 'import pytest\n'), (20, 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""method"""', "('get', 'post')"], {}), False, 'import pytest\n'), (34, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (51, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (65, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (66, 'pytest.mark.parametrize', 'pytest.mark.parametrize', (["('root_endpoint', 'endpoint', 'expected_url')", "(('https://ravem.test/', 'final_endpoint',\n    'https://ravem.test/final_endpoint'), ('https://ravem.test/api/',\n    'final_endpoint', 'https://ravem.test/api/final_endpoint'), (\n    'https://ravem.test/api/v2/', 'final_endpoint',\n    'https://ravem.test/api/v2/final_endpoint'), ('https://ravem.test',\n    './final_endpoint', 'https://ravem.test/final_endpoint'), (\n    'https://ravem.test/api/', './final_endpoint',\n    'https://ravem.test/api/final_endpoint'), ('https://ravem.test/api/v2/',\n    './final_endpoint', 'https://ravem.test/api/v2/final_endpoint'), (\n    'https://ravem.test', 'sub/final_endpoint',\n    'https://ravem.test/sub/final_endpoint'), ('https://ravem.test/api/',\n    'sub/final_endpoint', 'https://ravem.test/api/sub/final_endpoint'), (\n    'https://ravem.test/api/v2/', 'sub/final_endpoint',\n    'https://ravem.test/api/v2/sub/final_endpoint'), ('https://ravem.test',\n    './sub/final_endpoint', 'https://ravem.test/sub/final_endpoint'), (\n    'https://ravem.test/api/', './sub/final_endpoint',\n    'https://ravem.test/api/sub/final_endpoint'), (\n    'https://ravem.test/api/v2/', './sub/final_endpoint',\n    'https://ravem.test/api/v2/sub/final_endpoint'), ('https://ravem.test/',\n    '', 'https://ravem.test/'), ('https://ravem.test/api/', '',\n    'https://ravem.test/api/'), ('https://ravem.test/api/v2/', '',\n    'https://ravem.test/api/v2/'))"], {}), False, 'import pytest\n'), (97, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (98, 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""params"""', "({}, {'p1': '1stparam'}, {'p1': '1stparam', 'p2': '2ndparam'})"], {}), False, 'import pytest\n'), (116, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (128, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (129, 'pytest.mark.parametrize', 'pytest.mark.parametrize', (["('method', 'params')", "(('get', {}), ('post', {}), ('get', {'p1': '1stparam'}), ('post', {'p1':\n    '1stparam'}), ('get', {'p1': '1stparam', 'p2': '2ndparam'}), ('post', {\n    'p1': '1stparam', 'p2': '2ndparam'}))"], {}), False, 'import pytest\n'), (151, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (152, 'pytest.mark.parametrize', 'pytest.mark.parametrize', (["('method', 'params')", "(('get', {}), ('post', {}), ('get', {'p1': '1stparam'}), ('post', {'p1':\n    '1stparam'}), ('get', {'p1': '1stparam', 'p2': '2ndparam'}), ('post', {\n    'p1': '1stparam', 'p2': '2ndparam'}))"], {}), False, 'import pytest\n'), (181, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""'], {}), False, 'import pytest\n'), (189, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""', '"""request_context"""'], {}), False, 'import pytest\n'), (200, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""', '"""request_context"""'], {}), False, 'import pytest\n'), (218, 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', (['"""db"""', '"""request_context"""'], {}), 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', (['"""test_endpoint"""'], {'method': 'method', 'param1': '"""test1"""', 'param2': '"""test2"""'}), 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', (['"""access_token"""', 'token'], {}), False, 'from indico_ravem.plugin import RavemPlugin\n'), (44, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', (['"""test_endpoint"""'], {'param1': '"""test1"""', 'param2': '"""test2"""'}), 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', (['"""test_endpoint"""'], {'param1': '"""test1"""', 'param2': '"""test2"""'}), 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', (['"""api_endpoint"""', 'root_endpoint'], {}), False, 'from indico_ravem.plugin import RavemPlugin\n'), (91, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', (['endpoint'], {'param1': '"""test1"""', 'param2': '"""test2"""'}), 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', (['"""test_endpoint"""'], {'params': 'params'}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (119, 'requests.exceptions.Timeout', 'Timeout', (['"""Timeout test error message"""'], {'request': 'request'}), False, 'from requests.exceptions import HTTPError, Timeout\n'), (145, 'indico.testing.util.extract_logs', 'extract_logs', (['caplog'], {'one': '(True)', 'name': '"""indico.plugin.ravem"""'}), 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', (['"""Well this is embarrassing"""'], {}), False, 'from requests.exceptions import HTTPError, Timeout\n'), (174, 'indico.testing.util.extract_logs', 'extract_logs', (['caplog'], {'one': '(True)', 'name': '"""indico.plugin.ravem"""'}), 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', ([], {'return_value': '(True)'}), False, 'from unittest.mock import MagicMock\n'), (215, 'indico_ravem.util.has_access', 'has_access', (['event_vc_room'], {}), 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', ([], {'return_value': '(True)'}), False, 'from unittest.mock import MagicMock\n'), (231, 'indico_ravem.util.has_access', 'has_access', (['event_vc_room'], {}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (121, 'pytest.raises', 'pytest.raises', (['Timeout'], {}), False, 'import pytest\n'), (122, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', (['"""test_endpoint"""'], {}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (141, 'pytest.raises', 'pytest.raises', (['IndexError'], {}), False, 'import pytest\n'), (142, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', (['"""test_endpoint"""'], {'method': 'method'}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (163, 'indico_ravem.plugin.RavemPlugin.settings.get', 'RavemPlugin.settings.get', (['"""api_endpoint"""'], {}), False, 'from indico_ravem.plugin import RavemPlugin\n'), (170, 'pytest.raises', 'pytest.raises', (['HTTPError'], {}), False, 'import pytest\n'), (171, 'indico_ravem.util.ravem_api_call', 'ravem_api_call', (['"""test_endpoint"""'], {'method': 'method'}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (186, 'indico_ravem.util.has_access', 'has_access', (['event_vc_room'], {}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (197, 'indico_ravem.util.has_access', 'has_access', (['event_vc_room'], {}), False, 'from indico_ravem.util import has_access, ravem_api_call\n'), (176, 'indico_ravem.plugin.RavemPlugin.settings.get', 'RavemPlugin.settings.get', (['"""api_endpoint"""'], {}), False, 'from indico_ravem.plugin import RavemPlugin\n')]