"""
Functions that provide aid to calls to Spotify API
"""

import math
import random
import requests
from app import app
import json

BASE_URL = "https://api.spotify.com/v1"

def generateRandomString(length):
    """
    Spotify's proposal to generate randomly generated strings, https://developer.spotify.com/documentation/web-playback-sdk/guide/
    This is done to protect against attacks such as CSRF
    """

    state = ""
    possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

    for _ in range(length):
        state += possible[math.floor(random.randrange(len(possible)))]

    return state

def requestAccessToken(code):
    """
    Called after a user accepts application request, exchange the authorization code for an Access Token with Spotify
    """

    # Token Request Url
    OAUTH_TOKEN_URL = "https://accounts.spotify.com/api/token"

    # Request body parameters: grant_type, code, and redirect_uri
    client_id = app.config["CLIENT_ID"]
    client_secret = app.config["SECRET_KEY"]
    redirect_uri = app.config["REDIRECT_URI"]

    # Request intention in getting a new token
    payload = {
        "grant_type" : "authorization_code", 
        "code" : code, 
        "redirect_uri" : redirect_uri, 
    }

    # POST request to get access token after user grants authorization to account data
    response = requests.post(OAUTH_TOKEN_URL, auth=(client_id, client_secret), data=payload)

    return response

def requestRefreshToken(refresh_token):
    """
    requestRefreshToken reacquires a new token from Spotify to retrieve playlist and track information
    """

    # Refresh token request url
    OAUTH_REFRESH_URL = "https://accounts.spotify.com/api/token"

    client_id = app.config["CLIENT_ID"]
    client_secret = app.config["SECRET_KEY"]

    # Request intention in getting a new token
    payload = {
        "grant_type" : "refresh_token", 
        "refresh_token" : refresh_token, 
    }

    # Header parameters to allow the application to make requests to the Spotify Web API
    # Authorization in the form of "Bearer <Access Token>"
    header = {
        "Authorization" : "Basic " + client_id,
    }

    response = requests.post(OAUTH_REFRESH_URL, auth=(client_id, client_secret), data=payload, headers=header)

    return response

def getListPlaylist(access_token):
    """
    getListPlaylist() is called after the user authorizes the application to access their information. The application then retrieves the current user's list of playlists and displays
    them on the homepage.
    """
    # URL to retrieve current user's playlist, reference: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-a-list-of-current-users-playlists
    playlists_url = "/me/playlists"

    # Header parameters to allow the application to make requests to the Spotify Web API
    # Authorization in the form of "Bearer <Access Token>"
    header = {
        "Authorization" : "Bearer " + access_token,
    }

    # Query parameters:
    # limit: 0 <= x <= 50
    # offset: 0 <= x <= 100.000
    param = {
        "limit" : 50,
    }

    # Send GET request to recieve playlist data with set query parameters
    response = requests.get(BASE_URL + playlists_url, headers=header, params=param)

    # Check if the response is valid else extract the details of 
    if response.status_code != 200:
        return "Failed"
    else:
        # Empty list to contain playlist data
        playlist_list = dict()

        # Iterate through each item or playlist in the user's library
        for playlist in response.json()["items"]:
            playlist_list[playlist["id"]] = playlist["name"]

    return playlist_list

def getTracksAudioFeatures(access_token, id_string):
    """
    getTracksAudioFeatures() retrieves the track list audio features, this includes danceability, energy, loudness, etc..
    """
    
    # URL to pass a list of tracks to get their audio features
    audio_features_url = f"/audio-features"

    # Header parameter to allow the application to make requests to the Spotify Web API
    header = {
        "Authorization" : "Bearer " + access_token,
    }

    # Query parameters:
    # ids: string of song ids separated by a comma
    param = {
        "ids" : id_string,
    }

    # GET request to recieve track audio information
    response = requests.get(BASE_URL + audio_features_url, headers=header, params=param)

    return response

