import json
import os
from pathlib import Path
from typing import List, Dict, Any, Optional
from mutagen.mp4 import MP4


class SongListGenerator:
    """Utility class for generating song lists from MP4 files with ID3 tags."""
    
    def __init__(self, songlist_path: str = "data/songList.json"):
        self.songlist_path = Path(songlist_path)
        self.songlist_path.parent.mkdir(parents=True, exist_ok=True)
    
    def read_existing_songlist(self) -> List[Dict[str, Any]]:
        """Read existing song list from JSON file."""
        if self.songlist_path.exists():
            try:
                with open(self.songlist_path, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError) as e:
                print(f"⚠️ Warning: Could not read existing songlist: {e}")
                return []
        return []
    
    def save_songlist(self, songlist: List[Dict[str, Any]]) -> None:
        """Save song list to JSON file."""
        try:
            with open(self.songlist_path, 'w', encoding='utf-8') as f:
                json.dump(songlist, f, indent=2, ensure_ascii=False)
            print(f"✅ Song list saved to {self.songlist_path}")
        except IOError as e:
            print(f"❌ Error saving song list: {e}")
            raise
    
    def extract_id3_tags(self, mp4_path: Path) -> Optional[Dict[str, str]]:
        """Extract ID3 tags from MP4 file."""
        try:
            mp4 = MP4(str(mp4_path))
            
            # Extract artist and title from ID3 tags
            artist = mp4.get("\xa9ART", ["Unknown Artist"])[0] if "\xa9ART" in mp4 else "Unknown Artist"
            title = mp4.get("\xa9nam", ["Unknown Title"])[0] if "\xa9nam" in mp4 else "Unknown Title"
            
            return {
                "artist": artist,
                "title": title
            }
        except Exception as e:
            print(f"⚠️ Warning: Could not extract ID3 tags from {mp4_path.name}: {e}")
            return None
    
    def scan_directory_for_mp4_files(self, directory_path: str) -> List[Path]:
        """Scan directory for MP4 files."""
        directory = Path(directory_path)
        if not directory.exists():
            raise FileNotFoundError(f"Directory not found: {directory_path}")
        
        if not directory.is_dir():
            raise ValueError(f"Path is not a directory: {directory_path}")
        
        mp4_files = list(directory.glob("*.mp4"))
        if not mp4_files:
            print(f"⚠️ No MP4 files found in {directory_path}")
            return []
        
        print(f"📁 Found {len(mp4_files)} MP4 files in {directory.name}")
        return sorted(mp4_files)
    
    def generate_songlist_from_directory(self, directory_path: str, append: bool = True) -> Dict[str, Any]:
        """Generate a song list from MP4 files in a directory."""
        directory = Path(directory_path)
        directory_name = directory.name
        
        # Scan for MP4 files
        mp4_files = self.scan_directory_for_mp4_files(directory_path)
        if not mp4_files:
            return {}
        
        # Extract ID3 tags and create songs list
        songs = []
        for index, mp4_file in enumerate(mp4_files, start=1):
            id3_tags = self.extract_id3_tags(mp4_file)
            if id3_tags:
                song = {
                    "position": index,
                    "title": id3_tags["title"],
                    "artist": id3_tags["artist"]
                }
                songs.append(song)
                print(f"  {index:3d}. {id3_tags['artist']} - {id3_tags['title']}")
        
        if not songs:
            print("❌ No valid ID3 tags found in any MP4 files")
            return {}
        
        # Create the song list entry
        songlist_entry = {
            "title": directory_name,
            "songs": songs
        }
        
        # Handle appending to existing song list
        if append:
            existing_songlist = self.read_existing_songlist()
            
            # Check if a playlist with this title already exists
            existing_index = None
            for i, entry in enumerate(existing_songlist):
                if entry.get("title") == directory_name:
                    existing_index = i
                    break
            
            if existing_index is not None:
                # Replace existing entry
                print(f"🔄 Replacing existing playlist: {directory_name}")
                existing_songlist[existing_index] = songlist_entry
            else:
                # Add new entry to the beginning of the list
                print(f"➕ Adding new playlist: {directory_name}")
                existing_songlist.insert(0, songlist_entry)
            
            self.save_songlist(existing_songlist)
        else:
            # Create new song list with just this entry
            print(f"📝 Creating new song list with playlist: {directory_name}")
            self.save_songlist([songlist_entry])
        
        return songlist_entry
    
    def generate_songlist_from_multiple_directories(self, directory_paths: List[str], append: bool = True) -> List[Dict[str, Any]]:
        """Generate song lists from multiple directories."""
        results = []
        errors = []

        # Read existing song list once at the beginning
        existing_songlist = self.read_existing_songlist() if append else []
        
        for directory_path in directory_paths:
            try:
                print(f"\n📂 Processing directory: {directory_path}")
                directory = Path(directory_path)
                directory_name = directory.name

                # Scan for MP4 files
                mp4_files = self.scan_directory_for_mp4_files(directory_path)
                if not mp4_files:
                    continue

                # Extract ID3 tags and create songs list
                songs = []
                for index, mp4_file in enumerate(mp4_files, start=1):
                    id3_tags = self.extract_id3_tags(mp4_file)
                    if id3_tags:
                        song = {
                            "position": index,
                            "title": id3_tags["title"],
                            "artist": id3_tags["artist"]
                        }
                        songs.append(song)
                        print(f"  {index:3d}. {id3_tags['artist']} - {id3_tags['title']}")

                if not songs:
                    print("❌ No valid ID3 tags found in any MP4 files")
                    continue

                # Create the song list entry
                songlist_entry = {
                    "title": directory_name,
                    "songs": songs
                }

                # Check if a playlist with this title already exists
                existing_index = None
                for i, entry in enumerate(existing_songlist):
                    if entry.get("title") == directory_name:
                        existing_index = i
                        break

                if existing_index is not None:
                    # Replace existing entry
                    print(f"🔄 Replacing existing playlist: {directory_name}")
                    existing_songlist[existing_index] = songlist_entry
                else:
                    # Add new entry to the beginning of the list
                    print(f"➕ Adding new playlist: {directory_name}")
                    existing_songlist.insert(0, songlist_entry)

                results.append(songlist_entry)

            except Exception as e:
                error_msg = f"Error processing {directory_path}: {e}"
                print(f"❌ {error_msg}")
                errors.append(error_msg)

        # Save the final song list
        if results:
            if append:
                # Save the updated existing song list
                self.save_songlist(existing_songlist)
            else:
                # Create new song list with just the results
                self.save_songlist(results)

        # If there were any errors, raise an exception
        if errors:
            raise Exception(f"Failed to process {len(errors)} directories: {'; '.join(errors)}")

        return results


def main():
    """CLI entry point for song list generation."""
    import argparse
    import sys
    
    parser = argparse.ArgumentParser(
        description="Generate song lists from MP4 files with ID3 tags",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python -m karaoke_downloader.songlist_generator /path/to/mp4/directory
  python -m karaoke_downloader.songlist_generator /path/to/dir1 /path/to/dir2 --no-append
  python -m karaoke_downloader.songlist_generator /path/to/dir --songlist-path custom_songlist.json
        """
    )
    
    parser.add_argument(
        "directories",
        nargs="+",
        help="Directory paths containing MP4 files with ID3 tags"
    )
    
    parser.add_argument(
        "--no-append",
        action="store_true",
        help="Create a new song list instead of appending to existing one"
    )
    
    parser.add_argument(
        "--songlist-path",
        default="data/songList.json",
        help="Path to the song list JSON file (default: data/songList.json)"
    )
    
    args = parser.parse_args()
    
    try:
        generator = SongListGenerator(args.songlist_path)
        generator.generate_songlist_from_multiple_directories(
            args.directories, 
            append=not args.no_append
        )
        print("\n✅ Song list generation completed successfully!")
    except Exception as e:
        print(f"\n❌ Error: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main() 