import pytest
from django.contrib.auth.models import AnonymousUser
from django.db.models import Q

from funkwhale_api.common import fields
from funkwhale_api.favorites import models as favorite_models
from funkwhale_api.history import models


def test_privacy_level_query(factories):
    user = factories["users.User"](with_actor=True)
    user_query = (
        Q(privacy_level__in=["instance", "everyone"])
        | Q(privacy_level="me", user=user)
        | Q(
            privacy_level="followers",
            user__actor__in=user.actor.get_approved_followings(),
        )
        | Q(user__isnull=True)
    )

    query = fields.privacy_level_query(user)
    assert str(query) == str(user_query)

    user = AnonymousUser()
    user_query = Q(privacy_level="everyone")
    query = fields.privacy_level_query(user)
    assert str(query) == str(user_query)


def test_privacy_level_query_followers(factories):
    user = factories["users.User"](with_actor=True)
    target = factories["users.User"](with_actor=True, privacy_level="followers")
    userfollow = factories["federation.Follow"](
        actor=user.actor, target=target.actor, approved=True
    )
    assert user.actor.get_approved_followings()[0] == target.actor

    listening = factories["history.Listening"](actor=userfollow.target)
    favorite = factories["favorites.TrackFavorite"](actor=userfollow.target)
    factories["history.Listening"]()
    factories["favorites.TrackFavorite"]()
    factories["favorites.TrackFavorite"]()
    queryset = models.Listening.objects.all().filter(
        fields.privacy_level_query(user, "actor__user__privacy_level", "actor__user")
    )
    fav_qs = favorite_models.TrackFavorite.objects.all().filter(
        fields.privacy_level_query(user, "actor__user__privacy_level", "actor__user")
    )

    assert listening in queryset
    assert favorite in fav_qs


def test_privacy_level_query_not_followers(factories):
    user = factories["users.User"](with_actor=True)
    target = factories["users.User"](privacy_level="followers")
    target.create_actor()
    target.refresh_from_db()

    userfollow = factories["federation.Follow"](target=target.actor, approved=True)
    listening = factories["history.Listening"](actor=userfollow.target)
    favorite = factories["favorites.TrackFavorite"](actor=userfollow.target)

    factories["history.Listening"]()
    factories["history.Listening"]()
    factories["favorites.TrackFavorite"]()
    factories["favorites.TrackFavorite"]()

    queryset = models.Listening.objects.all().filter(
        fields.privacy_level_query(user, "actor__user__privacy_level", "actor__user")
    )
    fav_qs = favorite_models.TrackFavorite.objects.all().filter(
        fields.privacy_level_query(user, "actor__user__privacy_level", "actor__user")
    )

    assert listening not in queryset
    assert favorite not in fav_qs


def test_generic_relation_field(factories):
    obj = factories["users.User"]()
    f = fields.GenericRelation(
        {
            "user": {
                "queryset": obj.__class__.objects.all(),
                "id_attr": "username",
                "id_field": fields.serializers.CharField(),
            }
        }
    )

    data = {"type": "user", "username": obj.username}

    assert f.to_internal_value(data) == obj


@pytest.mark.parametrize(
    "payload, expected_error",
    [
        ({}, r".*Invalid data.*"),
        (1, r".*Invalid data.*"),
        (False, r".*Invalid data.*"),
        ("test", r".*Invalid data.*"),
        ({"missing": "type"}, r".*Invalid type.*"),
        ({"type": "noop"}, r".*Invalid type.*"),
        ({"type": "user"}, r".*Invalid username.*"),
        ({"type": "user", "username": {}}, r".*Invalid username.*"),
        ({"type": "user", "username": "not_found"}, r".*Object not found.*"),
    ],
)
def test_generic_relation_field_validation_error(payload, expected_error, factories):
    obj = factories["users.User"]()
    f = fields.GenericRelation(
        {
            "user": {
                "queryset": obj.__class__.objects.all(),
                "id_attr": "username",
                "id_field": fields.serializers.CharField(),
            }
        }
    )

    with pytest.raises(fields.serializers.ValidationError, match=expected_error):
        f.to_internal_value(payload)


def test_generic_relation_filter_target_type(factories):
    user = factories["users.User"]()
    note = factories["moderation.Note"](target=user)
    factories["moderation.Note"](target=factories["music.Artist"]())
    f = fields.GenericRelationFilter(
        "target",
        {
            "user": {
                "queryset": user.__class__.objects.all(),
                "id_attr": "username",
                "id_field": fields.serializers.CharField(),
            }
        },
    )
    qs = f.filter(note.__class__.objects.all(), "user")
    assert list(qs) == [note]


def test_generic_relation_filter_target_type_and_id(factories):
    user = factories["users.User"]()
    note = factories["moderation.Note"](target=user)
    factories["moderation.Note"](target=factories["users.User"]())
    f = fields.GenericRelationFilter(
        "target",
        {
            "user": {
                "queryset": user.__class__.objects.all(),
                "id_attr": "username",
                "id_field": fields.serializers.CharField(),
            }
        },
    )
    qs = f.filter(note.__class__.objects.all(), f"user:{user.username}")
    assert list(qs) == [note]
