# -*- coding: utf-8 -*-
from homehub_backend.lib import rpc


class Test_PostRPC:
    def _valid_call(self, **kwargs):
        result = {
            'jsonrpc': '2.0',
            'id': 'testing',
            'method': 'testing.greet',
        }
        result.update(kwargs)

        return result

    async def test_parse_error(self, homehub_client):
        response = await homehub_client.post('/backend/rpc', data=b'spam')
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCParseError.ERROR_CODE

    async def test_empty_call(self, homehub_client):
        response = await homehub_client.post('/backend/rpc', json={})
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidRequestError.ERROR_CODE

    async def test_empty_batch(self, homehub_client):
        response = await homehub_client.post('/backend/rpc', json=[])
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidRequestError.ERROR_CODE

    async def test_call_not_dict(self, homehub_client):
        response = await homehub_client.post('/backend/rpc', json='"spam"')
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidRequestError.ERROR_CODE

    async def test_call_without_jsonrpc(self, homehub_client):
        call = self._valid_call()
        call.pop('jsonrpc')

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidRequestError.ERROR_CODE

    async def test_call_without_method(self, homehub_client):
        call = self._valid_call()
        call.pop('method')

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidRequestError.ERROR_CODE

    async def test_method_not_found(self, homehub_client):
        call = self._valid_call()
        call['method'] = 'testing.idontexist'

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCMethodNotFoundError.ERROR_CODE

    async def test_invalid_params(self, homehub_client):
        call = self._valid_call()
        call['params'] = 'spam'

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidParamsError.ERROR_CODE
        assert 'data' not in data['error']

    async def test_call_with_method_params_error(self, homehub_client):
        call = self._valid_call()

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInvalidParamsError.ERROR_CODE
        assert data['error']['data'] == (
            "greet() missing 1 required positional argument: 'who'"
        )

    async def test_method_exception(self, homehub_client):
        call = self._valid_call()
        call['method'] = 'testing.fial'

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'result' not in data
        assert data['error']['code'] == rpc.JSONRPCInternalErrorError.ERROR_CODE
        assert data['error']['data'] == 'FIAL'

    async def test_call_with_args(self, homehub_client):
        call = self._valid_call()
        call['params'] = ['aiohttp-jsonrpc']

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'error' not in data
        assert data['result'] == 'Hello, aiohttp-jsonrpc!'

    async def test_call_with_kwargs(self, homehub_client):
        call = self._valid_call()
        call['params'] = {
            'who': 'aiohttp-jsonrpc',
            'how': 'Hi,',
        }

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'error' not in data
        assert data['result'] == 'Hi, aiohttp-jsonrpc!'

    async def test_result(self, homehub_client):
        call = self._valid_call()
        call['params'] = ['aiohttp-jsonrpc']

        response = await homehub_client.post('/backend/rpc', json=call)
        assert response.status == 200

        data = await response.json()
        assert 'error' not in data
        assert data['jsonrpc'] == call['jsonrpc']
        assert data['id'] == call['id']
        assert data['method'] == call['method']

    async def test_batch(self, homehub_client):
        call_without_id = self._valid_call(params=['World'])
        call_without_id.pop('id')

        batch = [
            self._valid_call(params=['aiohttp-jsonrpc']),
            self._valid_call(id='testing2', method='testing.fial', params=[]),
            call_without_id,
        ]

        response = await homehub_client.post('/backend/rpc', json=batch)
        assert response.status == 200

        data = await response.json()
        assert isinstance(data, list) is True
        assert len(data) == 2

        first_result, second_result = data

        assert first_result['id'] == batch[0]['id']
        assert 'error' not in first_result
        assert first_result['result'] == 'Hello, aiohttp-jsonrpc!'

        assert second_result['id'] == batch[1]['id']
        assert 'result' not in second_result
        assert second_result['error']['code'] == rpc.JSONRPCInternalErrorError.ERROR_CODE
        assert second_result['error']['data'] == 'FIAL'
