import asyncio
import io
from unittest.mock import patch

import pytest

from peony import BasePeonyClient, iterators, requests
from peony.api import APIPath

from . import dummy

url = "http://whatever.com/endpoint.json"


@pytest.fixture
def api_path():
    client = BasePeonyClient("", "", loop=asyncio.get_event_loop())
    with patch.object(client, "request", side_effect=dummy):
        yield APIPath(["http://whatever.com", "endpoint"], ".json", client)


@pytest.fixture
def peony_request(api_path):
    return requests.Request(api_path, "get")


def test_sanitize_params(peony_request):
    val = [1, 2, 3]
    kwargs, skip_params = peony_request.sanitize_params(
        "get", _test=1, boom=0, test=True, val=val, a=None, text="aaa"
    )

    assert kwargs == {
        "test": 1,
        "params": {"boom": "0", "test": "true", "val": "1,2,3", "text": "aaa"},
    }
    assert skip_params is False


def test_sanitize_params_skip(peony_request):
    data = io.BytesIO(b"test")
    kwargs, skip_params = peony_request.sanitize_params("post", _test=1, boom=data)

    assert kwargs == {"test": 1, "data": {"boom": data}}
    assert skip_params is True


def test_skip_params(api_path):
    client = api_path.client
    with patch.object(client, "request", side_effect=dummy) as client_request:
        request = requests.Request(api_path, "get", _skip_params=False)
        client.loop.run_until_complete(request)
        client_request.assert_called_with(
            method="get", skip_params=False, url=api_path.url(), future=request
        )

        client_request.reset_mock()
        request = requests.Request(api_path, "get", _skip_params=True)
        client.loop.run_until_complete(request)
        client_request.assert_called_with(
            method="get", skip_params=True, url=api_path.url(), future=request
        )


def test_error_handling(api_path):
    client = api_path.client
    with patch.object(client, "request", side_effect=dummy):
        request = requests.Request(api_path, "get", _error_handling=False)
        with patch.object(
            client, "error_handler", side_effect=dummy_error_handler
        ) as error_handler:
            client.loop.run_until_complete(request)
            assert not error_handler.called


def test_get_params(peony_request):
    kwargs, skip_params, req_url = peony_request._get_params(_test=1, test=2)

    assert kwargs == {"test": 1, "params": {"test": "2"}}
    assert skip_params is False
    assert url == req_url


def test_get_iterator(peony_request):
    assert isinstance(peony_request.iterator.with_cursor(), iterators.CursorIterator)
    assert isinstance(peony_request.iterator.with_max_id(), iterators.MaxIdIterator)
    assert isinstance(peony_request.iterator.with_since_id(), iterators.SinceIdIterator)


def test_get_iterator_from_factory(api_path):
    factory = requests.RequestFactory(api_path, "get")
    assert isinstance(factory.iterator.with_cursor(), iterators.CursorIterator)
    assert isinstance(factory.iterator.with_max_id(), iterators.MaxIdIterator)


def test_iterator_params_from_factory(api_path):
    factory = requests.RequestFactory(api_path, "get")
    iterator = factory.iterator.with_since_id(_force=False)
    assert isinstance(iterator, iterators.SinceIdIterator)
    assert iterator.force is False
    iterator = factory.iterator.with_since_id(_force=True)
    assert isinstance(iterator, iterators.SinceIdIterator)
    assert iterator.force is True


def test_request_call(peony_request):
    assert isinstance(peony_request(), requests.Request)


def test_iterator_unknown_iterator(peony_request):
    with pytest.raises(AttributeError):
        peony_request.iterator.whatchamacallit()


def dummy_error_handler(peony_request):
    return peony_request


def test_request_error_handler(api_path, _error_handler=True):
    client = api_path.client
    with patch.object(client, "request", side_effect=dummy) as client_request:
        with patch.object(
            client, "error_handler", side_effect=dummy_error_handler
        ) as error_handler:
            client.loop.run_until_complete(
                requests.Request(
                    api_path, "get", _error_handling=_error_handler, test=1, _test=2
                )
            )
            assert client_request.called_with(
                method="get", url=url, skip_params=False, test=2, params={"test": 1}
            )
            assert error_handler.called is _error_handler


def test_request_no_error_handler(api_path):
    test_request_error_handler(api_path, _error_handler=False)


def test_streaming_request(api_path):
    streaming_request = requests.StreamingRequest(api_path, "get")

    with patch.object(streaming_request.api.client, "stream_request") as client_request:
        streaming_request(test=1, _test=2)
        assert client_request.called_with(
            method="get", url=url, skip_params=False, test=1, params={"test": 2}
        )