def getPlaylistItems(access_token, playlist_id):
    """
    getPlaylistItems() compiles all information regarding to a particular song (i.e. song name, artist, danceability, etc...)
    """
    # URL to retrieve the current playlist's item, reference: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-playlists-tracks
    playlist_items_url = f"/playlists/{playlist_id}/tracks"

    # Header parameter to allow the application to make requests to the Spotify Web API
    header = {
        "Authorization" : "Bearer " + access_token,
    }

    # Query parameters:
    # additional_types: track, episode
    # limit: 0 <= x <= 50
    # offest: x >= 0, use limit to get the next set of items
    param = {
        "limit" : 50,
    }

    # GET request to recieve playlist tracklist
    items_response = requests.get(BASE_URL + playlist_items_url, headers=header, params=param)

    if items_response.status_code != 200:
        return "Failed"
    else:
        playlist_items_id = ""

        # Iterate through each song of the playlist and associated song id
        for item in items_response.json()["items"]:
            playlist_items_id += (item["track"]["id"] + ",")
        
    # GET resquest to recieve tracklist audio features
    features_response = getTracksAudioFeatures(access_token, playlist_items_id)

    if features_response.status_code != 200:
        return "Failed"
    else:
        # playlist_items_attributes is a list of dictionary items retaining song informations
        playlist_items_attributes = []

        # Iteration going through each song item and creating a dictionary object
        for i in range(len(features_response.json()["audio_features"])):

            song_information = items_response.json()["items"][i] # Information regarding song, artist, and album name, etc..
            song_features = features_response.json()["audio_features"][i] # Information regarding numerical analysis

            item_json = {
                "name" : song_information["track"]["name"], # Track name
                "artist" : song_information["track"]["artists"][0]["name"], # Artist name
                "album" : song_information["track"]["album"]["name"], # Album name
                "explicit" : song_information["track"]["explicit"], # Explicit warning
                "popularity" : song_information["track"]["popularity"], # Spotify popularity rating
                "danceability" : song_features["danceability"], # Danceability
                "energy" : song_features["energy"], # Energy
                "loudness" : song_features["loudness"], # Loudness
                "speechiness" : song_features["speechiness"], # Speechiness
                "liveness" : song_features["liveness"], # Liveness
                "tempo" : song_features["tempo"], # Tempo
            }

            playlist_items_attributes.append(item_json)

        attribute_json = json.dumps(playlist_items_attributes) # JSON conversion of list of dictionaries

    return attribute_json
"""
Functions that provide aid to calls to Spotify API
"""

import math
import random
import requests
from app import app
import json

BASE_URL = "https://api.spotify.com/v1"

def generateRandomString(length):
    """
    Spotify's proposal to generate randomly generated strings, https://developer.spotify.com/documentation/web-playback-sdk/guide/
    This is done to protect against attacks such as CSRF
    """

    state = ""
    possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

    for _ in range(length):
        state += possible[math.floor(random.randrange(len(possible)))]

    return state

def requestAccessToken(code):
    """
    Called after a user accepts application request, exchange the authorization code for an Access Token with Spotify
    """

    # Token Request Url
    OAUTH_TOKEN_URL = "https://accounts.spotify.com/api/token"

    # Request body parameters: grant_type, code, and redirect_uri
    client_id = app.config["CLIENT_ID"]
    client_secret = app.config["SECRET_KEY"]
    redirect_uri = app.config["REDIRECT_URI"]

    # Request intention in getting a new token
    payload = {
        "grant_type" : "authorization_code", 
        "code" : code, 
        "redirect_uri" : redirect_uri, 
    }

    # POST request to get access token after user grants authorization to account data
    response = requests.post(OAUTH_TOKEN_URL, auth=(client_id, client_secret), data=payload)

    return response

def requestRefreshToken(refresh_token):
    """
    requestRefreshToken reacquires a new token from Spotify to retrieve playlist and track information
    """

    # Refresh token request url
    OAUTH_REFRESH_URL = "https://accounts.spotify.com/api/token"

    client_id = app.config["CLIENT_ID"]
    client_secret = app.config["SECRET_KEY"]

    # Request intention in getting a new token
    payload = {
        "grant_type" : "refresh_token", 
        "refresh_token" : refresh_token, 
    }

    # Header parameters to allow the application to make requests to the Spotify Web API
    # Authorization in the form of "Bearer <Access Token>"
    header = {
        "Authorization" : "Basic " + client_id,
    }

    response = requests.post(OAUTH_REFRESH_URL, auth=(client_id, client_secret), data=payload, headers=header)

    return response

def getListPlaylist(access_token):
    """
    getListPlaylist() is called after the user authorizes the application to access their information. The application then retrieves the current user's list of playlists and displays
    them on the homepage.
    """
    # URL to retrieve current user's playlist, reference: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-a-list-of-current-users-playlists
    playlists_url = "/me/playlists"

    # Header parameters to allow the application to make requests to the Spotify Web API
    # Authorization in the form of "Bearer <Access Token>"
    header = {
        "Authorization" : "Bearer " + access_token,
    }

    # Query parameters:
    # limit: 0 <= x <= 50
    # offset: 0 <= x <= 100.000
    param = {
        "limit" : 50,
    }

    # Send GET request to recieve playlist data with set query parameters
    response = requests.get(BASE_URL + playlists_url, headers=header, params=param)

    # Check if the response is valid else extract the details of 
    if response.status_code != 200:
        return "Failed"
    else:
        # Empty list to contain playlist data
        playlist_list = dict()

        # Iterate through each item or playlist in the user's library
        for playlist in response.json()["items"]:
            playlist_list[playlist["id"]] = playlist["name"]

    return playlist_list

def getTracksAudioFeatures(access_token, id_string):
    """
    getTracksAudioFeatures() retrieves the track list audio features, this includes danceability, energy, loudness, etc..
    """
    
    # URL to pass a list of tracks to get their audio features
    audio_features_url = f"/audio-features"

    # Header parameter to allow the application to make requests to the Spotify Web API
    header = {
        "Authorization" : "Bearer " + access_token,
    }

    # Query parameters:
    # ids: string of song ids separated by a comma
    param = {
        "ids" : id_string,
    }

    # GET request to recieve track audio information
    response = requests.get(BASE_URL + audio_features_url, headers=header, params=param)

    return response

