from unittest.mock import patch

import pytest
from django.contrib.auth import get_user_model
from django.core.management import call_command
from music.models import Artist, Track
from music.utils import resolve_artist_names
from scrobbles.models import Scrobble


@pytest.mark.django_db
def test_resolve_with_mbid_keeps_literal_name():
    names = resolve_artist_names("Simon & Garfunkel", artist_mbid="grp-1")
    assert names == ["Simon & Garfunkel"]


@pytest.mark.django_db
@patch("music.utils.lookup_artist_from_mb", return_value={"name": "Simon & Garfunkel"})
def test_resolve_mb_exact_match_keeps_literal_name(mock_lookup):
    names = resolve_artist_names("Simon & Garfunkel")
    assert names == ["Simon & Garfunkel"]
    mock_lookup.assert_called_once_with("Simon & Garfunkel")


@pytest.mark.django_db
@patch(
    "music.utils.lookup_artist_from_mb", return_value={"name": "Simon and Garfunkel"}
)
def test_resolve_mb_inexact_match_falls_back_to_split(mock_lookup):
    names = resolve_artist_names("Simon & Garfunkel")
    assert names == ["Simon", "Garfunkel"]


@pytest.mark.django_db
@patch("music.utils.lookup_artist_from_mb", return_value={})
def test_resolve_splits_collab_when_mb_has_no_single_artist(mock_lookup):
    names = resolve_artist_names("Matt Sweeney & Bonnie Prince Billy")
    assert names == ["Matt Sweeney", "Bonnie Prince Billy"]


@pytest.mark.django_db
@patch("music.utils.lookup_artist_from_mb")
def test_resolve_existing_db_artist_skips_mb_lookup(mock_lookup):
    Artist.objects.create(name="Simon & Garfunkel")
    names = resolve_artist_names("Simon & Garfunkel")
    assert names == ["Simon & Garfunkel"]
    mock_lookup.assert_not_called()


@pytest.mark.django_db
@patch("music.utils.lookup_artist_from_mb", return_value={})
def test_resolve_strips_featured_names(mock_lookup):
    names = resolve_artist_names("Ariana Grande feat. Zedd")
    assert names == ["Ariana Grande"]


@pytest.mark.django_db
def test_find_or_create_trust_webhook_keeps_literal_artist():
    track = Track.find_or_create(
        title="Bridge over Troubled Water",
        artist_name="Simon & Garfunkel",
        album_name="Bridge over Troubled Water",
        artist_mbid="grp-1",
        trust_webhook_data=True,
    )

    assert list(track.artists.all().values_list("name", flat=True)) == [
        "Simon & Garfunkel"
    ]
    assert not Artist.objects.filter(name="Simon").exists()
    assert not Artist.objects.filter(name="Garfunkel").exists()


@pytest.mark.django_db
@patch("music.models.resolve_track", return_value=(None, ""))
def test_find_or_create_reuses_literal_artist(mock_resolve):
    artist = Artist.objects.create(name="Simon & Garfunkel")

    track = Track.find_or_create(
        title="Bridge over Troubled Water",
        artist_name="Simon & Garfunkel",
    )

    assert list(track.artists.all().values_list("name", flat=True)) == [
        "Simon & Garfunkel"
    ]
    assert not Artist.objects.filter(name="Simon").exists()
    assert not Artist.objects.filter(name="Garfunkel").exists()


@pytest.mark.django_db
@patch("music.models.get_track_metadata_with_artist", return_value=None)
@patch("music.models.lookup_artist_from_tadb", return_value=None)
def test_reconcile_command_merges_split_artists(mock_tadb, mock_track_meta):
    user = get_user_model().objects.create(email="reconcile@example.com")
    simon = Artist.objects.create(name="Simon")
    garfunkel = Artist.objects.create(name="Garfunkel")
    track = Track.objects.create(title="Bridge over Troubled Water", artist_fk=simon)
    track.artists.add(simon, garfunkel)
    Scrobble.objects.create(
        track=track,
        media_type="Track",
        user=user,
        log={
            "raw_data": {
                "artist": "Simon & Garfunkel",
                "musicbrainz_artist_id": "grp-1",
            }
        },
    )

    call_command("reconcile_split_artists", "--commit")

    track.refresh_from_db()
    assert [a.name for a in track.artists.all()] == ["Simon & Garfunkel"]
    assert track.artist_fk.name == "Simon & Garfunkel"
    assert "artist-reconciled" in track.tags.names()
    assert not Artist.objects.filter(name="Simon").exists()
    assert not Artist.objects.filter(name="Garfunkel").exists()


@pytest.mark.django_db
def test_reconcile_command_dry_run_makes_no_changes():
    user = get_user_model().objects.create(email="dry@example.com")
    simon = Artist.objects.create(name="Simon")
    garfunkel = Artist.objects.create(name="Garfunkel")
    track = Track.objects.create(title="Bridge over Troubled Water")
    track.artists.add(simon, garfunkel)
    Scrobble.objects.create(
        track=track,
        media_type="Track",
        user=user,
        log={
            "raw_data": {
                "artist": "Simon & Garfunkel",
                "musicbrainz_artist_id": "grp-1",
            }
        },
    )

    call_command("reconcile_split_artists")

    track.refresh_from_db()
    assert sorted(track.artists.all().values_list("name", flat=True)) == [
        "Garfunkel",
        "Simon",
    ]
    assert Artist.objects.filter(name="Simon").exists()
    assert Artist.objects.filter(name="Garfunkel").exists()


@pytest.mark.django_db
@patch("music.utils.lookup_artist_from_mb", return_value={})
def test_reconcile_command_leaves_genuine_collab_alone(mock_lookup):
    user = get_user_model().objects.create(email="collab@example.com")
    matt = Artist.objects.create(name="Matt Sweeney")
    bonnie = Artist.objects.create(name="Bonnie Prince Billy")
    track = Track.objects.create(title="My Morning Song")
    track.artists.add(matt, bonnie)
    Scrobble.objects.create(
        track=track,
        media_type="Track",
        user=user,
        log={"raw_data": {"artist": "Matt Sweeney & Bonnie Prince Billy"}},
    )

    call_command("reconcile_split_artists", "--commit")

    track.refresh_from_db()
    assert sorted(track.artists.all().values_list("name", flat=True)) == [
        "Bonnie Prince Billy",
        "Matt Sweeney",
    ]
