import unittest
from unittest.mock import Mock
from flask.helpers import url_for

import flask_testing

from albumcollections.config import Config
from albumcollections.spotify.item.spotify_artist import SpotifyArtist
from albumcollections.spotify.item.spotify_music import SpotifyAlbum
from albumcollections.spotify.item.spotify_collection import SpotifyCollection
import albumcollections.spotify.spotify_user as sp_user

from albumcollections import create_app


class TestingConfig(Config):
    TESTING = True
    PRESERVE_CONTEXT_ON_EXCEPTION = False

    # Overwrite session type to not have to deal with database in tests
    SESSION_TYPE = 'filesystem'
    SESSION_FILE_DIR = './.flask_session/'


class PickableMock(Mock):
    """This is needed for any mock objects that are going to
    be added to the cache as part of tests. It wasn't easy to
    find this workaround...https://github.com/testing-cabal/mock/issues/139
    """
    def __reduce__(self):
        return (Mock, ())


"""MOCK SPOTIFY CLIENT CREDENTIALS INTERFACE"""


class SpotifyMock(Mock):
    """Mock object to patch in to replace
    albumcollections.spotify.spotify.Spotify() class
    """
    def get_collection_from_playlist_id(self, *args, **kwargs):
        """Return dummy mock collection
        """
        album_mock1 = PickableMock(spec=SpotifyAlbum)
        album_mock1.name = "dummyalbum1"
        album_mock1.artists = [PickableMock(spec=SpotifyArtist)]
        album_mock1.artists[0].name = "dummyalbumartist1"
        album_mock1.id = "dummyalbumid1"
        album_mock1.link = "dummyalbumlink1"
        album_mock1.img_url = None

        album_mock2 = PickableMock(spec=SpotifyAlbum)
        album_mock2.name = "dummyalbum2"
        album_mock2.artists = [PickableMock(spec=SpotifyArtist)]
        album_mock2.artists[0].name = "dummyalbumartist2"
        album_mock2.id = "dummyalbumid2"
        album_mock2.link = "dummyalbumlink2"
        album_mock2.img_url = None

        collection_mock = Mock(spec=SpotifyCollection)
        collection_mock.albums = [album_mock1, album_mock2]
        collection_mock.name = "dummycollectionname"

        return collection_mock


class AlbumCollectionsTestCase(flask_testing.TestCase, unittest.TestCase):
    """Base test case class for all tests in albumcollections. Creates app
    using TestingConfig.
    """
    def create_app(self):
        # pass in test configuration
        return create_app(TestingConfig)

    def setUp(self):
        # Mock the user being logged out
        self.unauth_dummy_user()

    def tearDown(self):
        pass

    """MOCK SPOTIFY USER INTERFACE"""

    DUMMY_USER_SP_ID = "12345ABC"
    DUMMY_USER_DISPLAY_NAME = "<NAME>"

    def _raise_sp_test_exception(self, *args):
        """Raises an exception to authorize at the fake sp auth route"""
        raise sp_user.SpotifyUserAuthFailure(auth_url=url_for("test.fake_sp_auth"))

    def _mock_get_user_collections(self, *args):
        """Return a list of dummy spotify playlists
        """
        playlist1_mock = Mock(spec=SpotifyCollection)
        playlist1_mock.id = "dummyplaylistid1"
        playlist1_mock.link = "dummyplaylistlink1"
        playlist1_mock.name = "dummyplaylist1"
        playlist1_mock.img_url = None

        playlist2_mock = Mock(spec=SpotifyCollection)
        playlist2_mock.id = "dummyplaylistid2"
        playlist2_mock.link = "dummyplaylistlink2"
        playlist1_mock.name = "dummyplaylist2"
        playlist2_mock.img_url = None

        return [playlist1_mock, playlist2_mock]

    def auth_dummy_user(self, *args):
        sp_user.get_user_id = Mock(side_effect=lambda *args: self.DUMMY_USER_SP_ID)
        sp_user.is_authenticated = Mock(side_effect=lambda *args: True)
        sp_user.get_user_display_name = Mock(side_effect=lambda *args: self.DUMMY_USER_DISPLAY_NAME)
        sp_user.logout = Mock(side_effect=self.unauth_dummy_user)
        sp_user.get_user_collections = Mock(side_effect=self._mock_get_user_collections)

    def unauth_dummy_user(self, *args):
        sp_user.get_user_id = Mock(side_effect=self._raise_sp_test_exception)
        sp_user.is_authenticated = Mock(side_effect=lambda *args: False)
        sp_user.get_user_display_name = Mock(side_effect=self._raise_sp_test_exception)
        sp_user.auth_new_user = Mock(side_effect=self.auth_dummy_user)
        sp_user.get_user_collections = Mock(side_effect=lambda *args: [])

import unittest
from unittest.mock import Mock
from flask.helpers import url_for

import flask_testing

from albumcollections.config import Config
from albumcollections.spotify.item.spotify_artist import SpotifyArtist
from albumcollections.spotify.item.spotify_music import SpotifyAlbum
from albumcollections.spotify.item.spotify_collection import SpotifyCollection
import albumcollections.spotify.spotify_user as sp_user

