"""
    gepify.providers.songs
    ~~~~~~~~~~~~~~~~~~~~~~

    Provides information about downloaded songs
    as well as functionality to download songs.
"""

import os

from werkzeug.contrib.cache import RedisCache
from gepify.celery import celery_app
from celery.utils.log import get_task_logger
from . import youtube, soundcloud, SUPPORTED_FORMATS, SONGS_DIRECTORY

cache = RedisCache(
    host=os.environ.get('REDIS_HOST', 'localhost'),
    port=os.environ.get('REDIS_PORT', 6379),
    password=os.environ.get('REDIS_PASS', ''),
    key_prefix='song_info_',
    default_timeout=0
)
logger = get_task_logger(__name__)


def get_song(song_name):
    """Return information about a song.

    Parameters
    ----------
    song_name : str
        The song name.

    Returns
    -------
    dict
        name - The song name.
        files - Dictionary with downloaded files for this song.
    """

    song = cache.get(song_name)
    if song is None:
        song = {
            'name': song_name,
            'files': {}
        }

        cache.set(song_name, song)

    return song


def add_song_file(song_name, file, format):
    """Mark a song as downloaded in desired format.

    Parameters
    ----------
    song_name : str
        The song name.
    file : str
        The file of the song as a path on the filesystem.
    format : str
        The format of the file.

    Raises
    ------
    ValueError
        If `format` is not supported.
    """

    if format not in SUPPORTED_FORMATS:
        raise ValueError('Format not supported: {}'.format(format))

    song = get_song(song_name)
    song['files'][format] = file
    cache.set(song_name, song)


def has_song_format(song_name, format):
    """Check if a song is already downloaded in the desired format.

    Parameters
    ----------
    song_name : str
        The song name.
    format : str
        The format to check.

    Returns
    -------
    bool
        True if `song_name` is downloaded in `format`, False otherwise.
    """

    song = cache.get(song_name)

    if song is None or format not in song['files'].keys():
        return False

    return song['files'][format] != 'downloading'


@celery_app.task(bind=True)
def download_song(self, song_info, provider='youtube', format='mp3'):
    """Download a song.

    Parameters
    ----------
    song_info : dict
        Contains information about the song.
        name - The song name
        [provider] (optional) - Known id of the song by [provider].
        If present song will not be searched and will be directly
        downloaded by this id.
    provider : str
        The provider which will download the song. Default: 'youtube'
    format : str
        The format in which the song will be saved. Default: 'mp3'

    Raises
    ------
    ValueError
        If either `format` or `provider` is not supported.
    """

    if format not in SUPPORTED_FORMATS:
        raise ValueError('Format not supported: {}'.format(format))

    song = get_song(song_info['name'])

    if format in song['files']:
        if song['files'][format] == 'downloading':
            if self.request.chord is None:
                logger.info(
                    'Attempt to download a song in the process of downloading')
            else:
                logger.info(
                    'Song is aleady downloading. Will retry in 5 seconds.')
                self.retry(countdown=5)
            return
        elif song['files'][format] is not None:
            logger.info('Attempt to download already downloaded song.'
                        'Cache: {}'.format(song['files'][format]))
            return

    song['files'][format] = 'downloading'
    cache.set(song_info['name'], song)

    try:
        song_id = song_info.get(provider)
        if provider == 'youtube':
            if 'youtube' not in song_info:
                song_id = youtube.get_song_id(song_info['name'])
            youtube.download_song(song_id, format)
            add_song_file(
                song_info['name'],
                '{}/{}.{}'.format(SONGS_DIRECTORY, song_id, format), format)
        elif provider == 'soundcloud':
            if 'soundcloud' not in song_info:
                song_id, download_id = soundcloud.get_song_id(
                    song_info['name'])
            else:
                song_id, download_id = song_id
            soundcloud.download_song(download_id, format)
            add_song_file(
                song_info['name'],
                '{}/{}.{}'.format(SONGS_DIRECTORY, song_id, format), format)
        else:
            raise ValueError('Provider not found: {}'.format(provider))
    except:
        del song['files'][format]
        cache.set(song_info['name'], song)
        raise
