import os
from urllib.parse import urlparse

import factory
from django.conf import settings

from funkwhale_api.common import factories as common_factories
from funkwhale_api.factories import NoUpdateOnCreate, registry
from funkwhale_api.federation import factories as federation_factories
from funkwhale_api.music import licenses
from funkwhale_api.tags import factories as tags_factories
from funkwhale_api.users import factories as users_factories

SAMPLES_PATH = os.path.join(
    os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
    "tests",
    "music",
)


def playable_factory(field):
    @factory.post_generation
    def inner(self, create, extracted, **kwargs):
        if not create:
            return

        if extracted:
            UploadFactory(
                library__privacy_level="everyone",
                import_status="finished",
                **{field: self}
            )

    return inner


def deduce_from_conf(field):
    @factory.lazy_attribute
    def inner(self):
        return licenses.LICENSES_BY_ID[self.code][field]

    return inner


@registry.register
class LicenseFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
    code = "cc-by-4.0"
    url = deduce_from_conf("url")
    commercial = deduce_from_conf("commercial")
    redistribute = deduce_from_conf("redistribute")
    copyleft = deduce_from_conf("copyleft")
    attribution = deduce_from_conf("attribution")
    derivative = deduce_from_conf("derivative")

    class Meta:
        model = "music.License"
        django_get_or_create = ("code",)


@registry.register
class ArtistFactory(
    tags_factories.TaggableFactory, NoUpdateOnCreate, factory.django.DjangoModelFactory
):
    name = factory.Faker("name")
    mbid = factory.Faker("uuid4")
    fid = factory.Faker("federation_url")
    playable = playable_factory("track__album__artist_credit__artist")

    class Meta:
        model = "music.Artist"

    class Params:
        attributed = factory.Trait(
            attributed_to=factory.SubFactory(federation_factories.ActorFactory)
        )
        local = factory.Trait(fid=factory.Faker("federation_url", local=True))
        with_cover = factory.Trait(
            attachment_cover=factory.SubFactory(common_factories.AttachmentFactory)
        )


@registry.register
class ArtistCreditFactory(factory.django.DjangoModelFactory):
    artist = factory.SubFactory(ArtistFactory)
    credit = factory.LazyAttribute(lambda obj: obj.artist.name)
    joinphrase = ""

    class Meta:
        model = "music.ArtistCredit"

    class Params:
        local = factory.Trait(
            artist=factory.SubFactory(ArtistFactory, local=True),
        )


@registry.register
class AlbumFactory(
    tags_factories.TaggableFactory, NoUpdateOnCreate, factory.django.DjangoModelFactory
):
    title = factory.Faker("sentence", nb_words=3)
    mbid = factory.Faker("uuid4")
    release_date = factory.Faker("date_object")
    release_group_id = factory.Faker("uuid4")
    fid = factory.Faker("federation_url")
    playable = playable_factory("track__album")

    class Meta:
        model = "music.Album"

    class Params:
        attributed = factory.Trait(
            attributed_to=factory.SubFactory(federation_factories.ActorFactory)
        )
        local = factory.Trait(
            fid=factory.Faker("federation_url", local=True),
        )
        with_cover = factory.Trait(
            attachment_cover=factory.SubFactory(common_factories.AttachmentFactory)
        )

    @factory.post_generation
    def artist_credit(self, create, extracted, **kwargs):
        if urlparse(self.fid).netloc == settings.FEDERATION_HOSTNAME:
            kwargs["artist__local"] = True
        if extracted:
            self.artist_credit.add(extracted)
        if create:
            self.artist_credit.add(ArtistCreditFactory(**kwargs))


