# This file is part of beets.
# Copyright 2016, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

"""Tests for the 'beatport' plugin.
"""

import unittest
from test import _common
from test.helper import TestHelper
from datetime import timedelta

from beetsplug import beatport
from beets import library


class BeatportTest(_common.TestCase, TestHelper):
    def _make_release_response(self):
        """Returns a dict that mimics a response from the beatport API.

        The results were retrieved from:
        https://oauth-api.beatport.com/catalog/3/releases?id=1742984
        The list of elements on the returned dict is incomplete, including just
        those required for the tests on this class.
        """
        results = {
          "id": 1742984,
          "type": "release",
          "name": "Charade",
          "slug": "charade",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "audioFormat": "",
          "category": "Release",
          "currentStatus": "General Content",
          "catalogNumber": "GR089",
          "description": "",
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
        }
        return results

    def _make_tracks_response(self):
        """Return a list that mimics a response from the beatport API.

        The results were retrieved from:
        https://oauth-api.beatport.com/catalog/3/tracks?releaseId=1742984
        The list of elements on the returned list is incomplete, including just
        those required for the tests on this class.
        """
        results = [{
          "id": 7817567,
          "type": "track",
          "sku": "track-7817567",
          "name": "Mirage a Trois",
          "trackNumber": 1,
          "mixName": "Original Mix",
          "title": "Mirage a Trois (Original Mix)",
          "slug": "mirage-a-trois-original-mix",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "currentStatus": "General Content",
          "length": "7:05",
          "lengthMs": 425421,
          "bpm": 90,
          "key": {
            "standard": {
              "letter": "G",
              "sharp": False,
              "flat": False,
              "chord": "minor"
            },
            "shortName": "Gmin"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
          "subGenres": [{
            "id": 209,
            "name": "Glitch Hop",
            "slug": "glitch-hop",
            "type": "subgenre"
          }],
          "release": {
            "id": 1742984,
            "name": "Charade",
            "type": "release",
            "slug": "charade"
          },
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings",
            "status": True
          }
        }, {
          "id": 7817568,
          "type": "track",
          "sku": "track-7817568",
          "name": "Aeon Bahamut",
          "trackNumber": 2,
          "mixName": "Original Mix",
          "title": "Aeon Bahamut (Original Mix)",
          "slug": "aeon-bahamut-original-mix",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "currentStatus": "General Content",
          "length": "7:38",
          "lengthMs": 458000,
          "bpm": 100,
          "key": {
            "standard": {
              "letter": "G",
              "sharp": False,
              "flat": False,
              "chord": "major"
            },
            "shortName": "Gmaj"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
          "subGenres": [{
            "id": 209,
            "name": "Glitch Hop",
            "slug": "glitch-hop",
            "type": "subgenre"
          }],
          "release": {
            "id": 1742984,
            "name": "Charade",
            "type": "release",
            "slug": "charade"
          },
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings",
            "status": True
          }
        }, {
          "id": 7817569,
          "type": "track",
          "sku": "track-7817569",
          "name": "Trancendental Medication",
          "trackNumber": 3,
          "mixName": "Original Mix",
          "title": "Trancendental Medication (Original Mix)",
          "slug": "trancendental-medication-original-mix",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "currentStatus": "General Content",
          "length": "1:08",
          "lengthMs": 68571,
          "bpm": 141,
          "key": {
            "standard": {
              "letter": "F",
              "sharp": False,
              "flat": False,
              "chord": "major"
            },
            "shortName": "Fmaj"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
          "subGenres": [{
            "id": 209,
            "name": "Glitch Hop",
            "slug": "glitch-hop",
            "type": "subgenre"
          }],
          "release": {
            "id": 1742984,
            "name": "Charade",
            "type": "release",
            "slug": "charade"
          },
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings",
            "status": True
          }
        }, {
          "id": 7817570,
          "type": "track",
          "sku": "track-7817570",
          "name": "A List of Instructions for When I'm Human",
          "trackNumber": 4,
          "mixName": "Original Mix",
          "title": "A List of Instructions for When I'm Human (Original Mix)",
          "slug": "a-list-of-instructions-for-when-im-human-original-mix",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "currentStatus": "General Content",
          "length": "6:57",
          "lengthMs": 417913,
          "bpm": 88,
          "key": {
            "standard": {
              "letter": "A",
              "sharp": False,
              "flat": False,
              "chord": "minor"
            },
            "shortName": "Amin"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
          "subGenres": [{
            "id": 209,
            "name": "Glitch Hop",
            "slug": "glitch-hop",
            "type": "subgenre"
          }],
          "release": {
            "id": 1742984,
            "name": "Charade",
            "type": "release",
            "slug": "charade"
          },
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings",
            "status": True
          }
        }, {
          "id": 7817571,
          "type": "track",
          "sku": "track-7817571",
          "name": "The Great Shenanigan",
          "trackNumber": 5,
          "mixName": "Original Mix",
          "title": "The Great Shenanigan (Original Mix)",
          "slug": "the-great-shenanigan-original-mix",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "currentStatus": "General Content",
          "length": "9:49",
          "lengthMs": 589875,
          "bpm": 123,
          "key": {
            "standard": {
              "letter": "E",
              "sharp": False,
              "flat": True,
              "chord": "major"
            },
            "shortName": "E&#9837;maj"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
          "subGenres": [{
            "id": 209,
            "name": "Glitch Hop",
            "slug": "glitch-hop",
            "type": "subgenre"
          }],
          "release": {
            "id": 1742984,
            "name": "Charade",
            "type": "release",
            "slug": "charade"
          },
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings",
            "status": True
          }
        }, {
          "id": 7817572,
          "type": "track",
          "sku": "track-7817572",
          "name": "Charade",
          "trackNumber": 6,
          "mixName": "Original Mix",
          "title": "Charade (Original Mix)",
          "slug": "charade-original-mix",
          "releaseDate": "2016-04-11",
          "publishDate": "2016-04-11",
          "currentStatus": "General Content",
          "length": "7:05",
          "lengthMs": 425423,
          "bpm": 123,
          "key": {
            "standard": {
              "letter": "A",
              "sharp": False,
              "flat": False,
              "chord": "major"
            },
            "shortName": "Amaj"
          },
          "artists": [{
            "id": 326158,
            "name": "Supersillyus",
            "slug": "supersillyus",
            "type": "artist"
          }],
          "genres": [{
            "id": 9,
            "name": "Breaks",
            "slug": "breaks",
            "type": "genre"
          }],
          "subGenres": [{
            "id": 209,
            "name": "Glitch Hop",
            "slug": "glitch-hop",
            "type": "subgenre"
          }],
          "release": {
            "id": 1742984,
            "name": "Charade",
            "type": "release",
            "slug": "charade"
          },
          "label": {
            "id": 24539,
            "name": "Gravitas Recordings",
            "type": "label",
            "slug": "gravitas-recordings",
            "status": True
          }
        }]
        return results

    def setUp(self):
        self.setup_beets()
        self.load_plugins('beatport')
        self.lib = library.Library(':memory:')

        # Set up 'album'.
        response_release = self._make_release_response()
        self.album = beatport.BeatportRelease(response_release)

        # Set up 'tracks'.
        response_tracks = self._make_tracks_response()
        self.tracks = [beatport.BeatportTrack(t) for t in response_tracks]

        # Set up 'test_album'.
        self.test_album = self.mk_test_album()

        # Set up 'test_tracks'
        self.test_tracks = self.test_album.items()

    def tearDown(self):
        self.unload_plugins()
        self.teardown_beets()

    def mk_test_album(self):
        items = [_common.item() for _ in range(6)]
        for item in items:
            item.album = 'Charade'
            item.catalognum = 'GR089'
            item.label = 'Gravitas Recordings'
            item.artist = 'Supersillyus'
            item.year = 2016
            item.comp = False
            item.label_name = 'Gravitas Recordings'
            item.genre = 'Glitch Hop'
            item.year = 2016
            item.month = 4
            item.day = 11
            item.mix_name = 'Original Mix'

        items[0].title = 'Mirage a Trois'
        items[1].title = 'Aeon Bahamut'
        items[2].title = 'Trancendental Medication'
        items[3].title = 'A List of Instructions for When I\'m Human'
        items[4].title = 'The Great Shenanigan'
        items[5].title = 'Charade'

        items[0].length = timedelta(minutes=7, seconds=5).total_seconds()
        items[1].length = timedelta(minutes=7, seconds=38).total_seconds()
        items[2].length = timedelta(minutes=1, seconds=8).total_seconds()
        items[3].length = timedelta(minutes=6, seconds=57).total_seconds()
        items[4].length = timedelta(minutes=9, seconds=49).total_seconds()
        items[5].length = timedelta(minutes=7, seconds=5).total_seconds()

        items[0].url = 'mirage-a-trois-original-mix'
        items[1].url = 'aeon-bahamut-original-mix'
        items[2].url = 'trancendental-medication-original-mix'
        items[3].url = 'a-list-of-instructions-for-when-im-human-original-mix'
        items[4].url = 'the-great-shenanigan-original-mix'
        items[5].url = 'charade-original-mix'

        counter = 0
        for item in items:
            counter += 1
            item.track_number = counter

        items[0].bpm = 90
        items[1].bpm = 100
        items[2].bpm = 141
        items[3].bpm = 88
        items[4].bpm = 123
        items[5].bpm = 123

        items[0].initial_key = 'Gmin'
        items[1].initial_key = 'Gmaj'
        items[2].initial_key = 'Fmaj'
        items[3].initial_key = 'Amin'
        items[4].initial_key = 'E&#9837;maj'
        items[5].initial_key = 'Amaj'

        for item in items:
            self.lib.add(item)

        album = self.lib.add_album(items)
        album.store()

        return album

    # Test BeatportRelease.
    def test_album_name_applied(self):
        self.assertEqual(self.album.name, self.test_album['album'])

    def test_catalog_number_applied(self):
        self.assertEqual(self.album.catalog_number,
                         self.test_album['catalognum'])

    def test_label_applied(self):
        self.assertEqual(self.album.label_name, self.test_album['label'])

    def test_category_applied(self):
        self.assertEqual(self.album.category, 'Release')

    def test_album_url_applied(self):
        self.assertEqual(self.album.url,
                         'https://beatport.com/release/charade/1742984')

    # Test BeatportTrack.
    def test_title_applied(self):
        for track, test_track in zip(self.tracks, self.test_tracks):
            self.assertEqual(track.name, test_track.title)

    def test_mix_name_applied(self):
        for track, test_track in zip(self.tracks, self.test_tracks):
            self.assertEqual(track.mix_name, test_track.mix_name)

    def test_length_applied(self):
        for track, test_track in zip(self.tracks, self.test_tracks):
            self.assertEqual(int(track.length.total_seconds()),
                             int(test_track.length))

    def test_track_url_applied(self):
        # Specify beatport ids here because an 'item.id' is beets-internal.
        ids = [
            7817567,
            7817568,
            7817569,
            7817570,
            7817571,
            7817572,
        ]
        # Concatenate with 'id' to pass strict equality test.
        for track, test_track, id in zip(self.tracks, self.test_tracks, ids):
            self.assertEqual(
                track.url, 'https://beatport.com/track/' +
                test_track.url + '/' + str(id))

    def test_bpm_applied(self):
        for track, test_track in zip(self.tracks, self.test_tracks):
            self.assertEqual(track.bpm, test_track.bpm)

    def test_initial_key_applied(self):
        for track, test_track in zip(self.tracks, self.test_tracks):
            self.assertEqual(track.initial_key, test_track.initial_key)

    def test_genre_applied(self):
        for track, test_track in zip(self.tracks, self.test_tracks):
            self.assertEqual(track.genre, test_track.genre)


