import os
import requests
import logging
import random
import time
from flask import current_app
from musicround.models import Song, db
from musicround.helpers.metadata import get_song_metadata_by_isrc

logger = logging.getLogger(__name__)

class DeezerClient:
    """
    Client for interacting with the Deezer API
    Handles searching and importing songs, albums, and playlists
    """
    
    def __init__(self):
        self.base_url = "https://api.deezer.com"
        self.logger = logging.getLogger(__name__)
    
    def _make_request(self, endpoint, params=None):
        """Make a GET request to the Deezer API"""
        url = f"{self.base_url}/{endpoint}"
        try:
            response = requests.get(url, params=params)
            response.raise_for_status()
            return response.json()
        except requests.RequestException as e:
            self.logger.error(f"Deezer API request error: {e}")
            return None
    
    def search_tracks(self, query, limit=50):
        """Search for tracks on Deezer"""
        params = {
            'q': query,
            'limit': limit
        }
        result = self._make_request('search/track', params=params)
        if result and 'data' in result:
            return result['data']
        return []
    
    def search_albums(self, query, limit=25):
        """Search for albums on Deezer"""
        params = {
            'q': query,
            'limit': limit
        }
        result = self._make_request('search/album', params=params)
        if result and 'data' in result:
            return result['data']
        return []
    
    def search_playlists(self, query, limit=25):
        """Search for playlists on Deezer"""
        params = {
            'q': query,
            'limit': limit
        }
        result = self._make_request('search/playlist', params=params)
        if result and 'data' in result:
            return result['data']
        return []
    
    def get_track(self, track_id):
        """Get details for a specific track"""
        return self._make_request(f'track/{track_id}')
    
    def get_album(self, album_id):
        """Get details for a specific album"""
        return self._make_request(f'album/{album_id}')
    
    def get_album_tracks(self, album_id):
        """Get tracks from a specific album"""
        result = self._make_request(f'album/{album_id}/tracks')
        if result and 'data' in result:
            return result['data']
        return []
    
    def get_playlist(self, playlist_id):
        """Get details for a specific playlist"""
        return self._make_request(f'playlist/{playlist_id}')
    
    def get_playlist_tracks(self, playlist_id):
        """Get tracks from a specific playlist"""
        result = self._make_request(f'playlist/{playlist_id}/tracks')
        if result and 'data' in result:
            return result['data']
        return []
    
    def get_popular_playlists(self, limit=30):
        """
        Get popular playlists from Deezer
        This uses a set of predetermined searches to find popular playlists
        """
        playlists = []
        search_terms = ['hits', 'top', 'chart', 'popular', 'best', 'essential']
        
        # Search for each term and combine results
        for term in search_terms:
            results = self.search_playlists(term, limit=10)
            playlists.extend(results)
            
            # Avoid rate limiting
            time.sleep(0.1)
        
        # Filter for playlists with reasonable track counts (avoid tiny playlists)
        playlists = [p for p in playlists if p.get('nb_tracks', 0) >= 10]
        
        # Sort by popularity or track count
        playlists.sort(key=lambda x: x.get('nb_tracks', 0), reverse=True)
        
        # Shuffle and limit results
        random.shuffle(playlists)
        return playlists[:limit]
    
    def get_genre_from_lastfm(self, artist_name, track_name, lastfm_api_key):
        """
        Fetch genre from Last.fm API
        Returns the genre string, or empty string if not found
        """
        if not lastfm_api_key:
            return ""
            
        url = 'http://ws.audioscrobbler.com/2.0/'
        params = {
            'method': 'track.getInfo',
            'api_key': lastfm_api_key,
            'artist': artist_name,
            'track': track_name,
            'format': 'json'
        }
        
        try:
            response = requests.get(url=url, params=params).json()
            
            # If present, use the first top-level tag as "genre"
            if ('track' in response and 
                'toptags' in response['track'] and 
                'tag' in response['track']['toptags'] and 
                response['track']['toptags']['tag']):
                return response['track']['toptags']['tag'][0]['name']
            return ""
        except Exception as e:
            self.logger.error(f"Last.fm API error: {e}")
            return ""
    def import_track(self, track_id, lastfm_api_key=None):
        """
        Import a track from Deezer into the database
        Returns a tuple (Song object, was_new) where was_new indicates if this was a new import
        """
        track_info = self.get_track(track_id)
        
        if not track_info:
            self.logger.error(f"Could not fetch track with ID {track_id}")
            return None, False
            
        # Check if the track has a preview URL (required for our application)
        preview_url = track_info.get('preview')
        if not preview_url:
            self.logger.warning(f"Track {track_info.get('title')} has no preview URL")
            return None, False

        # Extract ISRC
        isrc = track_info.get('isrc')
            
        # Check if this track is already in our database by Deezer ID or ISRC
        existing_song = None
        if isrc:
            existing_song = Song.query.filter_by(isrc=isrc).first()
        if not existing_song:
            existing_song = Song.query.filter_by(deezer_id=str(track_info['id'])).first()
        
        if existing_song:
            self.logger.info(f"Track {track_info.get('title')} (Deezer ID: {track_info['id']}, ISRC: {isrc}) already exists in database with ID {existing_song.id}")
            # If ISRC was missing and we found it now, update the existing record
            if isrc and not existing_song.isrc:
                existing_song.isrc = isrc
                if existing_song.deezer_id is None: # If it was matched by ISRC but didn't have deezer_id
                    existing_song.deezer_id = str(track_info['id'])
                try:
                    db.session.commit()
                    self.logger.info(f"Updated ISRC for existing song {existing_song.id} to {isrc}")
                except Exception as e:
                    db.session.rollback()
                    self.logger.error(f"Error updating ISRC for existing song {existing_song.id}: {e}")
            
            # Optionally, trigger metadata refresh if ISRC is now available or if desired
            if existing_song.isrc:
                try:
                    app_context = current_app._get_current_object()
                    updated_metadata = get_song_metadata_by_isrc(existing_song.isrc, app=app_context)
                    if updated_metadata:
                        # Update song fields from aggregated metadata
                        existing_song.title = updated_metadata.get('title', existing_song.title)
                        existing_song.artist = updated_metadata.get('artist_name', existing_song.artist)
                        existing_song.year = updated_metadata.get('year', existing_song.year)
                        existing_song.genre = updated_metadata.get('genre', existing_song.genre)
                        # ... update other relevant fields ...
                        if updated_metadata.get('spotify_id') and not existing_song.spotify_id:
                            existing_song.spotify_id = updated_metadata.get('spotify_id')
                        if updated_metadata.get('cover_url') and not existing_song.cover_url: # Prioritize existing cover if any
                             existing_song.cover_url = updated_metadata.get('cover_url')
                        if updated_metadata.get('preview_url') and not existing_song.preview_url: # Prioritize existing preview if any
                             existing_song.preview_url = updated_metadata.get('preview_url')

                        db.session.commit()
                        self.logger.info(f"Refreshed metadata for existing song {existing_song.id} using ISRC {existing_song.isrc}")
                except Exception as e:
                    db.session.rollback()
                    self.logger.error(f"Error refreshing metadata for existing song {existing_song.id}: {e}")
            return existing_song, False  # Return existing song with was_new=False
            
        # Get additional artist details if needed
        artist_name = track_info.get('artist', {}).get('name', '')
        
        # Get album release year
        album_id = track_info.get('album', {}).get('id')
        release_year = ''
        cover_url = ''
        
        if album_id:
            album_info = self.get_album(album_id)
            if album_info:
                release_date = album_info.get('release_date', '')
                release_year = release_date[:4] if release_date else ''
                # Get highest quality cover image
                cover_url = album_info.get('cover_xl') or album_info.get('cover_big') or album_info.get('cover_medium', '')
        
        # Get genre from Last.fm (can be removed if metadata aggregation handles it)
        # genre = self.get_genre_from_lastfm(artist_name, track_info.get('title', ''), lastfm_api_key)
        genre = None # Will be populated by metadata aggregation if ISRC is present
        
        # Create new Song object
        new_song = Song(
            deezer_id=str(track_info['id']),
            spotify_id=None,  # Will be populated by metadata aggregation if ISRC is present
            title=track_info.get('title', ''),
            artist=artist_name,
            genre=genre,
            year=release_year,
            preview_url=preview_url,
            cover_url=cover_url,
            popularity=track_info.get('rank', 0), # Deezer 'rank' can be used as popularity
            isrc=isrc, # Save the ISRC
            used_count=0
        )
        
        try:
            db.session.add(new_song)
            db.session.commit()
            self.logger.info(f"Imported track '{new_song.title}' by {new_song.artist} with Deezer ID {new_song.deezer_id} and ISRC {new_song.isrc}")

            # If ISRC is present, fetch and update with aggregated metadata
            if new_song.isrc:
                try:
                    app_context = current_app._get_current_object()
                    aggregated_metadata = get_song_metadata_by_isrc(new_song.isrc, app=app_context)
                    if aggregated_metadata:
                        new_song.title = aggregated_metadata.get('title', new_song.title)
                        new_song.artist = aggregated_metadata.get('artist_name', new_song.artist)
                        new_song.year = aggregated_metadata.get('year', new_song.year)
                        new_song.genre = aggregated_metadata.get('genre', new_song.genre)
                        new_song.spotify_id = aggregated_metadata.get('spotify_id', new_song.spotify_id)
                        # Update cover and preview URLs if they are better or missing
                        if aggregated_metadata.get('cover_url'):
                             new_song.cover_url = aggregated_metadata.get('cover_url')
                        if aggregated_metadata.get('preview_url'):
                             new_song.preview_url = aggregated_metadata.get('preview_url')                        # Potentially update popularity if a more universal score is available
                        # new_song.popularity = aggregated_metadata.get('popularity', new_song.popularity)
                        db.session.commit()
                        self.logger.info(f"Updated new song {new_song.id} with aggregated metadata using ISRC {new_song.isrc}")
                except Exception as e:
                    db.session.rollback()
                    self.logger.error(f"Error updating new song {new_song.id} with aggregated metadata: {e}")
            return new_song, True  # Return new song with was_new=True
        except Exception as e:
            db.session.rollback()
            self.logger.error(f"Error saving track to database: {e}")
            return None, False
    def import_album(self, album_id, lastfm_api_key=None):
        """
        Import all tracks from an album
        Returns a dictionary with import statistics
        """
        tracks = self.get_album_tracks(album_id)
        imported_songs = []
        skipped_songs = []
        
        for track in tracks:
            track_id = track.get('id')
            if track_id:
                song, was_new = self.import_track(track_id, lastfm_api_key)
                if song:
                    if was_new:
                        imported_songs.append(song)
                    else:
                        skipped_songs.append(song)
                    
                # Add a small delay to avoid overwhelming the API
                time.sleep(0.2)
        
        return {
            'imported_songs': imported_songs,
            'skipped_songs': skipped_songs,
            'imported_count': len(imported_songs),
            'skipped_count': len(skipped_songs)
        }
    def import_playlist(self, playlist_id, lastfm_api_key=None):
        """
        Import all tracks from a playlist
        Returns a dictionary with import statistics
        """
        tracks = self.get_playlist_tracks(playlist_id)
        imported_songs = []
        skipped_songs = []
        
        for track in tracks:
            track_id = track.get('id')
            if track_id:
                song, was_new = self.import_track(track_id, lastfm_api_key)
                if song:
                    if was_new:
                        imported_songs.append(song)
                    else:
                        skipped_songs.append(song)
                    
                # Add a small delay to avoid overwhelming the API
                time.sleep(0.2)
        
        return {
            'imported_songs': imported_songs,
            'skipped_songs': skipped_songs,
            'imported_count': len(imported_songs),
            'skipped_count': len(skipped_songs)
        }