import pytest

from sansio_tools.queue import BytesQueue, StringQueue, NeedMoreItemsError


def test_string_queue():
    q = StringQueue()

    q.append("0123")
    assert len(q) == 4

    assert str(q.popleft(1)) == "0"
    assert str(q.popleft(1)) == "1"

    q.appendleft("ab")
    assert q.popleft(4) == "ab23"


def _s(q: BytesQueue):
    return b"".join(q.data)


def _a(q: BytesQueue):
    xs = [bytes(x) for x in q.data]
    assert len(q) == sum(len(x) for x in xs)
    return xs


def test_queue_popleft():
    q = BytesQueue((b"012", b"345"))
    assert bytes(q.popleft(1)) == b"0"
    assert _a(q) == [b"12", b"345"]
    assert bytes(q.popleft(3)) == b"123"
    assert _a(q) == [b"45"]


def test_queue_pop():
    q = BytesQueue((b"012", b"345"))
    assert bytes(q.pop(1)) == b"5"
    assert _a(q) == [b"012", b"34"]
    assert bytes(q.pop(3)) == b"234"
    assert _a(q) == [b"01"]


def test_queue_pop_to():
    q = BytesQueue((b"012", b"345"))
    q2 = BytesQueue()
    q.pop_to(1, q2)
    assert _a(q) == [b"012", b"34"]
    assert _a(q2) == [b"5"]
    q.pop_to(4, q2)
    assert _a(q) == [b"0"]
    assert _a(q2) == [b"12", b"34", b"5"]


def test_queue_popleft_to():
    q = BytesQueue((b"012", b"345"))
    q2 = BytesQueue()
    q.popleft_to(1, q2)
    assert _a(q) == [b"12", b"345"]
    assert _a(q2) == [b"0"]
    q.popleft_to(4, q2)
    assert _a(q) == [b"5"]
    assert _a(q2) == [b"0", b"12", b"34"]


@pytest.mark.parametrize("exception", [False, True])
def test_queue_temporary_left(exception):
    q = BytesQueue((b"012", b"345"))
    try:
        with q.temporary_left() as tmp:
            q.popleft_to(4, tmp)
            assert _a(tmp) == [b"012", b"3"]
            tmp.pop(1)
            tmp.popleft(1)
            assert _a(tmp) == [b"12"]
            assert _a(q) == [b"45"]
            if exception:
                raise RuntimeError
    except RuntimeError:
        pass
    assert _a(q) == [b"12", b"45"]
    assert _a(tmp) == []


def test_bytes_queue():
    q = BytesQueue()
    assert len(q) == 0
    assert not q

    q.append(b"0123")
    assert len(q) == 4
    assert bool(q)

    q.append(b"456")
    assert len(q) == 7
    assert _s(q) == b"0123456"

    assert bytes(q.peekleft(1)) == b"0"
    assert len(q.peekleft_any()) == 1
    assert bytes(q.peekleft(3)) == b"012"
    assert len(q.peekleft_any()) == 3

    assert bytes(q.popleft(2)) == b"01"
    assert len(q) == 5
    assert _s(q) == b"23456"

    q.appendleft(b"ab")
    assert len(q) == 7
    assert _s(q) == b"ab23456"

    assert bytes(q.peekleft(4)) == b"ab23"

    assert bytes(q.popleft_any()) == b"ab23"
    assert len(q) == 3
    assert _s(q) == b"456"

    with q.popleft_after(2) as b:
        assert bytes(b) == b"45"
        assert len(q) == 3
        assert _s(q) == b"456"
    assert len(q) == 1
    assert _s(q) == b"6"

    q.append(b"789")
    assert len(q) == 4
    assert _s(q) == b"6789"

    try:
        with q.popleft_after(2) as b:
            assert bytes(b) == b"67"
            assert len(q) == 4
            raise RuntimeError
    except RuntimeError:
        pass
    assert len(q) == 4

    with pytest.raises(NeedMoreItemsError):
        q.popleft(5)

    with pytest.raises(NeedMoreItemsError):
        with q.popleft_after(5) as b:
            pass

    assert len(q) == 4
