import json
import pytest
import urllib.parse

from django.core.serializers.json import DjangoJSONEncoder

from funkwhale_api.common import utils as common_utils
from funkwhale_api.federation import models as federation_models
from funkwhale_api.moderation import serializers
from funkwhale_api.moderation import signals


def test_user_filter_serializer_repr(factories):
    artist = factories["music.Artist"]()
    content_filter = factories["moderation.UserFilter"](target_artist=artist)

    expected = {
        "uuid": str(content_filter.uuid),
        "target": {"type": "artist", "id": artist.pk, "name": artist.name},
        "creation_date": content_filter.creation_date.isoformat().replace(
            "+00:00", "Z"
        ),
    }

    serializer = serializers.UserFilterSerializer(content_filter)

    assert serializer.data == expected


def test_user_filter_serializer_save(factories):
    artist = factories["music.Artist"]()
    user = factories["users.User"]()
    data = {"target": {"type": "artist", "id": artist.pk}}

    serializer = serializers.UserFilterSerializer(data=data)
    serializer.is_valid(raise_exception=True)
    content_filter = serializer.save(user=user)

    assert content_filter.target_artist == artist


@pytest.mark.parametrize(
    "factory_name, target_type, id_field, state_serializer",
    [
        ("music.Artist", "artist", "id", serializers.ArtistStateSerializer),
        ("music.Album", "album", "id", serializers.AlbumStateSerializer),
        ("music.Track", "track", "id", serializers.TrackStateSerializer),
        ("music.Library", "library", "uuid", serializers.LibraryStateSerializer),
        (
            "federation.Actor",
            "account",
            "full_username",
            serializers.ActorStateSerializer,
        ),
        ("audio.Channel", "channel", "uuid", serializers.ChannelStateSerializer),
    ],
)
def test_report_federated_entity_serializer_save(
    factory_name, target_type, id_field, state_serializer, factories, mocker, settings
):
    target = factories[factory_name]()
    target_owner = factories["federation.Actor"]()
    submitter = factories["federation.Actor"]()
    target_data = {"type": target_type, id_field: getattr(target, id_field)}
    payload = {
        "summary": "Report content",
        "type": "illegal_content",
        "target": target_data,
    }
    serializer = serializers.ReportSerializer(
        data=payload, context={"submitter": submitter}
    )
    get_target_owner = mocker.patch.object(
        serializers, "get_target_owner", return_value=target_owner
    )
    assert serializer.is_valid(raise_exception=True) is True

    report = serializer.save()

    expected_state = state_serializer(target).data
    expected_state["_target"] = json.loads(
        json.dumps(target_data, cls=DjangoJSONEncoder)
    )
    expected_state["domain"] = urllib.parse.urlparse(target.fid).hostname
    expected_state["is_local"] = (
        expected_state["domain"] == settings.FEDERATION_HOSTNAME
    )

    assert report.target == target
    assert report.type == payload["type"]
    assert report.summary == payload["summary"]
    assert report.target_state == expected_state
    assert report.target_owner == target_owner
    get_target_owner.assert_called_once_with(target)


@pytest.mark.parametrize(
    "factory_name, target_type, id_field, state_serializer",
    [("playlists.Playlist", "playlist", "id", serializers.PlaylistStateSerializer)],
)
def test_report_local_entity_serializer_save(
    factory_name, target_type, id_field, state_serializer, factories, mocker, settings
):
    target = factories[factory_name]()
    target_owner = factories["federation.Actor"]()
    submitter = factories["federation.Actor"]()
    target_data = {"type": target_type, id_field: getattr(target, id_field)}
    payload = {
        "summary": "Report content",
        "type": "illegal_content",
        "target": target_data,
    }
    serializer = serializers.ReportSerializer(
        data=payload, context={"submitter": submitter}
    )
    get_target_owner = mocker.patch.object(
        serializers, "get_target_owner", return_value=target_owner
    )
    assert serializer.is_valid(raise_exception=True) is True

    report = serializer.save()

    expected_state = state_serializer(target).data
    expected_state["_target"] = json.loads(
        json.dumps(target_data, cls=DjangoJSONEncoder)
    )
    expected_state["is_local"] = True
    assert report.target == target
    assert report.type == payload["type"]
    assert report.summary == payload["summary"]
    assert report.target_state == expected_state
    assert report.target_owner == target_owner
    get_target_owner.assert_called_once_with(target)


