import aiohttp
import pytest

pytest_plugins = "smb.common.aiotvm.pytest.plugin"


class LasagnaPytestPluginError(Exception):
    pass


class TestClient(aiohttp.test_utils.TestClient):
    async def get(self, path, *args, **kwargs):
        return await self.request("GET", path, *args, **kwargs)

    async def post(self, path, *args, **kwargs):
        return await self.request("POST", path, *args, **kwargs)

    async def put(self, path, *args, **kwargs):
        return await self.request("PUT", path, *args, **kwargs)

    async def patch(self, path, *args, **kwargs):
        return await self.request("PATCH", path, *args, **kwargs)

    async def delete(self, path, *args, **kwargs):
        return await self.request("DELETE", path, *args, **kwargs)

    async def request(self, method, path, *args, **kwargs):
        expected_status = kwargs.pop("expected_status", None)
        decode_as = kwargs.pop("decode_as", None)
        as_response = kwargs.pop("as_response", False)

        if "proto" in kwargs:
            proto = kwargs.pop("proto")
            kwargs["data"] = proto.SerializeToString()

        response = await super().request(method, path, *args, **kwargs)

        if expected_status:
            assert response.status == expected_status

        if as_response:
            result = response
        elif decode_as:
            result = decode_as.FromString(await response.read())
        elif response.content_type == "application/json":
            result = await response.json()
        else:
            result = await response.read()

        return result


@pytest.fixture
def loop(event_loop):
    return event_loop


@pytest.fixture
def _lasagna_db(app, request):
    db = None

    if app.SWIM_ENGINE_CLS is not None:
        db = request.getfixturevalue("db")

    yield db


@pytest.fixture
async def api(loop, app, aiotvm, _lasagna_db):
    api = await app.setup(_lasagna_db)

    server = aiohttp.test_utils.TestServer(api, loop=loop)
    client = TestClient(server, loop=loop)

    await client.start_server()
    yield client
    await client.close()
