import datetime

import pytest
from django.db.models.aggregates import Count

from funkwhale_api.music import models as music_models
from funkwhale_api.subsonic import renderers, serializers


@pytest.mark.parametrize(
    "date, expected",
    [
        (datetime.datetime(2017, 1, 12, 9, 53, 12, 1890), "2017-01-12T09:53:12.000Z"),
        (None, None),
    ],
)
def test_to_subsonic_date(date, expected):
    assert serializers.to_subsonic_date(date) == expected


@pytest.mark.parametrize(
    "input, expected",
    [
        ("AC/DC", "AC_DC"),
        ("AC-DC", "AC-DC"),
        ("A" * 100, "A" * 50),
        ("Album (2019)", "Album (2019)"),
        ("Haven't", "Haven_t"),
    ],
)
def test_get_valid_filepart(input, expected):
    assert serializers.get_valid_filepart(input) == expected


@pytest.mark.parametrize(
    "factory_kwargs, suffix, expected",
    [
        (
            {
                "artist_credit__artist__name": "Hello",
                "album__title": "World",
                "title": "foo",
                "position": None,
            },
            "mp3",
            "Hello/World/foo.mp3",
        ),
        (
            {
                "artist_credit__artist__name": "AC/DC",
                "album__title": "escape/my",
                "title": "sla/sh",
                "position": 12,
            },
            "ogg",
            "/".join(
                [
                    serializers.get_valid_filepart("AC/DC"),
                    serializers.get_valid_filepart("escape/my"),
                ]
            )
            + "/12 - {}.ogg".format(serializers.get_valid_filepart("sla/sh")),
        ),
    ],
)
def test_get_track_path(factory_kwargs, suffix, expected, factories):
    track = factories["music.Track"](**factory_kwargs)
    assert serializers.get_track_path(track, suffix) == expected


def test_get_artists_serializer(factories):
    artist1 = factories["music.Artist"](name="eliot", with_cover=True)
    artist2 = factories["music.Artist"](name="Ellena", with_cover=True)
    artist3 = factories["music.Artist"](name="Rilay", with_cover=True)
    artist4 = factories["music.Artist"](
        name="", with_cover=False
    )  # Shouldn't be serialised

    factories["music.Album"].create_batch(size=3, artist_credit__artist=artist1)
    factories["music.Album"].create_batch(size=2, artist_credit__artist=artist2)

    expected = {
        "ignoredArticles": "",
        "index": [
            {
                "name": "E",
                "artist": [
                    {
                        "id": artist1.pk,
                        "name": artist1.name,
                        "albumCount": 3,
                        "coverArt": f"ar-{artist1.id}",
                        "musicBrainzId": artist1.mbid,
                    },
                    {
                        "id": artist2.pk,
                        "name": artist2.name,
                        "albumCount": 2,
                        "coverArt": f"ar-{artist2.id}",
                        "musicBrainzId": artist2.mbid,
                    },
                ],
            },
            {
                "name": "R",
                "artist": [
                    {
                        "id": artist3.pk,
                        "name": artist3.name,
                        "albumCount": 0,
                        "coverArt": f"ar-{artist3.id}",
                        "musicBrainzId": artist3.mbid,
                    }
                ],
            },
        ],
    }

    queryset = artist1.__class__.objects.filter(
        pk__in=[artist1.pk, artist2.pk, artist3.pk, artist4.pk]
    )

    assert serializers.GetArtistsSerializer(queryset).data == expected


def test_get_artist_serializer(factories):
    artist = factories["music.Artist"](with_cover=True)
    album = factories["music.Album"](artist_credit__artist=artist, with_cover=True)
    tracks = factories["music.Track"].create_batch(size=3, album=album)

    expected = {
        "id": artist.pk,
        "name": artist.name,
        "albumCount": 1,
        "coverArt": f"ar-{artist.id}",
        "album": [
            {
                "id": album.pk,
                "coverArt": f"al-{album.id}",
                "artistId": artist.pk,
                "name": album.title,
                "artist": artist.name,
                "songCount": len(tracks),
                "created": serializers.to_subsonic_date(album.creation_date),
                "year": album.release_date.year,
            }
        ],
    }

    assert serializers.GetArtistSerializer(artist).data == expected


