import json
import os
import pytest
from django.urls import reverse

from funkwhale_api.music import models
from funkwhale_api.musicbrainz import api
from funkwhale_api.music import serializers
from funkwhale_api.music import tasks


DATA_DIR = os.path.dirname(os.path.abspath(__file__))


def test_can_submit_youtube_url_for_track_import(
        settings, artists, albums, tracks, mocker, superuser_client):
    mocker.patch('funkwhale_api.music.tasks.import_job_run.delay')
    mocker.patch(
        'funkwhale_api.musicbrainz.api.artists.get',
        return_value=artists['get']['adhesive_wombat'])
    mocker.patch(
        'funkwhale_api.musicbrainz.api.releases.get',
        return_value=albums['get']['marsupial'])
    mocker.patch(
        'funkwhale_api.musicbrainz.api.recordings.get',
        return_value=tracks['get']['8bitadventures'])
    mocker.patch(
        'funkwhale_api.music.models.TrackFile.download_file',
        return_value=None)
    mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed'
    video_id = 'tPEE9ZwTmy0'
    url = reverse('api:v1:submit-single')
    video_url = 'https://www.youtube.com/watch?v={0}'.format(video_id)
    response = superuser_client.post(
        url,
        {'import_url': video_url,
         'mbid': mbid})

    assert response.status_code == 201
    batch = superuser_client.user.imports.latest('id')
    job = batch.jobs.latest('id')
    assert job.status == 'pending'
    assert str(job.mbid) == mbid
    assert job.source == video_url


def test_import_creates_an_import_with_correct_data(mocker, superuser_client):
    mocker.patch('funkwhale_api.music.tasks.import_job_run')
    mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed'
    video_id = 'tPEE9ZwTmy0'
    url = reverse('api:v1:submit-single')
    response = superuser_client.post(
        url,
        {'import_url': 'https://www.youtube.com/watch?v={0}'.format(video_id),
         'mbid': mbid})

    batch = models.ImportBatch.objects.latest('id')
    assert batch.jobs.count() == 1
    assert batch.submitted_by == superuser_client.user
    assert batch.status == 'pending'
    job = batch.jobs.first()
    assert str(job.mbid) == mbid
    assert job.status == 'pending'
    assert job.source == 'https://www.youtube.com/watch?v={0}'.format(video_id)


def test_can_import_whole_album(
        artists, albums, mocker, superuser_client):
    mocker.patch('funkwhale_api.music.tasks.import_job_run')
    mocker.patch(
        'funkwhale_api.musicbrainz.api.artists.get',
        return_value=artists['get']['soad'])
    mocker.patch(
        'funkwhale_api.musicbrainz.api.images.get_front',
        return_value=b'')
    mocker.patch(
        'funkwhale_api.musicbrainz.api.releases.get',
        return_value=albums['get_with_includes']['hypnotize'])
    payload = {
        'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94',
        'tracks': [
            {
            'mbid': '1968a9d6-8d92-4051-8f76-674e157b6eed',
            'source': 'https://www.youtube.com/watch?v=1111111111',
            },
            {
            'mbid': '2968a9d6-8d92-4051-8f76-674e157b6eed',
            'source': 'https://www.youtube.com/watch?v=2222222222',
            },
            {
            'mbid': '3968a9d6-8d92-4051-8f76-674e157b6eed',
            'source': 'https://www.youtube.com/watch?v=3333333333',
            },
        ]
    }
    url = reverse('api:v1:submit-album')
    response = superuser_client.post(
        url, json.dumps(payload), content_type="application/json")

    batch = models.ImportBatch.objects.latest('id')
    assert batch.jobs.count() == 3
    assert batch.submitted_by == superuser_client.user
    assert batch.status == 'pending'

    album = models.Album.objects.latest('id')
    assert str(album.mbid) == '47ae093f-1607-49a3-be11-a15d335ccc94'
    medium_data = albums['get_with_includes']['hypnotize']['release']['medium-list'][0]
    assert int(medium_data['track-count']) == album.tracks.all().count()

    for track in medium_data['track-list']:
        instance = models.Track.objects.get(mbid=track['recording']['id'])
        assert instance.title == track['recording']['title']
        assert instance.position == int(track['position'])
        assert instance.title == track['recording']['title']

    for row in payload['tracks']:
        job = models.ImportJob.objects.get(mbid=row['mbid'])
        assert str(job.mbid) == row['mbid']
        assert job.status == 'pending'
        assert job.source == row['source']


