import csv
import json
import os
from datetime import datetime, timedelta

import spotipy
from TheCodeLabs_BaseUtils.DefaultLogger import DefaultLogger
from spotipy.oauth2 import SpotifyClientCredentials

LOG_FORMAT = '[%(levelname)-7s] - %(asctime)s - %(message)s'
LOGGER = DefaultLogger().create_logger_if_not_exists('SpotifyBackup', logFormat=LOG_FORMAT)


class SpotifyBackup:
    DATE_FORMAT = '%Y-%m-%d_%H-%M-%S'
    DATE_FORMAT_SHORT = '%Y-%m-%d'
    INVALID_CHARS = [' ', '\t', '\n']
    REPLACE_CHAR = '_'

    def __init__(self, clientID: str, clientSecret: str, exportFolder: str):
        self._exportFolder = exportFolder
        if self._exportFolder:
            os.makedirs(self._exportFolder, exist_ok=True)

        client_credentials_manager = SpotifyClientCredentials(client_id=clientID, client_secret=clientSecret)
        self._spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

    def backup_playlist(self, username: str, playlistID: str):
        playlist = self.__get_playlist(username, playlistID)
        tracks = self.__get_tracks(playlist)

        exportPath = self.__determine_export_path(playlist['name'])

        LOGGER.info(f'>>> Exporting tracks to "{exportPath}"...')
        self.__export_csv(exportPath, tracks)
        LOGGER.info('Exporting DONE')

    def __get_playlist(self, username: str, playlistID: str):
        LOGGER.info(f'>>> Fetching playlist with ID: {playlistID} by {username}...')
        identifier = f'spotify:user:{username}:playlist:{playlistID}'
        playlist = self._spotify.playlist(identifier)
        LOGGER.info(f'Found playlist "{playlist["name"]}"')
        return playlist

    def __get_tracks(self, playlist):
        tracks = playlist['tracks']
        results = tracks['items']
        while tracks['next']:
            tracks = self._spotify.next(tracks)
            results.extend(tracks['items'])

        LOGGER.info(f'Fetched {len(results)} tracks')
        return results

    def __determine_export_path(self, playlistName):
        for item in self.INVALID_CHARS:
            playlistName = playlistName.replace(item, self.REPLACE_CHAR)
        fileName = f'{datetime.strftime(datetime.now(), self.DATE_FORMAT)}_{playlistName}.csv'
        exportPath = os.path.join(self._exportFolder, fileName)
        return exportPath

    def __export_csv(self, exportPath, tracks):
        with open(exportPath, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL)
            writer.writerow(('title', 'artists', 'album', 'addedBy', 'addedAt', 'url'))

            for track in tracks:
                try:
                    trackInfo = track['track']
                    title = trackInfo['name']
                    artists = trackInfo['artists']
                    artists = ' & '.join(artist['name'] for artist in artists)
                    album = trackInfo['album']['name']
                    author = track['added_by']['id']
                    addedAt = track['added_at']
                    url = trackInfo['external_urls'].get('spotify', '')

                    writer.writerow((title, artists, album, author, addedAt, url))
                except Exception:
                    LOGGER.exception(f'Error while exporting track "{track}"')

    def clean_old_exports(self, daysToKeep):
        if not daysToKeep:
            LOGGER.info('Skipping deletion of old files')
            return

        minimumDate = datetime.today() - timedelta(days=int(daysToKeep))
        LOGGER.info(f'>>> Deleting files older than {minimumDate} ({daysToKeep} days)')

        files = [file for file in sorted(os.listdir(self._exportFolder)) if
                 os.path.isfile(os.path.join(self._exportFolder, file))]

        for file in files:
            parts = file.split('_')
            creationDate = datetime.strptime(parts[0], self.DATE_FORMAT_SHORT)
            if creationDate < minimumDate:
                LOGGER.info(f'Removing old file "{file}"')
                os.remove(os.path.join(self._exportFolder, file))


if __name__ == '__main__':
    with open('config/settings.json', 'r', encoding='utf-8') as f:
        SETTINGS = json.load(f)

    spotifyBackup = SpotifyBackup(SETTINGS['spotifyAPI']['clientID'],
                                  SETTINGS['spotifyAPI']['clientSecret'],
                                  SETTINGS['exportFolder'])

    spotifyBackup.clean_old_exports(SETTINGS['daysToKeep'])

    for playlist in SETTINGS['playlists']:
        spotifyBackup.backup_playlist(playlist['user'], playlist['id'])

    LOGGER.info('### DONE ###')