def test_get_artist_info_2_serializer(factories):
    content = factories["common.Content"]()
    artist = factories["music.Artist"](with_cover=True, description=content)

    expected = {
        "musicBrainzId": artist.mbid,
        "mediumImageUrl": renderers.TagValue(
            artist.attachment_cover.download_url_medium_square_crop
        ),
        "largeImageUrl": renderers.TagValue(
            artist.attachment_cover.download_url_large_square_crop
        ),
        "biography": renderers.TagValue(artist.description.rendered),
    }

    assert serializers.GetArtistInfo2Serializer(artist).data == expected


@pytest.mark.parametrize(
    "mimetype, extension, expected",
    [
        ("audio/ogg", "noop", "audio/ogg"),
        ("", "ogg", "audio/ogg"),
        ("", "mp3", "audio/mpeg"),
        ("", "", "audio/mpeg"),
    ],
)
def test_get_track_data_content_type(mimetype, extension, expected, factories):
    upload = factories["music.Upload"]()
    upload.mimetype = mimetype
    upload.audio_file = f"test.{extension}"

    data = serializers.get_track_data(
        album=upload.track.album, track=upload.track, upload=upload
    )

    assert data["contentType"] == expected


def test_get_album_serializer(factories):
    artist = factories["music.Artist"]()
    album = factories["music.Album"](artist_credit__artist=artist, with_cover=True)
    track = factories["music.Track"](album=album, disc_number=42)
    upload = factories["music.Upload"](track=track, bitrate=42000, duration=43, size=44)
    tagged_item = factories["tags.TaggedItem"](content_object=album, tag__name="foo")
    expected = {
        "id": album.pk,
        "artistId": artist.pk,
        "name": album.title,
        "artist": artist.name,
        "songCount": 1,
        "created": serializers.to_subsonic_date(album.creation_date),
        "year": album.release_date.year,
        "coverArt": f"al-{album.id}",
        "genre": tagged_item.tag.name,
        "genres": [{"name": tagged_item.tag.name}],
        "mediaType": "album",
        "musicBrainzId": album.mbid,
        "duration": 43,
        "playCount": album.tracks.aggregate(l=Count("listenings"))["l"] or 0,
        "song": [
            {
                "id": track.pk,
                "isDir": "false",
                "title": track.title,
                "coverArt": f"al-{album.id}",
                "album": album.title,
                "artist": artist.name,
                "track": track.position,
                "discNumber": track.disc_number,
                "year": track.album.release_date.year,
                "contentType": upload.mimetype,
                "suffix": upload.extension or "",
                "path": serializers.get_track_path(track, upload.extension),
                "bitRate": 42,
                "duration": 43,
                "size": 44,
                "created": serializers.to_subsonic_date(track.creation_date),
                "albumId": album.pk,
                "artistId": artist.pk,
                "type": "music",
                "mediaType": "song",
                "musicBrainzId": track.mbid,
            }
        ],
    }

    qs = album.__class__.objects.with_duration()

    assert serializers.GetAlbumSerializer(qs.first()).data == expected


def test_starred_tracks2_serializer(factories):
    artist_credit = factories["music.ArtistCredit"]()
    album = factories["music.Album"](artist_credit=artist_credit)
    track = factories["music.Track"](album=album)
    upload = factories["music.Upload"](track=track)
    favorite = factories["favorites.TrackFavorite"](track=track)
    expected = [serializers.get_track_data(album, track, upload)]
    expected[0]["starred"] = serializers.to_subsonic_date(favorite.creation_date)
    data = serializers.get_starred_tracks_data([favorite])
    assert data == expected