from albumcollections import create_app


class TestingConfig(Config):
    TESTING = True
    PRESERVE_CONTEXT_ON_EXCEPTION = False

    # Overwrite session type to not have to deal with database in tests
    SESSION_TYPE = 'filesystem'
    SESSION_FILE_DIR = './.flask_session/'


class PickableMock(Mock):
    """This is needed for any mock objects that are going to
    be added to the cache as part of tests. It wasn't easy to
    find this workaround...https://github.com/testing-cabal/mock/issues/139
    """
    def __reduce__(self):
        return (Mock, ())


"""MOCK SPOTIFY CLIENT CREDENTIALS INTERFACE"""


class SpotifyMock(Mock):
    """Mock object to patch in to replace
    albumcollections.spotify.spotify.Spotify() class
    """
    def get_collection_from_playlist_id(self, *args, **kwargs):
        """Return dummy mock collection
        """
        album_mock1 = PickableMock(spec=SpotifyAlbum)
        album_mock1.name = "dummyalbum1"
        album_mock1.artists = [PickableMock(spec=SpotifyArtist)]
        album_mock1.artists[0].name = "dummyalbumartist1"
        album_mock1.id = "dummyalbumid1"
        album_mock1.link = "dummyalbumlink1"
        album_mock1.img_url = None

        album_mock2 = PickableMock(spec=SpotifyAlbum)
        album_mock2.name = "dummyalbum2"
        album_mock2.artists = [PickableMock(spec=SpotifyArtist)]
        album_mock2.artists[0].name = "dummyalbumartist2"
        album_mock2.id = "dummyalbumid2"
        album_mock2.link = "dummyalbumlink2"
        album_mock2.img_url = None

        collection_mock = Mock(spec=SpotifyCollection)
        collection_mock.albums = [album_mock1, album_mock2]
        collection_mock.name = "dummycollectionname"

        return collection_mock


class AlbumCollectionsTestCase(flask_testing.TestCase, unittest.TestCase):
    """Base test case class for all tests in albumcollections. Creates app
    using TestingConfig.
    """
    def create_app(self):
        # pass in test configuration
        return create_app(TestingConfig)

    def setUp(self):
        # Mock the user being logged out
        self.unauth_dummy_user()

    def tearDown(self):
        pass

    """MOCK SPOTIFY USER INTERFACE"""

    DUMMY_USER_SP_ID = "12345ABC"
    DUMMY_USER_DISPLAY_NAME = "<NAME>"

    def _raise_sp_test_exception(self, *args):
        """Raises an exception to authorize at the fake sp auth route"""
        raise sp_user.SpotifyUserAuthFailure(auth_url=url_for("test.fake_sp_auth"))

    def _mock_get_user_collections(self, *args):
        """Return a list of dummy spotify playlists
        """
        playlist1_mock = Mock(spec=SpotifyCollection)
        playlist1_mock.id = "dummyplaylistid1"
        playlist1_mock.link = "dummyplaylistlink1"
        playlist1_mock.name = "dummyplaylist1"
        playlist1_mock.img_url = None

        playlist2_mock = Mock(spec=SpotifyCollection)
        playlist2_mock.id = "dummyplaylistid2"
        playlist2_mock.link = "dummyplaylistlink2"
        playlist1_mock.name = "dummyplaylist2"
        playlist2_mock.img_url = None

        return [playlist1_mock, playlist2_mock]

    def auth_dummy_user(self, *args):
        sp_user.get_user_id = Mock(side_effect=lambda *args: self.DUMMY_USER_SP_ID)
        sp_user.is_authenticated = Mock(side_effect=lambda *args: True)
        sp_user.get_user_display_name = Mock(side_effect=lambda *args: self.DUMMY_USER_DISPLAY_NAME)
        sp_user.logout = Mock(side_effect=self.unauth_dummy_user)
        sp_user.get_user_collections = Mock(side_effect=self._mock_get_user_collections)

    def unauth_dummy_user(self, *args):
        sp_user.get_user_id = Mock(side_effect=self._raise_sp_test_exception)
        sp_user.is_authenticated = Mock(side_effect=lambda *args: False)
        sp_user.get_user_display_name = Mock(side_effect=self._raise_sp_test_exception)
        sp_user.auth_new_user = Mock(side_effect=self.auth_dummy_user)
        sp_user.get_user_collections = Mock(side_effect=lambda *args: [])

# Overwrite session type to not have to deal with database in tests This is needed for any mock objects that are going to     be added to the cache as part of tests. It wasn't easy to     find this workaround...https://github.com/testing-cabal/mock/issues/139 MOCK SPOTIFY CLIENT CREDENTIALS INTERFACE Mock object to patch in to replace     albumcollections.spotify.spotify.Spotify() class Return dummy mock collection Base test case class for all tests in albumcollections. Creates app     using TestingConfig. # pass in test configuration # Mock the user being logged out MOCK SPOTIFY USER INTERFACE Raises an exception to authorize at the fake sp auth route Return a list of dummy spotify playlists