def test_report_serializer_save_anonymous(factories, mocker):
    target = factories["music.Artist"]()
    payload = {
        "summary": "Report content",
        "type": "illegal_content",
        "target": {"type": "artist", "id": target.pk},
        "submitter_email": "test@submitter.example",
    }
    serializer = serializers.ReportSerializer(data=payload)

    assert serializer.is_valid(raise_exception=True) is True

    report = serializer.save()

    assert report.target == target
    assert report.type == payload["type"]
    assert report.summary == payload["summary"]
    assert report.submitter_email == payload["submitter_email"]


@pytest.mark.parametrize(
    "factory_name, factory_kwargs, owner_field",
    [
        ("music.Artist", {"attributed": True}, "attributed_to"),
        ("music.Album", {"attributed": True}, "attributed_to"),
        ("music.Track", {"attributed": True}, "attributed_to"),
        ("music.Library", {}, "actor"),
        ("playlists.Playlist", {"user__with_actor": True}, "user.actor"),
        ("federation.Actor", {}, "self"),
        ("audio.Channel", {}, "attributed_to"),
    ],
)
def test_get_target_owner(factory_name, factory_kwargs, owner_field, factories):
    target = factories[factory_name](**factory_kwargs)
    if owner_field == "self":
        expected_owner = target
    else:
        expected_owner = common_utils.recursive_getattr(target, owner_field)

    assert isinstance(expected_owner, federation_models.Actor)
    assert serializers.get_target_owner(target) == expected_owner


def test_report_serializer_repr(factories, to_api_date):
    target = factories["music.Artist"]()
    report = factories["moderation.Report"](target=target)
    expected = {
        "uuid": str(report.uuid),
        "summary": report.summary,
        "type": report.type,
        "target": {"type": "artist", "id": target.pk},
        "creation_date": to_api_date(report.creation_date),
        "handled_date": None,
        "is_handled": False,
        "submitter_email": None,
    }
    serializer = serializers.ReportSerializer(report)
    assert serializer.data == expected


@pytest.mark.parametrize(
    "preference, context, payload, is_valid",
    [
        # anonymous reports not enabled for the category
        (
            ["illegal_content"],
            {},
            {"type": "other", "submitter_email": "hello@example.test"},
            False,
        ),
        # anonymous reports enabled for the category, but invalid e-mail
        (["other"], {}, {"type": "other", "submitter_email": "hello@"}, False),
        # anonymous reports enabled for the category, no e-mail
        (["other"], {}, {"type": "other"}, False),
        # anonymous reports enabled for the category, actor object is empty
        (["other"], {"submitter": None}, {"type": "other"}, False),
        # valid examples
        (
            ["other"],
            {},
            {"type": "other", "submitter_email": "hello@example.test"},
            True,
        ),
    ],
)
def test_report_serializer_save_unauthenticated_validation(
    preference, context, payload, is_valid, factories, preferences
):
    preferences["moderation__unauthenticated_report_types"] = preference
    target = factories["music.Artist"]()
    target_data = {"type": "artist", "id": target.id}
    payload["summary"] = "Test"
    payload["target"] = target_data
    serializer = serializers.ReportSerializer(data=payload, context=context)
    assert serializer.is_valid() is is_valid


def test_report_create_send_websocket_event(factories, mocker):
    target = factories["music.Artist"]()
    group_send = mocker.patch("funkwhale_api.common.channels.group_send")
    report_created = mocker.spy(signals.report_created, "send")
    payload = {
        "summary": "Report content",
        "type": "illegal_content",
        "target": {"type": "artist", "id": target.pk},
        "submitter_email": "test@submitter.example",
    }
    serializer = serializers.ReportSerializer(data=payload)

    assert serializer.is_valid(raise_exception=True) is True
    report = serializer.save()
    report_created.assert_called_once_with(sender=None, report=report)
    group_send.assert_called_with(
        "admin.moderation",
        {
            "type": "event.send",
            "text": "",
            "data": {
                "type": "report.created",
                "report": serializer.data,
                "unresolved_count": 1,
            },
        },
    )