def test_can_import_whole_artist(
        artists, albums, mocker, superuser_client):
    mocker.patch('funkwhale_api.music.tasks.import_job_run')
    mocker.patch(
        'funkwhale_api.musicbrainz.api.artists.get',
        return_value=artists['get']['soad'])
    mocker.patch(
        'funkwhale_api.musicbrainz.api.images.get_front',
        return_value=b'')
    mocker.patch(
        'funkwhale_api.musicbrainz.api.releases.get',
        return_value=albums['get_with_includes']['hypnotize'])
    payload = {
        'artistId': 'mbid',
        'albums': [
            {
                'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94',
                'tracks': [
                    {
                    'mbid': '1968a9d6-8d92-4051-8f76-674e157b6eed',
                    'source': 'https://www.youtube.com/watch?v=1111111111',
                    },
                    {
                    'mbid': '2968a9d6-8d92-4051-8f76-674e157b6eed',
                    'source': 'https://www.youtube.com/watch?v=2222222222',
                    },
                    {
                    'mbid': '3968a9d6-8d92-4051-8f76-674e157b6eed',
                    'source': 'https://www.youtube.com/watch?v=3333333333',
                    },
                ]
            }
        ]
    }
    url = reverse('api:v1:submit-artist')
    response = superuser_client.post(
        url, json.dumps(payload), content_type="application/json")

    batch = models.ImportBatch.objects.latest('id')
    assert batch.jobs.count() == 3
    assert batch.submitted_by == superuser_client.user
    assert batch.status == 'pending'

    album = models.Album.objects.latest('id')
    assert str(album.mbid) == '47ae093f-1607-49a3-be11-a15d335ccc94'
    medium_data = albums['get_with_includes']['hypnotize']['release']['medium-list'][0]
    assert int(medium_data['track-count']) == album.tracks.all().count()

    for track in medium_data['track-list']:
        instance = models.Track.objects.get(mbid=track['recording']['id'])
        assert instance.title == track['recording']['title']
        assert instance.position == int(track['position'])
        assert instance.title == track['recording']['title']

    for row in payload['albums'][0]['tracks']:
        job = models.ImportJob.objects.get(mbid=row['mbid'])
        assert str(job.mbid) == row['mbid']
        assert job.status == 'pending'
        assert job.source == row['source']


def test_user_can_query_api_for_his_own_batches(
        superuser_api_client, factories):
    factories['music.ImportJob']()
    job = factories['music.ImportJob'](
        batch__submitted_by=superuser_api_client.user)
    url = reverse('api:v1:import-batches-list')

    response = superuser_api_client.get(url)
    results = response.data
    assert results['count'] == 1
    assert results['results'][0]['jobs'][0]['mbid'] == job.mbid


def test_user_cannnot_access_other_batches(
        superuser_api_client, factories):
    factories['music.ImportJob']()
    job = factories['music.ImportJob']()
    url = reverse('api:v1:import-batches-list')

    response = superuser_api_client.get(url)
    results = response.data
    assert results['count'] == 0


def test_user_can_create_an_empty_batch(superuser_api_client, factories):
    url = reverse('api:v1:import-batches-list')
    response = superuser_api_client.post(url)

    assert response.status_code == 201

    batch = superuser_api_client.user.imports.latest('id')

    assert batch.submitted_by == superuser_api_client.user
    assert batch.source == 'api'


def test_user_can_create_import_job_with_file(
        superuser_api_client, factories, mocker):
    path = os.path.join(DATA_DIR, 'test.ogg')
    m = mocker.patch('funkwhale_api.common.utils.on_commit')
    batch = factories['music.ImportBatch'](
        submitted_by=superuser_api_client.user)
    url = reverse('api:v1:import-jobs-list')
    with open(path, 'rb') as f:
        content = f.read()
        f.seek(0)
        response = superuser_api_client.post(url, {
            'batch': batch.pk,
            'audio_file': f,
            'source': 'file://'
        })

    assert response.status_code == 201

    job = batch.jobs.latest('id')

    assert job.status == 'pending'
    assert job.source.startswith('file://')
    assert 'test.ogg' in job.source
    assert job.audio_file.read() == content

    m.assert_called_once_with(
        tasks.import_job_run.delay,
        import_job_id=job.pk)


def test_can_search_artist(factories, logged_in_client):
    artist1 = factories['music.Artist']()
    artist2 = factories['music.Artist']()
    expected = [serializers.ArtistSerializerNested(artist1).data]
    url = reverse('api:v1:artists-search')
    response = logged_in_client.get(url, {'query': artist1.name})
    assert response.data == expected


def test_can_search_artist_by_name_start(factories, logged_in_client):
    artist1 = factories['music.Artist'](name='alpha')
    artist2 = factories['music.Artist'](name='beta')
    expected = {
        'next': None,
        'previous': None,
        'count': 1,
        'results': [serializers.ArtistSerializerNested(artist1).data]
    }
    url = reverse('api:v1:artists-list')
    response = logged_in_client.get(url, {'name__startswith': 'a'})

    assert expected == response.data


def test_can_search_tracks(factories, logged_in_client):
    track1 = factories['music.Track'](title="test track 1")
    track2 = factories['music.Track']()
    query = 'test track 1'
    expected = [serializers.TrackSerializerNested(track1).data]
    url = reverse('api:v1:tracks-search')
    response = logged_in_client.get(url, {'query': query})

    assert expected == response.data


@pytest.mark.parametrize('route,method', [
    ('api:v1:tags-list', 'get'),
    ('api:v1:tracks-list', 'get'),
    ('api:v1:artists-list', 'get'),
    ('api:v1:albums-list', 'get'),
])
def test_can_restrict_api_views_to_authenticated_users(
        db, route, method, settings, client):
    url = reverse(route)
    settings.API_AUTHENTICATION_REQUIRED = True
    response = getattr(client, method)(url)
    assert response.status_code == 401


def test_track_file_url_is_restricted_to_authenticated_users(
        api_client, factories, settings):
    settings.API_AUTHENTICATION_REQUIRED = True
    f = factories['music.TrackFile']()
    assert f.audio_file is not None
    url = f.path
    response = api_client.get(url)
    assert response.status_code == 401


def test_track_file_url_is_accessible_to_authenticated_users(
        logged_in_api_client, factories, settings):
    settings.API_AUTHENTICATION_REQUIRED = True
    f = factories['music.TrackFile']()
    assert f.audio_file is not None
    url = f.path
    response = logged_in_api_client.get(url)

    assert response.status_code == 200
    assert response['X-Accel-Redirect'] == '/_protected{}'.format(f.audio_file.url)