def getPlaylistItems(access_token, playlist_id):
    """
    getPlaylistItems() compiles all information regarding to a particular song (i.e. song name, artist, danceability, etc...)
    """
    # URL to retrieve the current playlist's item, reference: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-playlists-tracks
    playlist_items_url = f"/playlists/{playlist_id}/tracks"

    # Header parameter to allow the application to make requests to the Spotify Web API
    header = {
        "Authorization" : "Bearer " + access_token,
    }

    # Query parameters:
    # additional_types: track, episode
    # limit: 0 <= x <= 50
    # offest: x >= 0, use limit to get the next set of items
    param = {
        "limit" : 50,
    }

    # GET request to recieve playlist tracklist
    items_response = requests.get(BASE_URL + playlist_items_url, headers=header, params=param)

    if items_response.status_code != 200:
        return "Failed"
    else:
        playlist_items_id = ""

        # Iterate through each song of the playlist and associated song id
        for item in items_response.json()["items"]:
            playlist_items_id += (item["track"]["id"] + ",")
        
    # GET resquest to recieve tracklist audio features
    features_response = getTracksAudioFeatures(access_token, playlist_items_id)

    if features_response.status_code != 200:
        return "Failed"
    else:
        # playlist_items_attributes is a list of dictionary items retaining song informations
        playlist_items_attributes = []

        # Iteration going through each song item and creating a dictionary object
        for i in range(len(features_response.json()["audio_features"])):

            song_information = items_response.json()["items"][i] # Information regarding song, artist, and album name, etc..
            song_features = features_response.json()["audio_features"][i] # Information regarding numerical analysis

            item_json = {
                "name" : song_information["track"]["name"], # Track name
                "artist" : song_information["track"]["artists"][0]["name"], # Artist name
                "album" : song_information["track"]["album"]["name"], # Album name
                "explicit" : song_information["track"]["explicit"], # Explicit warning
                "popularity" : song_information["track"]["popularity"], # Spotify popularity rating
                "danceability" : song_features["danceability"], # Danceability
                "energy" : song_features["energy"], # Energy
                "loudness" : song_features["loudness"], # Loudness
                "speechiness" : song_features["speechiness"], # Speechiness
                "liveness" : song_features["liveness"], # Liveness
                "tempo" : song_features["tempo"], # Tempo
            }

            playlist_items_attributes.append(item_json)

        attribute_json = json.dumps(playlist_items_attributes) # JSON conversion of list of dictionaries

    return attribute_json
Functions that provide aid to calls to Spotify API Spotify's proposal to generate randomly generated strings, https://developer.spotify.com/documentation/web-playback-sdk/guide/     This is done to protect against attacks such as CSRF Called after a user accepts application request, exchange the authorization code for an Access Token with Spotify # Token Request Url # Request body parameters: grant_type, code, and redirect_uri # Request intention in getting a new token # POST request to get access token after user grants authorization to account data requestRefreshToken reacquires a new token from Spotify to retrieve playlist and track information # Refresh token request url # Request intention in getting a new token # Header parameters to allow the application to make requests to the Spotify Web API # Authorization in the form of "Bearer <Access Token>" getListPlaylist() is called after the user authorizes the application to access their information. The application then retrieves the current user's list of playlists and displays     them on the homepage. # URL to retrieve current user's playlist, reference: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-a-list-of-current-users-playlists # Header parameters to allow the application to make requests to the Spotify Web API # Authorization in the form of "Bearer <Access Token>" # Query parameters: # limit: 0 <= x <= 50 # offset: 0 <= x <= 100.000 # Send GET request to recieve playlist data with set query parameters # Check if the response is valid else extract the details of # Empty list to contain playlist data # Iterate through each item or playlist in the user's library getTracksAudioFeatures() retrieves the track list audio features, this includes danceability, energy, loudness, etc.. # URL to pass a list of tracks to get their audio features # Header parameter to allow the application to make requests to the Spotify Web API # Query parameters: # ids: string of song ids separated by a comma # GET request to recieve track audio information getPlaylistItems() compiles all information regarding to a particular song (i.e. song name, artist, danceability, etc...) # URL to retrieve the current playlist's item, reference: https://developer.spotify.com/documentation/web-api/reference/#/operations/get-playlists-tracks # Header parameter to allow the application to make requests to the Spotify Web API # Query parameters: # additional_types: track, episode # limit: 0 <= x <= 50 # offest: x >= 0, use limit to get the next set of items # GET request to recieve playlist tracklist # Iterate through each song of the playlist and associated song id # GET resquest to recieve tracklist audio features # playlist_items_attributes is a list of dictionary items retaining song informations # Iteration going through each song item and creating a dictionary object # Information regarding song, artist, and album name, etc.. # Information regarding numerical analysis # Track name # Artist name # Album name # Explicit warning # Spotify popularity rating # Danceability # Energy # Loudness # Speechiness # Liveness # Tempo # JSON conversion of list of dictionaries