text: """
  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