import logging
import re
from typing import Optional

from music.musicbrainz import (
    lookup_album_dict_from_mb,
    lookup_artist_from_mb,
    lookup_track_from_mb,
)
from music.constants import VARIOUS_ARTIST_DICT
from scrobbles.utils import convert_to_seconds

logger = logging.getLogger(__name__)


from music.models import Album, Artist, Track


def clean_artist_name(name: str) -> str:
    """Remove featured names from artist string."""
    if "feat." in name.lower():
        name = re.split("feat.", name, flags=re.IGNORECASE)[0].strip()
    if "featuring" in name.lower():
        name = re.split("featuring", name, flags=re.IGNORECASE)[0].strip()
    if "&" in name.lower():
        name = re.split("&", name, flags=re.IGNORECASE)[0].strip()

    return name


# TODO These are depreacted, remove them eventually
def get_or_create_artist(name: str, mbid: str = "") -> Artist:
    """Get an Artist object from the database.

    Check if an artist with this name or Musicbrainz ID already exists.
    Otherwise, go lookup artist data from Musicbrainz and create one.

    """
    artist = None
    name = clean_artist_name(name)

    # Check for name/mbid combo, just mbid and then just name
    artist = Artist.objects.filter(name=name, mbid=mbid).first()
    if not artist:
        artist = Artist.objects.filter(musicbrainz_id=mbid).first()
    if not artist:
        artist = Artist.objects.filter(name=name).first()

    # Does not exist, look it up from Musicbrainz
    if not artist:
        artist_dict = lookup_artist_from_mb(name)
        mbid = mbid or artist_dict.get("id", "")

        if mbid:
            artist = Artist.objects.filter(musicbrainz_id=mbid).first()

    if not artist:
        artist = Artist.objects.create(name=name, musicbrainz_id=mbid)
        # TODO maybe this should be spun off into an async task?
        artist.fix_metadata()

    return artist


# TODO These are depreacted, remove them eventually
def get_or_create_album(
    name: str, artist: Artist, mbid: str = None
) -> Optional[Album]:
    album = None
    album_dict = lookup_album_dict_from_mb(name, artist_name=artist.name)

    name = name or album_dict.get("title", None)
    if not name:
        logger.debug(
            f"Cannot get or create album by {artist} with no name ({name})"
        )
        return

    album = Album.objects.filter(
        musicbrainz_id=mbid, name=name, artists__in=[artist]
    ).first()

    if not album:
        mbid_group = album_dict.get("mb_group_id")
        album = Album.objects.filter(
            musicbrainz_releasegroup_id=mbid_group
        ).first()

    if not album and name:
        mbid = mbid or album_dict["mb_id"]
        album, album_created = Album.objects.get_or_create(musicbrainz_id=mbid)
        if album_created:
            album.name = name
            album.year = album_dict["year"]
            album.musicbrainz_releasegroup_id = album_dict["mb_group_id"]
            album.musicbrainz_albumartist_id = artist.musicbrainz_id
            album.save(
                update_fields=[
                    "name",
                    "musicbrainz_id",
                    "year",
                    "musicbrainz_releasegroup_id",
                    "musicbrainz_albumartist_id",
                ]
            )
            album.artists.add(artist)
            album.fix_album_artist()
            album.fetch_artwork()
            album.scrape_allmusic()

    if not album:
        logger.warn(f"No album found for {name} and {mbid}")

    album.fix_album_artist()
    return album


# TODO These are depreacted, remove them eventually
def get_or_create_track(post_data: dict, post_keys: dict) -> Track:
    try:
        track_run_time_seconds = int(
            post_data.get(post_keys.get("RUN_TIME"), 0)
        )
    except ValueError:  # Sometimes we get run time as a string like "01:35"
        track_run_time_seconds = convert_to_seconds(
            post_data.get(post_keys.get("RUN_TIME"), 0)
        )

    artist_name = post_data.get(post_keys.get("ARTIST_NAME"), "")
    artist_mb_id = post_data.get(post_keys.get("ARTIST_MB_ID"), "")
    album_title = post_data.get(post_keys.get("ALBUM_NAME"), "")
    album_mb_id = post_data.get(post_keys.get("ALBUM_MB_ID"), "")
    track_title = post_data.get(post_keys.get("TRACK_TITLE"), "")
    track_mb_id = post_data.get(post_keys.get("TRACK_MB_ID"), "")

    artist = Artist.find_or_create(artist_name, artist_mb_id)
    album = None
    # We may get no album ID or title, in which case, skip
    if album_mb_id or album_title:
        album = Album.find_or_create(
            album_title, str(artist.name), album_mb_id
        )

    track = None
    if not track_mb_id and album:
        try:
            track_mb_id = lookup_track_from_mb(
                track_title,
                artist.musicbrainz_id,
                album.musicbrainz_id,
            ).get("id", 0)
        except TypeError:
            pass

    if not track_title and not track_mb_id:
        logger.info(
            "Cannot find track without either title or MB ID",
            extra={"post_data": post_data},
        )
        return

    if track_mb_id:
        track = Track.objects.filter(musicbrainz_id=track_mb_id).first()

    if not track and track_title:
        track = Track.objects.filter(title=track_title, artist=artist).first()

    if not track:
        track = Track.objects.create(
            title=track_title,
            artist=artist,
            album=album,
            musicbrainz_id=track_mb_id,
            run_time_seconds=track_run_time_seconds,
        )
    return track


def get_or_create_various_artists() -> Artist:
    artist = Artist.objects.filter(name="Various Artists").first()
    if not artist:
        artist = Artist.objects.create(**VARIOUS_ARTIST_DICT)
        logger.info("Created Various Artists placeholder")
    return artist
