import pytest
from django.contrib.auth import get_user_model
from django.test.utils import CaptureQueriesContext
from django.db import connection
from django.utils import timezone

from music.aggregators import batch_live_charts, live_charts
from scrobbles.models import Scrobble, Track, Artist
from videos.models import Video

User = get_user_model()


@pytest.mark.django_db
def test_scrobble_queryset_with_related_reduces_queries():
    """Test that Scrobble.objects.with_related() reduces query count."""
    user = User.objects.create_user(username="perfuser", password="testpass123")

    artist = Artist.objects.create(name="Test Artist")
    track = Track.objects.create(
        title="Test Track", artist=artist, base_run_time_seconds=180
    )

    num_scrobbles = 10
    for i in range(num_scrobbles):
        Scrobble.objects.create(
            user=user,
            track=track,
            media_type=Scrobble.MediaType.TRACK,
            played_to_completion=True,
            timestamp=timezone.now() - timezone.timedelta(days=i),
        )

    with CaptureQueriesContext(connection) as context:
        scrobbles = list(Scrobble.objects.with_related().filter(user=user))
        for s in scrobbles:
            _ = s.track.title
            _ = s.track.artist.name

    print(
        f"\nwith_related() queries for {num_scrobbles} scrobbles: {len(context.captured_queries)}"
    )
    for q in context.captured_queries:
        print(f"  {q['sql'][:100]}...")

    assert len(context.captured_queries) < num_scrobbles


@pytest.mark.django_db
def test_batch_live_charts_vs_individual_calls():
    """Test that batch_live_charts uses fewer queries than individual live_charts calls."""
    user = User.objects.create_user(username="batchuser", password="testpass123")

    artist = Artist.objects.create(name="Test Artist")
    track = Track.objects.create(
        title="Test Track", artist=artist, base_run_time_seconds=180
    )

    for i in range(5):
        Scrobble.objects.create(
            user=user,
            track=track,
            media_type=Scrobble.MediaType.TRACK,
            played_to_completion=True,
            timestamp=timezone.now() - timezone.timedelta(days=i),
        )

    with CaptureQueriesContext(connection) as batch_context:
        batch_result = batch_live_charts(user=user, media_type="Track", limit=15)

    with CaptureQueriesContext(connection) as individual_context:
        individual_result = {
            "today": list(
                live_charts(user=user, media_type="Track", chart_period="today")
            ),
            "last7": list(
                live_charts(user=user, media_type="Track", chart_period="last7")
            ),
            "last30": list(
                live_charts(user=user, media_type="Track", chart_period="last30")
            ),
            "year": list(
                live_charts(user=user, media_type="Track", chart_period="year")
            ),
            "all": list(live_charts(user=user, media_type="Track", chart_period="all")),
        }

    print(f"\nbatch_live_charts queries: {len(batch_context.captured_queries)}")
    print(f"individual live_charts queries: {len(individual_context.captured_queries)}")

    assert len(batch_context.captured_queries) <= len(
        individual_context.captured_queries
    )


@pytest.mark.django_db
def test_as_dict_by_type_uses_with_related():
    """Test that as_dict_by_type uses with_related() for reduced queries."""
    user = User.objects.create_user(username="dictuser", password="testpass123")

    artist = Artist.objects.create(name="Test Artist")
    track = Track.objects.create(
        title="Test Track", artist=artist, base_run_time_seconds=180
    )

    for i in range(5):
        Scrobble.objects.create(
            user=user,
            track=track,
            media_type=Scrobble.MediaType.TRACK,
            played_to_completion=True,
            timestamp=timezone.now() - timezone.timedelta(days=i),
        )

    scrobbles = Scrobble.objects.filter(user=user)

    with CaptureQueriesContext(connection) as context:
        result = Scrobble.as_dict_by_type(scrobbles)

    print(f"\nas_dict_by_type queries: {len(context.captured_queries)}")
    for q in context.captured_queries:
        print(f"  {q['sql'][:100]}...")

    assert len(context.captured_queries) < 10
    assert "Track" in result
    assert len(result["Track"]) == 5


@pytest.mark.django_db
def test_person_scrobble_junction_efficient_count():
    """Test that PersonScrobble junction table enables efficient counts."""
    from people.models import Person, PersonScrobble

    creator = User.objects.create_user(username="creator2", password="testpass123")
    other_user = User.objects.create_user(username="other2", password="testpass123")

    person = Person.objects.create(name="Test Person", created_by=creator, user=creator)

    artist = Artist.objects.create(name="Test Artist")
    track = Track.objects.create(
        title="Test Track", artist=artist, base_run_time_seconds=180
    )

    for i in range(10):
        scrobble = Scrobble.objects.create(
            user=other_user,
            track=track,
            media_type=Scrobble.MediaType.TRACK,
            played_to_completion=True,
            log={"with_people_ids": [person.id]},
        )

    count = person.get_scrobble_count(user=other_user)
    assert count == 10

    with CaptureQueriesContext(connection) as context:
        assert person.get_scrobble_count(user=other_user) == 10

    print(f"\nPersonScrobble count queries: {len(context.captured_queries)}")
    for q in context.captured_queries:
        print(f"  {q['sql'][:100]}...")

    assert len(context.captured_queries) <= 3


@pytest.mark.django_db
def test_video_scrobbles_with_related():
    """Test prefetching works for video scrobbles."""
    user = User.objects.create_user(username="videouser", password="testpass123")

    video = Video.objects.create(
        title="Test Video",
        video_type=Video.VideoType.MOVIE,
        base_run_time_seconds=5400,
    )

    for i in range(3):
        Scrobble.objects.create(
            user=user,
            video=video,
            media_type=Scrobble.MediaType.VIDEO,
            played_to_completion=True,
            timestamp=timezone.now() - timezone.timedelta(days=i),
        )

    with CaptureQueriesContext(connection) as context:
        scrobbles = list(Scrobble.objects.with_related().filter(user=user))
        for s in scrobbles:
            _ = s.video.title
            _ = s.video.video_type

    print(f"\nVideo scrobbles with_related() queries: {len(context.captured_queries)}")
    for q in context.captured_queries:
        print(f"  {q['sql'][:100]}...")

    assert len(context.captured_queries) < 5
