from email.message import EmailMessage
import smtplib
import base64
import os
import sys
#CHANGE THIS TO WHERE YOUR secrets.py and database FILES ARE
sys.path.insert(1, 'j:\\Spotty\secrets_and_database')
from refresh import Refresh
import difflib
import json
import requests
import time
import sqlite3
from alive_progress import alive_bar
from sqlite3 import Error
from does_everything_secrets import spotify_user_id

refreshCaller = Refresh()

class Reducer:
    def __init__(self):
        self.email_address = "vikingindustries2023@gmail.com"
        self.email_password = b'dGFvZSBlZnluIHF2dmwgZnZ4eQ=='
        self.bitpassword = base64.b64decode(self.email_password)
        self.email_password = self.bitpassword.decode('utf-8')
        self.spotify_token = ""
        self.unwanted_path = "C:\\Users\AlSmi\Dropbox\MUSIC-TIN\\Unwanted" # CHANGE TO YOUR 'TIN' PATH
        #self.database = r"CHANGE THIS TO YOUR MASTER PATH\\MASTER.db" SEE LINE 7 ABOVE
        self.database = r"j:\Spotty\secrets_and_database\\Endless Feed.db" # REM THIS OUT
        self.limit = 0.9
        self.user_id = spotify_user_id
        self.live_found = False
        self.bad_names_file_1 = "OLLIE UNWANTED ARTISTS.TXT"
        self.bad_names_file_2 = "FREDDY UNWANTED ARTISTS.TXT"
        self.viking_unwanted_file = "VIKING UNWANTED.TXT"
        self.message = ""
        self.unplayables =[]
        self.bad_names = []
        self.viking_unwanted = []
        self.source_uri = []
        self.source_track = []
        self.source_artist = []
        self.source_playable = []
        self.review_uri = []
        self.review_track = []
        self.review_artist = []
        self.unplay_uri = []
        self.unplay_track = []
        self.unplay_artist = []
##########################################################################################################
########## ENTER SOURCE PLAYLIST HERE ####################################################################
        self.source_playlist_id = "0Suqn7pstvLbi16i8eF1KL"
