# The MIT License (MIT)
#
# Copyright (c) 2016-2020 Frederic Guillot
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import unittest
from unittest import mock
import types
import warnings

import kanboard


class TestClient(unittest.TestCase):

    def setUp(self):
        self.url = 'some api url'
        self.client = kanboard.Client(self.url, 'username', 'password')
        self.request, self.urlopen = self._create_mocks()

    def ignore_warnings(test_func):
        def do_test(self, *args, **kwargs):
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                test_func(self, *args, **kwargs)
        return do_test

    def test_api_call(self):
        body = b'{"jsonrpc": "2.0", "result": true, "id": 123}'
        self.urlopen.return_value.read.return_value = body
        self.assertEqual(True, self.client.remote_procedure())
        self.request.assert_called_once_with(self.url,
                                             data=mock.ANY,
                                             headers=mock.ANY)

    def test_custom_auth_header(self):
        self.client._auth_header = 'X-Auth-Header'
        body = b'{"jsonrpc": "2.0", "result": true, "id": 123}'
        self.urlopen.return_value.read.return_value = body
        self.assertEqual(True, self.client.remote_procedure())
        self.request.assert_called_once_with(self.url,
                                             data=mock.ANY,
                                             headers=mock.ANY)
        _, kwargs = self.request.call_args
        assert kwargs['headers']['X-Auth-Header'] == 'dXNlcm5hbWU6cGFzc3dvcmQ='

    def test_http_error(self):
        self.urlopen.side_effect = Exception()
        with self.assertRaises(kanboard.ClientError):
            self.client.remote_procedure()

    def test_application_error(self):
        body = b'{"jsonrpc": "2.0", "error": {"code": -32603, "message": "Internal error"}, "id": 123}'
        self.urlopen.return_value.read.return_value = body

        with self.assertRaises(kanboard.ClientError, msg='Internal error'):
            self.client.remote_procedure()

    def test_async_method_call_recognised(self):
        method_name = "some_method_async"
        result = self.client.is_async_method_name(method_name)
        self.assertTrue(result)

    def test_standard_method_call_recognised(self):
        method_name = "some_method"
        result = self.client.is_async_method_name(method_name)
        self.assertFalse(result)

    def test_method_name_extracted_from_async_name(self):
        expected_method_name = "some_method"
        async_method_name = expected_method_name + "_async"
        result = self.client.get_funcname_from_async_name(async_method_name)
        self.assertEqual(expected_method_name, result)

    # suppress a RuntimeWarning because coro is not awaited
    # this is done on purpose
    @ignore_warnings
    def test_async_call_generates_coro(self):
        method = self.client.my_method_async()
        self.assertIsInstance(method, types.CoroutineType)

    @staticmethod
    def _create_mocks():
        request_patcher = mock.patch('urllib.request.Request')
        urlopen_patcher = mock.patch('urllib.request.urlopen')
        return request_patcher.start(), urlopen_patcher.start()