class BeatportResponseEmptyTest(_common.TestCase, TestHelper):
    def _make_tracks_response(self):
        results = [{
            "id": 7817567,
            "name": "Mirage a Trois",
            "genres": [{
              "id": 9,
              "name": "Breaks",
              "slug": "breaks",
              "type": "genre"
            }],
            "subGenres": [{
              "id": 209,
              "name": "Glitch Hop",
              "slug": "glitch-hop",
              "type": "subgenre"
            }],
        }]
        return results

    def setUp(self):
        self.setup_beets()
        self.load_plugins('beatport')
        self.lib = library.Library(':memory:')

        # Set up 'tracks'.
        self.response_tracks = self._make_tracks_response()
        self.tracks = [beatport.BeatportTrack(t) for t in self.response_tracks]

        # Make alias to be congruent with class `BeatportTest`.
        self.test_tracks = self.response_tracks

    def tearDown(self):
        self.unload_plugins()
        self.teardown_beets()

    def test_response_tracks_empty(self):
        response_tracks = []
        tracks = [beatport.BeatportTrack(t) for t in response_tracks]
        self.assertEqual(tracks, [])

    def test_sub_genre_empty_fallback(self):
        """No 'sub_genre' is provided. Test if fallback to 'genre' works.
        """
        self.response_tracks[0]['subGenres'] = []
        tracks = [beatport.BeatportTrack(t) for t in self.response_tracks]

        self.test_tracks[0]['subGenres'] = []

        self.assertEqual(tracks[0].genre,
                         self.test_tracks[0]['genres'][0]['name'])

    def test_genre_empty(self):
        """No 'genre' is provided. Test if 'sub_genre' is applied.
        """
        self.response_tracks[0]['genres'] = []
        tracks = [beatport.BeatportTrack(t) for t in self.response_tracks]

        self.test_tracks[0]['genres'] = []

        self.assertEqual(tracks[0].genre,
                         self.test_tracks[0]['subGenres'][0]['name'])


def suite():
    return unittest.TestLoader().loadTestsFromName(__name__)


if __name__ == '__main__':
    unittest.main(defaultTest='suite')
