"""
Centralized song processing logic for MusicBrainz Data Cleaner.
This module contains all song processing methods that should be shared
between CLI and database interactions to ensure consistency.
"""

from typing import Dict, Optional, Tuple, Any, List
from ..utils.artist_title_processing import parse_complex_collaboration, generate_title_variations
from ..utils.data_loader import data_loader
from .interfaces import MusicBrainzDataProvider, SongProcessorInterface


class SongProcessor(SongProcessorInterface):
    """Centralized song processing with consistent logic across all interfaces."""
    
    def __init__(self, data_provider: MusicBrainzDataProvider):
        """
        Initialize with a data provider that implements MusicBrainzDataProvider.
        
        Args:
            data_provider: Instance implementing MusicBrainzDataProvider interface
        """
        self.data_provider = data_provider
    
    def find_artist_mbid(self, artist_name: str, recording_title: str = None) -> Optional[str]:
        """
        Find artist MBID using consistent logic across all interfaces.
        
        Args:
            artist_name: Name of the artist to search for
            recording_title: Optional recording title to help choose the correct artist
            
        Returns:
            Artist MBID if found, None otherwise
        """
        if not artist_name:
            return None
        
        # If we have a recording title, use the improved search that considers the recording
        if recording_title and hasattr(self.data_provider, 'fuzzy_search_artist_with_recording'):
            result = self.data_provider.fuzzy_search_artist_with_recording(artist_name, recording_title)
            if result and isinstance(result, tuple) and len(result) >= 2:
                return result[1]  # Return MBID from tuple (artist_name, mbid, score)
        
        # Fallback to regular search
        result = self.data_provider.fuzzy_search_artist(artist_name)
        if result and isinstance(result, tuple) and len(result) >= 2:
            return result[1]  # Return MBID from tuple (artist_name, mbid, score)
        
        return None
    
    def find_recording_mbid(self, artist_mbid: str, title: str, original_artist: str = None) -> Optional[str]:
        """
        Find recording MBID using consistent logic across all interfaces.
        
        Args:
            artist_mbid: Artist MBID (None for collaborations)
            title: Song title
            original_artist: Original artist string (for collaboration verification)
            
        Returns:
            Recording MBID if found, None otherwise
        """
        if not title:
            return None
            
        # Use the shared title variations logic for comprehensive title cleaning
        title_variations = generate_title_variations(title)
        
        # Load known recordings data first
        known_recordings = data_loader.load_known_recordings()
        
        # Check known recordings first with all title variations
        for (known_artist_mbid, known_title), recording_mbid in known_recordings.items():
            if artist_mbid == known_artist_mbid:
                for variation in title_variations:
                    if variation.lower() == known_title.lower():
                        return recording_mbid
        
        # If we have an artist_mbid, try to find recording with that specific artist
        if artist_mbid:
            # Try to find recording with the specific artist MBID
            for variation in title_variations:
                result = self.data_provider.fuzzy_search_recording(variation, artist_mbid)
                if result and isinstance(result, tuple) and len(result) >= 2:
                    return result[1]  # Return recording MBID
        
        # If no artist_mbid or recording not found, try multiple artist candidates
        if original_artist and hasattr(self.data_provider, 'fuzzy_search_artist_candidates'):
            print(f"🎯 Trying multiple artist candidates for '{original_artist}' with recording '{title}'...")
            
            # Get multiple artist candidates
            candidates = self.data_provider.fuzzy_search_artist_candidates(original_artist, title)
            
            # Try each candidate to find the recording
            for i, candidate in enumerate(candidates[:5]):  # Limit to top 5 candidates
                candidate_mbid = candidate['gid']
                candidate_name = candidate['name']
                candidate_score = candidate['score']
                has_recording = candidate.get('has_recording', False)
                
                print(f"  🔍 Trying candidate {i+1}: {candidate_name} (score: {candidate_score:.2f}, has recording: {has_recording})")
                
                # Try to find recording with this artist candidate
                for variation in title_variations:
                    result = self.data_provider.fuzzy_search_recording(variation, candidate_mbid)
                    if result and isinstance(result, tuple) and len(result) >= 2:
                        recording_mbid = result[1]
                        print(f"  ✅ Found recording with candidate {i+1}: {candidate_name}")
                        return recording_mbid
        
        # Handle collaborations using artist credit (fallback)
        # If no artist_mbid (collaboration case), try to find by title and verify artist credit
        if not artist_mbid and original_artist:
            # This is a collaboration case, try to find by title with all variations
            for variation in title_variations:
                result = self.data_provider.fuzzy_search_recording(variation)
                if result and isinstance(result, tuple) and len(result) >= 2:
                    recording_mbid = result[1]
                    
                    # Verify that this recording has the correct artist credit
                    recording_info = self.get_recording_info(recording_mbid)
                    if recording_info and recording_info.get('artist_credit'):
                        # Check if the artist credit matches our expected collaboration
                        expected_artist_string = original_artist.replace(',', ' & ').replace(' and ', ' & ')
                        if recording_info['artist_credit'].lower() == expected_artist_string.lower():
                            return recording_mbid
                    
                    # If exact match fails, try partial match
                    if recording_info and recording_info.get('artist_credit'):
                        # Check if all artists in the collaboration are present in the recording
                        main_artist, collaborators = parse_complex_collaboration(original_artist)
                        recording_artists = recording_info['artist_credit'].lower()
                        
                        # Check if main artist is in the recording
                        if main_artist.lower() in recording_artists:
                            # Check if at least one collaborator is also present
                            for collaborator in collaborators:
                                if collaborator.lower() in recording_artists:
                                    return recording_mbid
        
        # Regular case with artist_mbid - try all title variations
        for variation in title_variations:
            result = self.data_provider.fuzzy_search_recording(variation, artist_mbid)
            if result and isinstance(result, tuple) and len(result) >= 2:
                return result[1]  # Return MBID from tuple (recording_name, mbid, score)
        
        return None
        
        return None
    
    def get_artist_info(self, mbid: str) -> Optional[Dict[str, Any]]:
        """Get artist info using consistent logic."""
        return self.data_provider.get_artist_info(mbid)
    
    def get_recording_info(self, mbid: str) -> Optional[Dict[str, Any]]:
        """Get recording info using consistent logic."""
        return self.data_provider.get_recording_info(mbid)
    
    def _build_artist_string(self, artist_credit: list) -> str:
        """Build artist string from artist credit list (API format)."""
        if not artist_credit:
            return ""
        parts = []
        for credit in artist_credit:
            parts.append(credit['name'])
            if credit.get('joinphrase'):
                parts.append(credit['joinphrase'])
        return ''.join(parts).strip()
    
    def clean_song(self, song: Dict[str, Any]) -> Tuple[Dict[str, Any], bool]:
        """
        Clean a single song using consistent logic across all interfaces.
        
        Args:
            song: Song dictionary with 'artist' and 'title' keys
            
        Returns:
            Tuple of (cleaned_song, success_status)
        """
        original_artist = song.get('artist', '')
        original_title = song.get('title', '')
        
        # Find artist MBID (pass recording title to help choose correct artist)
        artist_mbid = self.find_artist_mbid(song.get('artist', ''), song.get('title', ''))
        
        # Handle collaboration case (artist_mbid is None)
        # Use the same complex collaboration parsing as the database
        artist_name = song.get('artist', '')
        main_artist, collaborators = parse_complex_collaboration(artist_name)
        has_collaboration = len(collaborators) > 0
        
        if artist_mbid is None and has_collaboration:
            # Try to find recording using the improved find_recording_mbid method
            recording_mbid = self.find_recording_mbid(None, song.get('title', ''), original_artist)
            if recording_mbid:
                # Get recording info to update the song
                recording_info = self.get_recording_info(recording_mbid)
                if recording_info:
                    # Update with the correct artist credit and title
                    if recording_info.get('artist_credit'):
                        song['artist'] = recording_info['artist_credit']
                    elif recording_info.get('artist-credit'):
                        artist_string = self._build_artist_string(recording_info['artist-credit'])
                        if artist_string:
                            song['artist'] = artist_string
                    
                    song['title'] = recording_info['title']
                    song['recording_mbid'] = recording_mbid
                    
                    # For collaborations, try to get the main artist's MBID
                    main_artist, collaborators = parse_complex_collaboration(song.get('artist', ''))
                    if main_artist:
                        artist_result = self.data_provider.fuzzy_search_artist(main_artist)
                        if artist_result and isinstance(artist_result, tuple) and len(artist_result) >= 2:
                            song['mbid'] = artist_result[1]  # Set the main artist's MBID
                    
                    return song, True
            return song, False
        
        # Regular case (non-collaboration or collaboration not found)
        if not artist_mbid:
            return song, False
        
        # Get artist info
        artist_info = self.get_artist_info(artist_mbid)
        if artist_info:
            song['artist'] = artist_info['name']
            song['mbid'] = artist_mbid
        
        # Find recording MBID (pass original artist for multiple candidate search)
        recording_mbid = self.find_recording_mbid(artist_mbid, song.get('title', ''), original_artist)
        if not recording_mbid:
            return song, False
        
        # Get recording info
        recording_info = self.get_recording_info(recording_mbid)
        if recording_info:
            # Update artist string if there are multiple artists, but preserve the artist MBID
            if recording_info.get('artist_credit'):
                song['artist'] = recording_info['artist_credit']
                # Keep the original artist MBID even when updating artist name
                if 'mbid' not in song:
                    song['mbid'] = artist_mbid
            elif recording_info.get('artist-credit'):
                artist_string = self._build_artist_string(recording_info['artist-credit'])
                if artist_string:
                    song['artist'] = artist_string
                    # Keep the original artist MBID even when updating artist name
                    if 'mbid' not in song:
                        song['mbid'] = artist_mbid
            
            song['title'] = recording_info['title']
            song['recording_mbid'] = recording_mbid
            return song, True
        
        return song, False 