@registry.register
class TrackFactory(
    tags_factories.TaggableFactory, NoUpdateOnCreate, factory.django.DjangoModelFactory
):
    uuid = factory.Faker("uuid4")
    fid = factory.Faker("federation_url")
    title = factory.Faker("sentence", nb_words=3)
    mbid = factory.Faker("uuid4")
    album = factory.SubFactory(AlbumFactory)
    position = 1
    playable = playable_factory("track")

    class Meta:
        model = "music.Track"

    class Params:
        attributed = factory.Trait(
            attributed_to=factory.SubFactory(federation_factories.ActorFactory)
        )

        local = factory.Trait(
            fid=factory.Faker(
                "federation_url",
                local=True,
                prefix="/federation/music/tracks",
                obj_uuid=factory.SelfAttribute("..uuid"),
            ),
            album__local=True,
        )
        with_cover = factory.Trait(
            attachment_cover=factory.SubFactory(common_factories.AttachmentFactory)
        )

    @factory.post_generation
    def artist_credit(self, created, extracted, **kwargs):
        """
        A bit intricated, because we want to be able to specify a different
        track artist with a fallback on album artist if nothing is specified.

        And handle cases where build or build_batch are used (so no db calls)
        """
        # needed to get a primary key on the track and album objects. The primary key is needed for many_to_many
        if self.album:
            self.album.save()

        if not self.pk:
            self.save()

        if extracted:
            self.artist_credit.add(extracted)
        elif kwargs:
            if created:
                self.artist_credit.add(ArtistCreditFactory(**kwargs))
            else:
                self.artist_credit.add(ArtistCreditFactory.build(**kwargs))
        elif self.album:
            self.artist_credit.set(self.album.artist_credit.all())
        if created:
            self.save()

    # The @factory.post_generation is not used because we must
    # not redefine the builtin `license` function.
    def _license_post_generation(self, created, extracted, **kwargs):
        if not created:
            return

        if extracted:
            self.license = LicenseFactory(code=extracted)
            self.save()

    license = factory.PostGeneration(_license_post_generation)


@registry.register
class UploadGroupFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
    name = factory.Faker("name")
    owner = factory.SubFactory(federation_factories.ActorFactory)

    class Meta:
        model = "music.UploadGroup"


@registry.register
class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
    fid = factory.Faker("federation_url")
    track = factory.SubFactory(TrackFactory)
    library = factory.SubFactory(federation_factories.MusicLibraryFactory)
    audio_file = factory.django.FileField(
        from_path=os.path.join(SAMPLES_PATH, "test.ogg")
    )
    upload_group = factory.RelatedFactory(UploadGroupFactory)

    bitrate = None
    size = None
    duration = None
    mimetype = "audio/ogg"
    quality = 1

    class Meta:
        model = "music.Upload"

    class Params:
        in_place = factory.Trait(audio_file=None, mimetype=None)
        playable = factory.Trait(
            import_status="finished", library__privacy_level="everyone"
        )
        local = factory.Trait(
            fid=factory.Faker("federation_url", local=True),
            track__local=True,
            library__local=True,
        )

    @factory.post_generation
    def channel(self, created, extracted, **kwargs):
        if not extracted:
            return
        from funkwhale_api.audio import factories as audio_factories

        audio_factories.ChannelFactory(
            library=self.library,
            artist=self.track.artist_credit.all()[0].artist,
            **kwargs
        )


@registry.register
class UploadVersionFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
    upload = factory.SubFactory(UploadFactory, bitrate=200000)
    bitrate = factory.SelfAttribute("upload.bitrate")
    mimetype = "audio/mpeg"
    audio_file = factory.django.FileField()
    size = 2000000

    class Meta:
        model = "music.UploadVersion"


class ImportBatchFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
    submitted_by = factory.SubFactory(users_factories.UserFactory)

    class Meta:
        model = "music.ImportBatch"


@registry.register
class ImportJobFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
    batch = factory.SubFactory(ImportBatchFactory)
    source = factory.Faker("url")
    mbid = factory.Faker("uuid4")
    replace_if_duplicate = False

    class Meta:
        model = "music.ImportJob"
