import pytest

from rest_framework import viewsets

from funkwhale_api.common import decorators
from funkwhale_api.common import models
from funkwhale_api.common import mutations
from funkwhale_api.common import serializers
from funkwhale_api.common import signals
from funkwhale_api.common import tasks
from funkwhale_api.music import models as music_models
from funkwhale_api.music import licenses


class V(viewsets.ModelViewSet):
    queryset = music_models.Track.objects.all()
    mutations = decorators.mutations_route(types=["update"])
    permission_classes = []


def test_mutations_route_list(factories, api_request):
    track = factories["music.Track"]()
    mutation = factories["common.Mutation"](target=track, type="update", payload="")
    factories["common.Mutation"](target=track, type="noop", payload="")

    view = V.as_view({"get": "mutations"})
    expected = {
        "next": None,
        "previous": None,
        "count": 1,
        "results": [serializers.APIMutationSerializer(mutation).data],
    }

    request = api_request.get("/")
    response = view(request, pk=track.pk)

    assert response.status_code == 200
    assert response.data == expected


@pytest.mark.parametrize("is_approved", [False, True])
def test_mutations_route_create_success(factories, api_request, is_approved, mocker):
    licenses.load(licenses.LICENSES)
    on_commit = mocker.patch("funkwhale_api.common.utils.on_commit")
    user = factories["users.User"](permission_library=True)
    actor = user.create_actor()
    track = factories["music.Track"](title="foo", local=True)
    view = V.as_view({"post": "mutations"})

    request = api_request.post(
        "/",
        {
            "type": "update",
            "payload": {"title": "bar", "unknown": "test", "license": "cc-by-nc-4.0"},
            "summary": "hello",
            "is_approved": is_approved,
        },
        format="json",
    )
    setattr(request, "user", user)
    setattr(request, "session", {})
    response = view(request, pk=track.pk)

    assert response.status_code == 201

    mutation = models.Mutation.objects.get_for_target(track).latest("id")

    assert mutation.type == "update"
    assert mutation.payload == {"title": "bar", "license": "cc-by-nc-4.0"}
    assert mutation.created_by == actor
    assert mutation.is_approved is is_approved
    assert mutation.is_applied is None
    assert mutation.target == track
    assert mutation.summary == "hello"

    if is_approved:
        on_commit.assert_any_call(tasks.apply_mutation.delay, mutation_id=mutation.pk)
    expected = serializers.APIMutationSerializer(mutation).data
    assert response.data == expected
    on_commit.assert_any_call(
        signals.mutation_created.send, mutation=mutation, sender=None
    )


def test_mutations_route_create_no_auth(factories, api_request):
    track = factories["music.Track"](title="foo")
    view = V.as_view({"post": "mutations"})

    request = api_request.post("/", {}, format="json")
    response = view(request, pk=track.pk)

    assert response.status_code == 401


@pytest.mark.parametrize("is_approved", [False, True])
def test_mutations_route_create_no_perm(factories, api_request, mocker, is_approved):
    track = factories["music.Track"](title="foo")
    view = V.as_view({"post": "mutations"})
    user = factories["users.User"]()
    actor = user.create_actor()
    has_perm = mocker.patch.object(mutations.registry, "has_perm", return_value=False)
    request = api_request.post(
        "/",
        {
            "type": "update",
            "payload": {"title": "bar", "unknown": "test"},
            "summary": "hello",
            "is_approved": is_approved,
        },
        format="json",
    )
    setattr(request, "user", user)
    setattr(request, "session", {})
    response = view(request, pk=track.pk)

    assert response.status_code == 403
    has_perm.assert_called_once_with(
        actor=actor,
        obj=track,
        type="update",
        perm="approve" if is_approved else "suggest",
    )
