import csv
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 Album, Artist, Track
from scrobbles.models import Scrobble


def _write_fix_csv(path, track_ids):
    with open(path, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(
            [
                "track_id",
                "track_artist_name",
                "track_album_name",
                "raw_artist",
                "raw_album",
                "source",
                "mismatch",
            ]
        )
        for track_id in track_ids:
            writer.writerow([track_id, "", "", "", "", "", ""])


def _scrobble(user, track, raw_data):
    return Scrobble.objects.create(
        track=track,
        media_type="Track",
        user=user,
        log={"raw_data": raw_data},
    )


@pytest.mark.django_db
def test_report_writes_mismatch_csv(tmp_path):
    user = get_user_model().objects.create(email="report@example.com")
    wrong = Artist.objects.create(name="Wrong Artist")
    track = Track.objects.create(title="Song", artist_fk=wrong)
    track.artists.add(wrong)
    _scrobble(user, track, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"})

    out = tmp_path / "report.csv"
    call_command("report_mismatched_metadata", file_path=str(out))

    with open(out) as f:
        rows = list(csv.DictReader(f))
    assert len(rows) == 1
    assert rows[0]["track_id"] == str(track.id)
    assert "artist" in rows[0]["mismatch"]
    assert rows[0]["raw_artist"] == "Right Artist"


@pytest.mark.django_db
def test_fix_file_commits_resolved_artists(tmp_path):
    user = get_user_model().objects.create(email="fix@example.com")
    wrong = Artist.objects.create(name="Wrong Artist")
    right = Artist.objects.create(name="Right Artist")
    track = Track.objects.create(title="Song", artist_fk=wrong)
    track.artists.add(wrong)
    _scrobble(user, track, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"})

    fix = tmp_path / "fix.csv"
    _write_fix_csv(fix, [track.id])
    call_command("report_mismatched_metadata", fix_file=str(fix), commit=True)

    track.refresh_from_db()
    assert list(track.artists.all().values_list("name", flat=True)) == ["Right Artist"]
    assert track.artist_fk.name == "Right Artist"
    assert "metadata-fixed" in track.tags.names()
    assert not Artist.objects.filter(name="Wrong Artist").exists()


@pytest.mark.django_db
def test_fix_file_dry_run_makes_no_changes(tmp_path):
    user = get_user_model().objects.create(email="dry-fix@example.com")
    wrong = Artist.objects.create(name="Wrong Artist")
    right = Artist.objects.create(name="Right Artist")
    track = Track.objects.create(title="Song", artist_fk=wrong)
    track.artists.add(wrong)
    _scrobble(user, track, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"})

    fix = tmp_path / "fix.csv"
    _write_fix_csv(fix, [track.id])
    call_command("report_mismatched_metadata", fix_file=str(fix))

    track.refresh_from_db()
    assert sorted(track.artists.all().values_list("name", flat=True)) == [
        "Wrong Artist"
    ]
    assert not track.tags.filter(name="metadata-fixed").exists()


@pytest.mark.django_db
def test_fix_file_only_touches_listed_tracks(tmp_path):
    user = get_user_model().objects.create(email="subset@example.com")
    wrong = Artist.objects.create(name="Wrong Artist")
    keep = Artist.objects.create(name="Keep Artist")
    track_a = Track.objects.create(title="Song A", artist_fk=wrong)
    track_a.artists.add(wrong)
    track_b = Track.objects.create(title="Song B", artist_fk=wrong)
    track_b.artists.add(wrong)
    _scrobble(
        user, track_a, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"}
    )
    _scrobble(
        user, track_b, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"}
    )

    fix = tmp_path / "fix.csv"
    _write_fix_csv(fix, [track_a.id])
    call_command("report_mismatched_metadata", fix_file=str(fix), commit=True)

    track_a.refresh_from_db()
    track_b.refresh_from_db()
    assert list(track_a.artists.all().values_list("name", flat=True)) == [
        "Right Artist"
    ]
    assert sorted(track_b.artists.all().values_list("name", flat=True)) == [
        "Wrong Artist"
    ]


@pytest.mark.django_db
@patch("music.models.Album.find_or_create")
def test_fix_file_fixes_album(mock_album, tmp_path):
    user = get_user_model().objects.create(email="album@example.com")
    artist = Artist.objects.create(name="Right Artist")
    old_album = Album.objects.create(name="Old Album", album_artist=artist)
    new_album = Album.objects.create(name="New Album", album_artist=artist)
    mock_album.return_value = new_album
    track = Track.objects.create(title="Song", artist_fk=artist, album=old_album)
    track.artists.add(artist)
    _scrobble(user, track, {"Artist": "Right Artist", "Album": "New Album"})

    fix = tmp_path / "fix.csv"
    _write_fix_csv(fix, [track.id])
    call_command("report_mismatched_metadata", fix_file=str(fix), commit=True)

    track.refresh_from_db()
    assert track.album == new_album
    mock_album.assert_called_once_with("New Album", "Right Artist")