##########################################################################################################
        self.target_playlist_id = ""  #NOTE: LEAVE THIS BLANK
        self.market = "GB"
 
    def reduction_begins(self):
        os.system('cls')
        print("\033[91mThe Reducer v.1.4.0 ======== Enter Source Playlist ID in Line 52....\033[0m")
        print("\033[91m        and adjust lines 7, 27, 28 & 29 to your paths ...\033[0m")
        print("\033[91mRemoves Tracks already present in MASTER/ENDLESS FEED and UNPLAYABLE tracks.\033[0m")
        print("\033[91m        Emails user with Unavailable Track results.\033[0m")
        print("\033[33m..Only suitable for use with 'non-personal' playlists as a new 'Reduced' playlist is built\033[0m")
        print("\033[33m... Not Really Suitable for use with MASTER or ENDLESS FEED Playlist ...\033[0m")
        self.unwanted()
        self.vik_unwanted()
        self.get_source_info()
        self.get_source_tracks()
        self.check_tracks()
        self.create_target_playlist()
        self.add_tracks_to_target()
        print()
        self.call_refresh()
        self.add_to_message()
        print()
        self.send_message()

    def get_source_info(self):
        query = "https://api.spotify.com/v1/playlists/{}".format(self.source_playlist_id)
        response = requests.request("get", url=query, headers={"Content-type": "application/json", "Authorization": "Bearer {}".format(self.spotify_token)})
        json_data = json.loads(response.text)
        self.queue_total = (json_data["tracks"]["total"])
        self.playlist_name = (json_data["name"])
        newname = ""
        for c in self.playlist_name:
            if 0 <= ord(c) <= 127:
                newname = newname + c                            
            else:
                self.playlist_name = newname
        return

    def get_source_tracks(self):
        with alive_bar(self.queue_total, title="Getting Playlist Tracks") as bar:
            t = 0
            while t < self.queue_total:
                try:
                    if t > 99:
                        if str(t)[-3:] == "000":
                            self.call_refresh()
                    self.offset = t
                    query = "https://api.spotify.com/v1/playlists/{}/tracks".format(self.source_playlist_id)
                    response = requests.request("get", url=query, headers={"Content-type": "application/json", "Authorization": "Bearer {}".format(self.spotify_token)}, params={"limit": 1, "market": self.market, "offset": self.offset})
                    json_data = json.loads(response.text)
                    self.track_uri = json_data["items"][0]["track"]["uri"]
                    self.track_name = json_data["items"][0]["track"]["name"]
                    self.artist_name = json_data["items"][0]["track"]["artists"][0]["name"]
                    self.not_playable = json_data["items"][0]["track"]["is_playable"]
                    self.configure_the_information()
                    if response.status_code == 200:
                        if not self.not_playable:
                            self.unplay_uri.append(self.track_uri)
                            self.unplay_track.append(self.track_name)
                            self.unplay_artist.append(self.artist_name)
                            bar()
                            t+=1
                        else:
                            self.source_uri.append(self.track_uri)
                            self.source_track.append(self.track_name)
                            self.source_artist.append(self.artist_name)
                            self.source_playable.append(self.not_playable)
                            bar()
                            t = t + 1
                            continue
                except:
                    print()
                    print("Get Source Track error  " + str(t))
                    continue
        return

    def check_tracks(self):
        conn = self.create_connection(self.database)
        cur = conn.cursor()
        t=0
        while t < len(self.source_uri):
            print("Checking Track " + str(t) + " of " + str(len(self.source_uri)), end='\r')        
            self.artist_found = False
            self.new_uri = self.source_uri[t]
            self.new_track = self.source_track[t]
            self.new_artist = self.source_artist[t]
            cur.execute("SELECT URI FROM Track_list WHERE URI = '" + self.new_uri + "'")
            wow = cur.fetchone()
            if wow is None:
                self.bad_name_check()
                if self.artist_found:
                    t += 1
                    continue
                self.sort_out_the_track_name()
                self.track_similarity_check()
                if not self.found:
                    self.review_uri.append(self.new_uri)
                    self.review_track.append(self.new_track)
                    self.review_artist.append(self.new_artist)
            t += 1

    def track_similarity_check(self):
        conn = self.create_connection(self.database)
        cur = conn.cursor()
        cur.execute("SELECT * FROM Track_list")
        rows2 = cur.fetchall()
        for row in rows2:
            self.track_name = row[1]
            self.artist_name = row[2]
            self.found = False
            self.string_similarity(self.new_track, self.track_name)
            if self.result_ratio == 1:
                self.string_similarity(self.new_artist, self.artist_name)
                if self.result_ratio > self.limit:
                    self.found = True
                    return
            if self.result_ratio > self.limit:
                self.string_similarity(self.new_artist, self.artist_name)
                if self.result_ratio > self.limit:
                    self.found = True
                    return

    def string_similarity(self, str1, str2):
        result = difflib.SequenceMatcher(a=str1.lower(), b=str2.lower())
        self.result_ratio = result.ratio()
        return self.result_ratio

    def create_target_playlist(self):
        print()
        self.call_refresh()
        try:
            query = "https://api.spotify.com/v1/users/{}/playlists".format(self.user_id)
            request_body = json.dumps({"name": self.playlist_name + " - Reduced", "description": "Tracks to Review from " + self.playlist_name + " Original playlist", "public": True})
            response = requests.post(query, data=request_body, headers={"Content-Type": "application/json","Authorization": "Bearer {}".format(self.spotify_token)})
            response_json = response.json()
            self.rebuilt_playlist_id = response_json["id"]
        except:
            print("Can't create reduced playlist")
            print(self.user_id)
            time.sleep(5)
            self.create_target_playlist()
        return

    def add_tracks_to_target(self):
        print()
        t = 0
        while t < len(self.review_uri):
            try:
                self.add_new_uri = self.review_uri[t]
                self.live_found = False
                print("Adding Track " + str(t) + " of " + str(len(self.review_uri)) + " to Playlist", end='\r')
                self.check_new_track()
                if not self.live_found:
                    self.new_track_for_target()
                t+=1
            except:
                print()
                print("Add to target failure  " + str(t))
                continue

    def check_new_track(self):
        new_id = self.add_new_uri[14:]
        query = "https://api.spotify.com/v1/tracks/{}".format(new_id)
        response = requests.request("get", url=query, headers={"Content-type": "application/json", "Authorization": "Bearer {}".format(self.spotify_token)})
        json_data = json.loads(response.text)
        new_track_name = json_data["name"]
        new_album_name = json_data["album"]["name"]
        vkung = 0
        while vkung < len(self.viking_unwanted):
            gencheck = new_track_name.lower()
            if self.viking_unwanted[vkung] in gencheck:
                self.live_found = True
                return
            gencheck = new_album_name.lower()
            if self.viking_unwanted[vkung] in gencheck:
                self.live_found = True
                return
            vkung+=1
        return

    def new_track_for_target(self):
        query = "https://api.spotify.com/v1/playlists/{}/tracks?uris={}".format(self.rebuilt_playlist_id, self.add_new_uri)
        response = requests.request("post", url=query, headers={"Content-type": "application/json","Authorization":"Bearer {}".format(self.spotify_token)})
        return

    def add_to_message(self):
        t=0
        while t < len(self.unplay_uri):
            self.message = self.message + str(self.unplay_uri[t]) + " - " + str(self.unplay_track[t]) + " - " + str(self.unplay_artist[t]) + "\n\n"
            t+=1
            continue

    def send_message(self):
        print("Sending Message")
        if self.message == "":
            self.message = "No Unplayable Tracks Found....."
        if spotify_user_id == "daftjoe":
            addressee = "allanlockwoodsmith@gmail.com"
        if spotify_user_id == "hddd-1960":
            addressee = "peter.eddowes@gmail.com"
        msg = EmailMessage()
        msg['Subject'] = self.playlist_name + " - Reduced. Unplayable Tracks."
        msg['From'] = self.email_address
        msg['To'] = addressee
        msg.set_content("The following tracks were found unplayable in playlist - " + self.playlist_name + "\n Review them and replace with working versions if applicable \n\n" + self.message + "\n\n DO NOT REPLY TO THIS EMAIL IT IS NOT MONITORED!")

        with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
            smtp.login(self.email_address, self.email_password)
            smtp.send_message(msg)

    def create_connection(self, db_file):
        conn = None
        try:
            conn = sqlite3.connect(db_file)
            return conn
        except Error as e1:
            print("Connection Error")
            print(e1)
        return conn

    def call_refresh(self):
        global refreshCaller
        while True:
            self.spotify_token = refreshCaller.refresh()
            if self.spotify_token == -1:
                print(
                    "==============================\n[!] Error refreshing token, retrying in 30 Seconds\n==============================")
                time.sleep(30)
            else:
                break

    def unwanted(self):
        try:
            f = open(self.unwanted_path + '\\' + self.bad_names_file_1, encoding='utf-8')
            for line in f:
                self.bad_names.append(line.strip())
            return
        except FileNotFoundError:
            try:
                f = open(self.unwanted_path + '\\' + self.bad_names_file_2, encoding='utf-8')
                for line in f:
                    self.bad_names.append(line.strip())
                return
            except FileNotFoundError:
                return
        
    def vik_unwanted(self):
        f = open(self.unwanted_path + '\\' +
                 self.viking_unwanted_file, encoding='utf-8')
        for line in f:
            self.viking_unwanted.append(line.rstrip())

    def bad_name_check(self):
        for name in self.bad_names:
            if name in self.new_artist:
                self.artist_found = True
                return
        return

    def configure_the_information(self):
        self.track_name = self.track_name.replace("'", "")
        self.artist_name = self.artist_name.replace("'", "")
        self.hyphen = str(self.track_name).find(" - ")
        if self.hyphen != -1:
            self.track_name = self.track_name[:self.hyphen]
        self.sqbrack = str(self.track_name).find("[")
        if self.sqbrack != -1:
            self.track_name = self.track_name[:self.sqbrack]
        self.feat = str(self.track_name).find(" (feat. ")
        if self.feat != -1:
            self.track_name = self.track_name[:self.feat]
        self.remaster = str(self.track_name).find("(Remastered)")
        if self.remaster != -1:
            self.track_name = self.track_name[:self.remaster]
        self.rerecord = str(self.track_name).find("(Rerecorded)")
        if self.rerecord != -1:
            self.track_name = self.track_name[:self.rerecord]
        self.digrem = str(self.track_name).find("(Digitally")
        if self.digrem != -1:
            self.track_name = self.track_name[:self.digrem]
        self.dubstep = str(self.track_name).find("(Dubstep")
        if self.dubstep != -1:
            self.track_name = self.track_name[:self.dubstep]
        self.track_name = self.track_name.strip()
        self.artist_name = self.artist_name.strip()

    def sort_out_the_track_name(self):
        self.new_track = self.new_track.replace("'", "")
        self.new_artist = self.new_artist.replace("'", "")
        self.hyphen = str(self.new_track).find(" - ")
        if self.hyphen != -1:
            self.new_track = self.new_track[:self.hyphen]
        self.sqbrack = str(self.new_track).find("[")
        if self.sqbrack != -1:
            self.new_track = self.new_track[:self.sqbrack]
        self.feat = str(self.new_track).find(" (feat. ")
        if self.feat != -1:
            self.new_track = self.new_track[:self.feat]
        self.remaster = str(self.new_track).find("(Remastered)")
        if self.remaster != -1:
            self.new_track = self.new_track[:self.remaster]
        self.rerecord = str(self.new_track).find("(Rerecorded)")
        if self.rerecord != -1:
            self.new_track = self.new_track[:self.rerecord]
        self.digrem = str(self.new_track).find("(Digitally")
        if self.digrem != -1:
            self.new_track = self.new_track[:self.digrem]
        self.dubstep = str(self.new_track).find("(Dubstep")
        if self.dubstep != -1:
            self.new_track = self.new_track[:self.dubstep]
        self.new_track = self.new_track.strip()
        self.new_artist = self.new_artist.strip()

a = Reducer()
a.call_refresh()
a.reduction_begins()