import pafy
import time
import tidalapi
import json
import re
from youtubesearchpython import VideosSearch
from src.database.model import DiscordServer, Track, Radio
from spotipy import Spotify, SpotifyClientCredentials
from tidal_dl import tidal
from os import listdir
from pony.orm import select
import lyricsgenius as lg
import string

from config import (
    SPOTIFY_CLIENT_ID,
    SPOTIFY_CLIENT_SECRET,
    GENIUS_API_KEY
)
from pony.orm import (
    db_session
)

client_credentials_manager = SpotifyClientCredentials(
    client_id=SPOTIFY_CLIENT_ID, client_secret=SPOTIFY_CLIENT_SECRET)
SPOTIFY = Spotify(client_credentials_manager=client_credentials_manager)
TIDAL_SESSION = tidalapi.Session()
TIDAL = tidal.TidalAPI()

GENIUS = lg.Genius(GENIUS_API_KEY, skip_non_songs=True, excluded_terms=[
                   "(Remix)", "(Live), [Music Video]"], remove_section_headers=True)


class Utils:

    def __init__(self):

        # TIDAL API Authentication
        if "credentials.json" not in listdir():
            TIDAL_SESSION.login_oauth_simple()
            with open("credentials.json", "w") as f:
                json.dump({
                    "token_type": TIDAL_SESSION.token_type,
                    "session_id": TIDAL_SESSION.session_id,
                    "access_token": TIDAL_SESSION.access_token,
                    "refresh_token": TIDAL_SESSION.refresh_token
                }, f, indent=True)

        else:
            session_dict = json.load(open("credentials.json"))

            TIDAL_SESSION.load_oauth_session(
                session_dict["session_id"],
                session_dict["token_type"],
                session_dict["access_token"],
                session_dict["refresh_token"]
            )

        TIDAL.loginByAccessToken(TIDAL_SESSION.access_token)

    @staticmethod
    def get_youtube_metadata(link):
        try:
            video = pafy.new(link)
        except OSError:
            return None
        title = video.title
        author = video.author
        duration = video.length
        # stream_flag = True if video.getbestaudio().url else False

        return {
            "title": title,
            "artist": author,
            "duration": duration,
            "youtube_link": link
        }

    @staticmethod
    def get_youtube_video(query):
        search = VideosSearch(query)
        if search.result()["result"]:
            return search.result()["result"][0]["id"]

    @staticmethod
    def get_spotify_metadata(link):
        if re.search("https:\/\/open\.spotify\.com\/track\/.+", link):
            return SPOTIFY.track(link)

        elif re.search("https:\/\/open\.spotify\.com\/playlist\/.+", link):
            return SPOTIFY.playlist(link)

        elif re.search("https:\/\/open\.spotify\.com\/album\/.+", link):
            return SPOTIFY.album(link)

        elif re.search("https:\/\/open\.spotify\.com\/artist\/.+", link):
            return SPOTIFY.artist_top_tracks(link)

    @staticmethod
    def get_tidal_url(track):

        title = re.sub(r'\([^)]*\)', '', track.title)

        if len(title.split('-')) > 1 and 'feat' in title.split('-')[1]:
            title = title.split('-')[0]

        query = "{artist} {title}".format(
            artist=track.artist,
            title=title
        )
        res = TIDAL_SESSION.search('track', query, 10)

        for tidal_track in res.tracks:
            msg, tidal_track = TIDAL.getTrack(id=tidal_track.id)

            if track.isrc and tidal_track.isrc == track.isrc:
                msg, stream = TIDAL.getStreamUrl(tidal_track.id, "Master")
                return stream.url

            track_title = re.sub(r'[^\w\s]', '', track.title)
            tidal_track.title = re.sub(r'[^\w\s]', '', tidal_track.title)

            if track_title in tidal_track.title or tidal_track.title in track_title:

                if abs(track.duration - tidal_track.duration) < 30:
                    msg, stream = TIDAL.getStreamUrl(tidal_track.id, "Master")
                    return stream.url

        return None

    @staticmethod
    def get_youtube_url(track):
        if track.youtube_link:
            video = pafy.new(track.youtube_link)
            return video.getbestaudio().url
        return None

    @staticmethod
    def song_counter(voice_client):
        sec = 0
        while True:
            time.sleep(0.5)
            new_sec = (voice_client._player.loops * 20) // 1000
            if new_sec != sec:
                sec = new_sec

    @staticmethod
    def search_url(track):
        try:
            if Utils.get_tidal_url(track):
                Utils.set_track_value(track.ID, {
                    "tidal_url": True
                })

            if Utils.get_youtube_url(track):
                Utils.set_track_value(track.ID, {
                    "youtube_url": True
                })

        except AttributeError:
            return

    @staticmethod
    def get_lyrics(track):
        song = GENIUS.search_song(track.title, track.artist)
        if not song:
            return None
        lyrics = song.lyrics.replace("EmbedShare URLCopyEmbedCopy", "")

        while lyrics[-1] in string.digits:
            lyrics = lyrics[:-1]

        if len(lyrics) > 4096:
            lyrics = lyrics[:4090] + "..."

        return lyrics

    #! TRACK TABLE METHODS

    @staticmethod
    @db_session
    def set_track_value(track_id, track_values):
        track = Track.get(ID=track_id)
        track.set(**track_values)

    #! RADIO TABLE METHODS

    @staticmethod
    @db_session
    def get_radio(values=None):
        if not values:
            return Radio.select()[:]

        if "lower_name" in values:
            radio = select(
                radio for radio in Radio if values["lower_name"] in radio.lower_name)[:]
            return radio[0] if radio else None

        return Radio.get(**values)