def test_get_album_list2_serializer(factories):
    album1 = factories["music.Album"]().__class__.objects.with_duration().first()
    album2 = factories["music.Album"]().__class__.objects.with_duration().last()

    qs = music_models.Album.objects.with_tracks_count().with_duration().order_by("pk")
    expected = [
        serializers.get_album2_data(album1),
        serializers.get_album2_data(album2),
    ]
    data = serializers.get_album_list2_data(qs)
    assert data == expected


def test_playlist_serializer(factories):
    plt = factories["playlists.PlaylistTrack"]()
    playlist = plt.playlist
    factories["users.User"](actor=playlist.actor)
    qs = music_models.Album.objects.with_tracks_count().order_by("pk")
    expected = {
        "id": playlist.pk,
        "name": playlist.name,
        "owner": playlist.actor.user.username,
        "public": "false",
        "songCount": 1,
        "duration": 0,
        "created": serializers.to_subsonic_date(playlist.creation_date),
    }
    qs = playlist.__class__.objects.with_tracks_count()
    data = serializers.get_playlist_data(qs.first())
    assert data == expected


def test_playlist_detail_serializer(factories):
    plt = factories["playlists.PlaylistTrack"]()
    upload = factories["music.Upload"](track=plt.track)
    playlist = plt.playlist
    factories["users.User"](actor=playlist.actor)
    qs = music_models.Album.objects.with_tracks_count().order_by("pk")
    expected = {
        "id": playlist.pk,
        "name": playlist.name,
        "owner": playlist.actor.user.username,
        "public": "false",
        "songCount": 1,
        "duration": 0,
        "created": serializers.to_subsonic_date(playlist.creation_date),
        "entry": [serializers.get_track_data(plt.track.album, plt.track, upload)],
    }
    qs = playlist.__class__.objects.with_tracks_count()
    data = serializers.get_playlist_detail_data(qs.first())
    assert data == expected


def test_scrobble_serializer(factories):
    upload = factories["music.Upload"]()
    track = upload.track
    user = factories["users.User"](with_actor=True)
    payload = {"id": track.pk, "submission": True}
    serializer = serializers.ScrobbleSerializer(data=payload, context={"user": user})

    assert serializer.is_valid(raise_exception=True)

    listening = serializer.save()

    assert listening.actor.user == user
    assert listening.track == track


def test_channel_serializer(factories):
    description = factories["common.Content"]()
    channel = factories["audio.Channel"](
        external=True, artist__description=description, artist__with_cover=True
    )
    upload = factories["music.Upload"](
        playable=True, library=channel.library, duration=42
    )

    expected = {
        "id": str(channel.uuid),
        "url": channel.rss_url,
        "title": channel.artist.name,
        "description": description.as_plain_text,
        "coverArt": f"at-{channel.artist.attachment_cover.uuid}",
        "originalImageUrl": channel.artist.attachment_cover.url,
        "status": "completed",
        "episode": [serializers.get_channel_episode_data(upload, channel.uuid)],
    }
    data = serializers.get_channel_data(channel, [upload])
    assert data == expected


def test_channel_episode_serializer(factories):
    description = factories["common.Content"]()
    channel = factories["audio.Channel"]()
    track = factories["music.Track"](
        description=description, artist_credit__artist=channel.artist, with_cover=True
    )
    upload = factories["music.Upload"](
        playable=True, track=track, bitrate=128000, duration=42
    )

    expected = {
        "id": str(upload.uuid),
        "channelId": str(channel.uuid),
        "streamId": upload.track.id,
        "title": track.title,
        "description": description.as_plain_text,
        "coverArt": f"at-{track.attachment_cover.uuid}",
        "isDir": "false",
        "year": track.creation_date.year,
        "created": track.creation_date.isoformat(),
        "publishDate": track.creation_date.isoformat(),
        "genre": "Podcast",
        "size": upload.size,
        "duration": upload.duration,
        "bitRate": upload.bitrate / 1000,
        "contentType": upload.mimetype,
        "suffix": upload.extension,
        "status": "completed",
    }
    data = serializers.get_channel_episode_data(upload, channel.uuid)
    assert data == expected
