"""
Artist lookup table utilities for MusicBrainz Data Cleaner.
Provides fallback artist matching when database search fails.
"""

import json
import os
from pathlib import Path
from typing import Dict, Optional, List, Any
from fuzzywuzzy import fuzz


class ArtistLookupTable:
    """Handles artist name variations lookup for fallback matching."""
    
    def __init__(self, lookup_file: str = "data/artist_lookup.json"):
        """
        Initialize the artist lookup table.
        
        Args:
            lookup_file: Path to the JSON lookup file
        """
        self.lookup_file = Path(lookup_file)
        self.lookup_data = self._load_lookup_data()
    
    def _load_lookup_data(self) -> Dict[str, Any]:
        """Load the artist lookup data from JSON file."""
        try:
            if self.lookup_file.exists():
                with open(self.lookup_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    print(f"📚 Loaded artist lookup table with {len(data.get('artist_variations', {}))} artists")
                    return data
            else:
                print(f"⚠️  Artist lookup file not found: {self.lookup_file}")
                return {"artist_variations": {}, "metadata": {}}
        except Exception as e:
            print(f"❌ Error loading artist lookup table: {e}")
            return {"artist_variations": {}, "metadata": {}}
    
    def find_artist_by_variation(self, artist_name: str, min_score: float = 0.8) -> Optional[Dict[str, Any]]:
        """
        Find artist by name variation using fuzzy matching.
        
        Args:
            artist_name: The artist name to search for
            min_score: Minimum fuzzy match score (0.0 to 1.0)
            
        Returns:
            Artist info dict with mbid, name, and score, or None if not found
        """
        if not artist_name or not self.lookup_data.get('artist_variations'):
            return None
        
        artist_name_clean = artist_name.strip()
        best_match = None
        best_score = 0
        
        for canonical_name, artist_info in self.lookup_data['artist_variations'].items():
            # Check against canonical name
            score = fuzz.ratio(artist_name_clean.lower(), canonical_name.lower()) / 100.0
            if score > best_score and score >= min_score:
                best_score = score
                best_match = {
                    'canonical_name': canonical_name,
                    'mbid': artist_info['mbid'],
                    'score': score,
                    'notes': artist_info.get('notes', ''),
                    'match_type': 'canonical_name'
                }
            
            # Check against variations
            for variation in artist_info.get('variations', []):
                score = fuzz.ratio(artist_name_clean.lower(), variation.lower()) / 100.0
                if score > best_score and score >= min_score:
                    best_score = score
                    best_match = {
                        'canonical_name': canonical_name,
                        'mbid': artist_info['mbid'],
                        'score': score,
                        'notes': artist_info.get('notes', ''),
                        'match_type': 'variation',
                        'matched_variation': variation
                    }
        
        return best_match
    
    def add_artist_variation(self, canonical_name: str, mbid: str, variations: List[str], notes: str = "") -> bool:
        """
        Add a new artist variation to the lookup table.
        
        Args:
            canonical_name: The canonical artist name
            mbid: The MusicBrainz ID
            variations: List of name variations
            notes: Optional notes about the artist
            
        Returns:
            True if successfully added, False otherwise
        """
        try:
            if not self.lookup_data.get('artist_variations'):
                self.lookup_data['artist_variations'] = {}
            
            self.lookup_data['artist_variations'][canonical_name] = {
                'mbid': mbid,
                'variations': variations,
                'notes': notes
            }
            
            # Update metadata
            if 'metadata' not in self.lookup_data:
                self.lookup_data['metadata'] = {}
            
            self.lookup_data['metadata']['total_artists'] = len(self.lookup_data['artist_variations'])
            
            return self._save_lookup_data()
            
        except Exception as e:
            print(f"❌ Error adding artist variation: {e}")
            return False
    
    def _save_lookup_data(self) -> bool:
        """Save the lookup data back to the JSON file."""
        try:
            # Ensure directory exists
            self.lookup_file.parent.mkdir(parents=True, exist_ok=True)
            
            with open(self.lookup_file, 'w', encoding='utf-8') as f:
                json.dump(self.lookup_data, f, indent=2, ensure_ascii=False)
            
            print(f"💾 Saved artist lookup table to {self.lookup_file}")
            return True
            
        except Exception as e:
            print(f"❌ Error saving artist lookup table: {e}")
            return False
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get statistics about the lookup table."""
        if not self.lookup_data.get('artist_variations'):
            return {'total_artists': 0, 'total_variations': 0}
        
        total_variations = sum(
            len(artist_info.get('variations', [])) + 1  # +1 for canonical name
            for artist_info in self.lookup_data['artist_variations'].values()
        )
        
        return {
            'total_artists': len(self.lookup_data['artist_variations']),
            'total_variations': total_variations,
            'metadata': self.lookup_data.get('metadata', {})
        }
    
    def list_artists(self) -> List[str]:
        """Get a list of all canonical artist names in the lookup table."""
        return list(self.lookup_data.get('artist_variations', {}).keys())
    
    def search_artists(self, query: str, min_score: float = 0.6) -> List[Dict[str, Any]]:
        """
        Search for artists in the lookup table.
        
        Args:
            query: Search query
            min_score: Minimum fuzzy match score
            
        Returns:
            List of matching artists with scores
        """
        results = []
        query_lower = query.lower()
        
        for canonical_name, artist_info in self.lookup_data.get('artist_variations', {}).items():
            # Check canonical name
            score = fuzz.ratio(query_lower, canonical_name.lower()) / 100.0
            if score >= min_score:
                results.append({
                    'canonical_name': canonical_name,
                    'mbid': artist_info['mbid'],
                    'score': score,
                    'match_type': 'canonical_name',
                    'notes': artist_info.get('notes', '')
                })
            
            # Check variations
            for variation in artist_info.get('variations', []):
                score = fuzz.ratio(query_lower, variation.lower()) / 100.0
                if score >= min_score:
                    results.append({
                        'canonical_name': canonical_name,
                        'mbid': artist_info['mbid'],
                        'score': score,
                        'match_type': 'variation',
                        'matched_variation': variation,
                        'notes': artist_info.get('notes', '')
                    })
        
        # Sort by score (highest first)
        results.sort(key=lambda x: x['score'], reverse=True)
        return results


# Global instance for easy access
artist_lookup = ArtistLookupTable()


def find_artist_fallback(artist_name: str, min_score: float = 0.8) -> Optional[Dict[str, Any]]:
    """
    Convenience function to find artist using the lookup table.
    
    Args:
        artist_name: The artist name to search for
        min_score: Minimum fuzzy match score
        
    Returns:
        Artist info dict or None if not found
    """
    return artist_lookup.find_artist_by_variation(artist_name, min_score)


def add_artist_fallback(canonical_name: str, mbid: str, variations: List[str], notes: str = "") -> bool:
    """
    Convenience function to add artist variation to lookup table.
    
    Args:
        canonical_name: The canonical artist name
        mbid: The MusicBrainz ID
        variations: List of name variations
        notes: Optional notes about the artist
        
    Returns:
        True if successfully added, False otherwise
    """
    return artist_lookup.add_artist_variation(canonical_name, mbid, variations, notes) 