import pafy
import time
import tidalapi
import json
import re
from youtubesearchpython import VideosSearch
from src.database.model import DiscordServer, Track, Radio
from spotipy import Spotify, SpotifyClientCredentials
from tidal_dl import tidal
from os import listdir
from pony.orm import select
import lyricsgenius as lg
import string

from config import (
    SPOTIFY_CLIENT_ID,
    SPOTIFY_CLIENT_SECRET,
    GENIUS_API_KEY
)
from pony.orm import (
    db_session
)

client_credentials_manager = SpotifyClientCredentials(
    client_id=SPOTIFY_CLIENT_ID, client_secret=SPOTIFY_CLIENT_SECRET)
SPOTIFY = Spotify(client_credentials_manager=client_credentials_manager)
TIDAL_SESSION = tidalapi.Session()
TIDAL = tidal.TidalAPI()

GENIUS = lg.Genius(GENIUS_API_KEY, skip_non_songs=True, excluded_terms=[
                   "(Remix)", "(Live), [Music Video]"], remove_section_headers=True)


class Utils:

    def __init__(self):

        # TIDAL API Authentication
        if "credentials.json" not in listdir():
            TIDAL_SESSION.login_oauth_simple()
            with open("credentials.json", "w") as f:
                json.dump({
                    "token_type": TIDAL_SESSION.token_type,
                    "session_id": TIDAL_SESSION.session_id,
                    "access_token": TIDAL_SESSION.access_token,
                    "refresh_token": TIDAL_SESSION.refresh_token
                }, f, indent=True)

        else:
            session_dict = json.load(open("credentials.json"))

            TIDAL_SESSION.load_oauth_session(
                session_dict["session_id"],
                session_dict["token_type"],
                session_dict["access_token"],
                session_dict["refresh_token"]
            )

        TIDAL.loginByAccessToken(TIDAL_SESSION.access_token)

    @staticmethod
    def get_youtube_metadata(link):
        try:
            video = pafy.new(link)
        except OSError:
            return None
        title = video.title
        author = video.author
        duration = video.length
        # stream_flag = True if video.getbestaudio().url else False

        return {
            "title": title,
            "artist": author,
            "duration": duration,
            "youtube_link": link
        }

    @staticmethod
    def get_youtube_video(query):
        search = VideosSearch(query)
        if search.result()["result"]:
            return search.result()["result"][0]["id"]

    @staticmethod
    def get_spotify_metadata(link):
        if re.search("https:\/\/open\.spotify\.com\/track\/.+", link):
            return SPOTIFY.track(link)

        elif re.search("https:\/\/open\.spotify\.com\/playlist\/.+", link):
            return SPOTIFY.playlist(link)

        elif re.search("https:\/\/open\.spotify\.com\/album\/.+", link):
            return SPOTIFY.album(link)

        elif re.search("https:\/\/open\.spotify\.com\/artist\/.+", link):
            return SPOTIFY.artist_top_tracks(link)

    @staticmethod
    def get_tidal_url(track):

        title = re.sub(r'\([^)]*\)', '', track.title)

        if len(title.split('-')) > 1 and 'feat' in title.split('-')[1]:
            title = title.split('-')[0]

        query = "{artist} {title}".format(
            artist=track.artist,
            title=title
        )
        res = TIDAL_SESSION.search('track', query, 10)

        for tidal_track in res.tracks:
            msg, tidal_track = TIDAL.getTrack(id=tidal_track.id)

            if track.isrc and tidal_track.isrc == track.isrc:
                msg, stream = TIDAL.getStreamUrl(tidal_track.id, "Master")
                return stream.url

            track_title = re.sub(r'[^\w\s]', '', track.title)
            tidal_track.title = re.sub(r'[^\w\s]', '', tidal_track.title)

            if track_title in tidal_track.title or tidal_track.title in track_title:

                if abs(track.duration - tidal_track.duration) < 30:
                    msg, stream = TIDAL.getStreamUrl(tidal_track.id, "Master")
                    return stream.url

        return None

    @staticmethod
    def get_youtube_url(track):
        if track.youtube_link:
            video = pafy.new(track.youtube_link)
            return video.getbestaudio().url
        return None

    @staticmethod
    def song_counter(voice_client):
        sec = 0
        while True:
            time.sleep(0.5)
            new_sec = (voice_client._player.loops * 20) // 1000
            if new_sec != sec:
                sec = new_sec

    @staticmethod
    def search_url(track):
        try:
            if Utils.get_tidal_url(track):
                Utils.set_track_value(track.ID, {
                    "tidal_url": True
                })

            if Utils.get_youtube_url(track):
                Utils.set_track_value(track.ID, {
                    "youtube_url": True
                })

        except AttributeError:
            return

    @staticmethod
    def get_lyrics(track):
        song = GENIUS.search_song(track.title, track.artist)
        if not song:
            return None
        lyrics = song.lyrics.replace("EmbedShare URLCopyEmbedCopy", "")

        while lyrics[-1] in string.digits:
            lyrics = lyrics[:-1]

        if len(lyrics) > 4096:
            lyrics = lyrics[:4090] + "..."

        return lyrics

    #! TRACK TABLE METHODS

    @staticmethod
    @db_session
    def set_track_value(track_id, track_values):
        track = Track.get(ID=track_id)
        track.set(**track_values)

    #! RADIO TABLE METHODS

    @staticmethod
    @db_session
    def get_radio(values=None):
        if not values:
            return Radio.select()[:]

        if "lower_name" in values:
            radio = select(
                radio for radio in Radio if values["lower_name"] in radio.lower_name)[:]
            return radio[0] if radio else None

        return Radio.get(**values)

# TIDAL API Authentication # stream_flag = True if video.getbestaudio().url else False #! TRACK TABLE METHODS #! RADIO TABLE METHODS