"""
Tests for cards services.
"""

from django.http import Http404
from django.db.models import Q

import pytest
from django_mock_queries.query import MockSet

import cards.services
from cards.models import Card
from users.models import User
from cards.services import get_cards_with_rating
from cards.services import get_default_card_with_rating


@pytest.mark.django_db()
def test_get_cards_with_rating():
    user = User.objects.create(username="test", email="test@example.com")
    card = Card.objects.create(user=user, image="bla bla bla")

    cards = get_cards_with_rating()

    assert list(cards) == [card]


@pytest.mark.django_db()
def test_get_cards_with_rating_prefetch_data(mocker):
    user = User.objects.create(username="test", email="test@example.com")
    card = Card.objects.create(user=user, image="bla bla bla")
    related_mock = mocker.patch(
        "django.db.models.query.prefetch_related_objects",
        MockSet(),
    )

    list(get_cards_with_rating())

    related_mock.assert_called_once_with(
        [card],
        "user",
        "ratings",
    )


@pytest.mark.django_db()
def test_get_cards_with_rating_calculation(mocker):
    user = User.objects.create(username="test", email="test@example.com")
    Card.objects.create(user=user, image="bla bla bla")
    avg_spy = mocker.spy(cards.services, "Avg")

    cards_list = get_cards_with_rating()
    cards_values = cards_list.values()[0]

    assert "rating" in cards_values
    avg_spy.assert_called_once_with("ratings__value")


@pytest.mark.django_db()
def test_get_cards_with_rating_count_ratings(mocker):
    user = User.objects.create(username="test", email="test@example.com")
    Card.objects.create(user=user, image="bla bla bla")
    count_spy = mocker.spy(cards.services, "Count")

    cards_list = get_cards_with_rating()
    cards_values = cards_list.values()[0]

    assert "rating_count" in cards_values
    count_spy.assert_called_once_with(
        "ratings",
        filter=Q(ratings__value__isnull=False),
    )


@pytest.mark.django_db()
def test_get_default_card(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    card = Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=True,
    )
    Card.objects.create(
        user=user,
        name="non default",
        image="bla bla bla",
        default=False,
    )

    card_object = get_default_card_with_rating(username)

    assert card_object == card


@pytest.mark.django_db()
def test_get_default_card_filters_by_username(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=True,
    )

    with pytest.raises(Http404):
        get_default_card_with_rating("")


@pytest.mark.django_db()
def test_get_default_card_multiple_defaults(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=True,
    )
    Card.objects.create(
        user=user,
        name="another default",
        image="bla bla bla",
        default=True,
    )

    with pytest.raises(Card.MultipleObjectsReturned):
        get_default_card_with_rating(username)


@pytest.mark.django_db()
def test_get_default_card_not_found(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=False,
    )

    with pytest.raises(Http404):
        get_default_card_with_rating(username)


@pytest.mark.django_db()
def test_get_default_card_prefetch_data(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    card = Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=True,
    )
    related_mock = mocker.patch(
        "django.db.models.query.prefetch_related_objects",
        MockSet(),
    )

    get_default_card_with_rating(username)

    related_mock.assert_called_once_with([card], "user", "ratings")


@pytest.mark.django_db()
def test_get_default_card_rating_calculation(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=True,
    )
    avg_spy = mocker.spy(cards.services, "Avg")

    card_object = get_default_card_with_rating(username)

    assert hasattr(card_object, "rating")
    avg_spy.assert_called_once_with("ratings__value")


@pytest.mark.django_db()
def test_get_default_card_count_ratings(mocker):
    username = "test"
    user = User.objects.create(username=username, email="test@example.com")
    Card.objects.create(
        user=user,
        name="default",
        image="bla bla bla",
        default=True,
    )
    count_spy = mocker.spy(cards.services, "Count")

    card_object = get_default_card_with_rating(username)

    assert hasattr(card_object, "rating_count")
    count_spy.assert_called_once_with(
        "ratings",
        filter=Q(ratings__value__isnull=False),
    )
