#!/usr/bin/env python3
"""
create and manage Extended M3U (EXTM3U) playlists (compatible with M3U)
"""
import sys
import os
import sqlite3
import argparse
from pathlib import Path

# Handle imports for both package and standalone execution
try:
    from . import metadata
except ImportError:
    # Add parent directory to path for standalone execution
    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from core import metadata

# Default database path
DEFAULT_DB_PATH = "walrio_library.db"

# Supported audio extensions
AUDIO_EXTENSIONS = {'.mp3', '.flac', '.ogg', '.oga', '.opus', '.m4a', '.mp4', '.aac', '.wv', '.ape', '.mpc', '.wav'}

def connect_to_database(db_path):
    """Connect to the SQLite database and return connection.
    
    Args:
        db_path: Path to the SQLite database file.
        
    Returns:
        Database connection object or None on failure.
    """
    if not os.path.exists(db_path):
        print(f"Error: Database not found: {db_path}")
        return None
    try:
        conn = sqlite3.connect(db_path)
        conn.row_factory = sqlite3.Row
        return conn
    except Exception as e:
        print(f"Error connecting to database: {e}")
        return None

def get_songs_from_database(conn, filters=None):
    """Get songs from database based on filters.
    
    Args:
        conn: Database connection object.
        filters: Optional dict with 'artist', 'album', 'genre' keys for filtering.
        
    Returns:
        List of song records matching the filters.
    """
    cursor = conn.cursor()
    query = "SELECT * FROM songs WHERE unavailable = 0"
    params = []
    
    if filters:
        if 'artist' in filters and filters['artist']:
            query += " AND (artist LIKE ? OR albumartist LIKE ?)"
            artist_filter = f"%{filters['artist']}%"
            params.extend([artist_filter, artist_filter])
        if 'album' in filters and filters['album']:
            query += " AND album LIKE ?"
            params.append(f"%{filters['album']}%")
        if 'genre' in filters and filters['genre']:
            query += " AND genre LIKE ?"
            params.append(f"%{filters['genre']}%")
    
    query += " ORDER BY artist, album, disc, track"
    cursor.execute(query, params)
    return cursor.fetchall()

def format_song_info(song):
    """Format song information for display.
    
    Args:
        song: Song record dict with keys like 'artist', 'title', 'album', etc.
        
    Returns:
        Formatted string with song information.
    """
    artist = song.get('artist') or 'Unknown Artist'
    title = song.get('title') or 'Unknown Title'
    album = song.get('album') or 'Unknown Album'
    
    # Format duration
    duration = ""
    if song.get('length'):
        minutes = song['length'] // 60
        seconds = song['length'] % 60
        duration = f" [{minutes}:{seconds:02d}]"
    
    # Format track number
    track = ""
    if song.get('track'):
        track = f"{song['track']:02d}. "
    
    return f"{track}{artist} - {title} ({album}){duration}"

def get_relative_path(file_path, playlist_path):
    """Convert file path to relative path from playlist location.
    
    Args:
        file_path: Absolute path to audio file.
        playlist_path: Path to the playlist file.
        
    Returns:
        Relative path from playlist directory to the file.
    """
    # Remove file:// prefix if present
    if file_path.startswith('file://'):
        file_path = file_path[7:]
    
    try:
        file_p = Path(file_path).resolve()
        playlist_dir = Path(playlist_path).parent.resolve()
        return os.path.relpath(file_p, playlist_dir)
    except (ValueError, OSError):
        # If relative path calculation fails, return original path
        return file_path

def create_m3u_playlist(songs, playlist_path, use_absolute_paths=False, playlist_name="Playlist"):
    """Create Extended M3U (EXTM3U) playlist file with metadata from a list of songs.
    
    Args:
        songs: List of song dicts with metadata.
        playlist_path: Output path for the playlist file.
        use_absolute_paths: Use absolute paths instead of relative.
        playlist_name: Name/title for the playlist.
        
    Returns:
        True if successful, False otherwise.
    """
    try:
        # Ensure directory exists
        playlist_dir = Path(playlist_path).parent
        playlist_dir.mkdir(parents=True, exist_ok=True)
        
        with open(playlist_path, 'w', encoding='utf-8') as f:
            # Write M3U header
            f.write("#EXTM3U\n")
            f.write(f"#PLAYLIST:{playlist_name}\n")
            f.write(f"#EXTENC:UTF-8\n")
            f.write(f"# Generated by Walrio Playlist Manager\n")
            f.write(f"# Total tracks: {len(songs)}\n\n")
            
            for song in songs:
                # Get file path
                if 'url' in song and song['url']:
                    file_path = song['url']
                    if file_path.startswith('file://'):
                        file_path = file_path[7:]
                elif 'filepath' in song:
                    file_path = song['filepath']
                else:
                    continue
                
                # Get metadata
                title = song.get('title', Path(file_path).stem)
                artist = song.get('artist', 'Unknown Artist')
                length = int(song.get('length', 0))
                
                # Write EXTINF line
                f.write(f"#EXTINF:{length},{artist} - {title}\n")
                
                # Write file path (relative or absolute)
                if use_absolute_paths:
                    # Convert to absolute path
                    abs_path = os.path.abspath(file_path)
                    f.write(f"{abs_path}\n")
                else:
                    rel_path = get_relative_path(file_path, playlist_path)
                    # Add ./ prefix for visual clarity if not already present
                    if not rel_path.startswith('./') and not rel_path.startswith('../'):
                        rel_path = f"./{rel_path}"
                    f.write(f"{rel_path}\n")
        
        print(f"Playlist created: {playlist_path} ({len(songs)} songs)")
        return True
    except Exception as e:
        print(f"Error creating playlist: {e}")
        return False

def load_m3u_playlist(playlist_path):
    """Load songs from M3U/Extended M3U playlist file.
    
    Args:
        playlist_path: Path to the M3U playlist file.
        
    Returns:
        List of song dicts with metadata and file paths.
    """
    if not os.path.exists(playlist_path):
        print(f"Error: Playlist not found: {playlist_path}")
        return []
    
    songs = []
    playlist_dir = Path(playlist_path).parent
    
    try:
        with open(playlist_path, 'r', encoding='utf-8') as f:
            current_info = {}
            
            for line in f:
                line = line.strip()
                
                if line.startswith('#EXTINF:'):
                    # Parse EXTINF line: #EXTINF:duration,artist - title
                    parts = line[8:].split(',', 1)
                    if len(parts) == 2:
                        try:
                            current_info['length'] = int(parts[0])
                        except:
                            current_info['length'] = 0
                        
                        # Try to parse artist - title
                        if ' - ' in parts[1]:
                            artist, title = parts[1].split(' - ', 1)
                            current_info['artist'] = artist.strip()
                            current_info['title'] = title.strip()
                        else:
                            current_info['title'] = parts[1].strip()
                
                elif line and not line.startswith('#'):
                    # This is a file path
                    file_path = line
                    
                    # Convert relative paths to absolute
                    if not os.path.isabs(file_path):
                        # Relative paths are relative to the playlist directory
                        file_path = os.path.abspath(os.path.join(playlist_dir, file_path))
                    
                    # Extract full metadata from the audio file
                    metadata_info = extract_metadata(file_path)
                    
                    if metadata_info:
                        # Use extracted metadata but prefer M3U info for artist/title if available
                        song = metadata_info.copy()
                        
                        # Override with M3U info if available (M3U might have corrected info)
                        if current_info.get('artist'):
                            song['artist'] = current_info['artist']
                        if current_info.get('title'):
                            song['title'] = current_info['title']
                        if current_info.get('length'):
                            song['length'] = current_info['length']
                    else:
                        # Fallback to basic M3U info if metadata extraction fails
                        song = {
                            'url': f"file://{file_path}",
                            'filepath': file_path,
                            'title': current_info.get('title', Path(file_path).stem),
                            'artist': current_info.get('artist', 'Unknown Artist'),
                            'album': 'Unknown Album',
                            'albumartist': current_info.get('artist', 'Unknown Artist'),
                            'length': current_info.get('length', 0),
                            'track': 0,
                            'disc': 0,
                            'year': 0,
                            'genre': 'Unknown'
                        }
                    
                    songs.append(song)
                    current_info = {}
        
        return songs
    except Exception as e:
        print(f"Error loading playlist: {e}")
        return []

def extract_metadata(file_path):
    """Extract metadata from audio file using the centralized metadata module.
    
    Args:
        file_path: Path to the audio file.
        
    Returns:
        Dict with metadata including title, artist, album, etc.
    """
    try:
        return metadata.extract_metadata_for_playlist(file_path)
    except Exception as e:
        print(f"Warning: Could not extract metadata from {file_path}: {e}")
        return {
            'url': f"file://{file_path}",
            'filepath': file_path,
            'title': Path(file_path).stem,
            'artist': 'Unknown Artist',
            'album': 'Unknown Album',
            'albumartist': 'Unknown Artist',
            'length': 0,
            'track': 0,
            'disc': 0,
            'year': 0,
            'genre': 'Unknown'
        }

def scan_directory(directory_path, recursive=False):
    """Scan directory for audio files.
    
    Args:
        directory_path: Path to directory to scan
        recursive: If True, scan subdirectories recursively
    """
    audio_files = []
    
    if recursive:
        # Scan recursively with os.walk
        for root, dirs, files in os.walk(directory_path):
            for file in sorted(files):
                file_path = os.path.join(root, file)
                if Path(file_path).suffix.lower() in AUDIO_EXTENSIONS:
                    audio_files.append(file_path)
    else:
        # Only scan top level directory
        if os.path.isdir(directory_path):
            for file in sorted(os.listdir(directory_path)):
                file_path = os.path.join(directory_path, file)
                if os.path.isfile(file_path) and Path(file_path).suffix.lower() in AUDIO_EXTENSIONS:
                    audio_files.append(file_path)
    
    return audio_files

def create_playlist_from_inputs(inputs, playlist_path, use_absolute_paths=False, playlist_name="Playlist", recursive=False):
    """Create playlist from list of files and folders.
    
    Args:
        inputs: List of file or directory paths
        playlist_path: Output path for the playlist
        use_absolute_paths: Use absolute paths in playlist
        playlist_name: Name of the playlist
        recursive: Scan directories recursively
    """
    songs = []
    
    for input_path in inputs:
        if not os.path.exists(input_path):
            print(f"Warning: Path not found: {input_path}")
            continue
        
        if os.path.isfile(input_path):
            # Single file
            if Path(input_path).suffix.lower() in AUDIO_EXTENSIONS:
                meta = extract_metadata(input_path)
                if meta:
                    songs.append(meta)
                else:
                    # Add without metadata
                    songs.append({
                        'url': f"file://{input_path}",
                        'filepath': input_path,
                        'title': Path(input_path).stem,
                        'artist': 'Unknown Artist',
                        'album': 'Unknown Album',
                        'length': 0
                    })
        elif os.path.isdir(input_path):
            # Directory - scan with or without recursion
            scan_msg = f"Scanning directory{' recursively' if recursive else ''}: {input_path}"
            print(scan_msg)
            audio_files = scan_directory(input_path, recursive=recursive)
            
            for file_path in audio_files:
                meta = extract_metadata(file_path)
                if meta:
                    songs.append(meta)
                else:
                    songs.append({
                        'url': f"file://{file_path}",
                        'filepath': file_path,
                        'title': Path(file_path).stem,
                        'artist': 'Unknown Artist',
                        'album': 'Unknown Album',
                        'length': 0
                    })
    
    if not songs:
        print("No songs found in specified inputs.")
        return False
    
    return create_m3u_playlist(songs, playlist_path, use_absolute_paths, playlist_name)

def main():
    """Main function for playlist management command-line interface."""
    parser = argparse.ArgumentParser(
        description='Playlist Manager - Create and manage Extended M3U playlists (EXTM3U format)',
        epilog='Examples:\n'
               '  python playlist.py --name "My Playlist" --artist "Pink Floyd" --output playlists/\n'
               '  python playlist.py --name "Files" --inputs song1.mp3 /path/to/music/\n'
               '  python playlist.py --load existing_playlist.m3u',
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    
    # Playlist creation options
    parser.add_argument('--name', help='Name of the playlist')
    parser.add_argument('--output', '-o', help='Output directory or full path for playlist file')
    parser.add_argument('--absolute', action='store_true', help='Use absolute paths instead of relative')
    parser.add_argument('--recursive', '-r', action='store_true', help='Scan directories recursively')
    
    # Database query options
    parser.add_argument('--db-path', default=DEFAULT_DB_PATH, 
                       help=f'Path to the SQLite database file (default: {DEFAULT_DB_PATH})')
    parser.add_argument('--artist', help='Filter by artist (partial match)')
    parser.add_argument('--album', help='Filter by album (partial match)')
    parser.add_argument('--genre', help='Filter by genre (partial match)')
    
    # File/directory input options
    parser.add_argument('--inputs', nargs='+', help='Files or directories to include in playlist')
    parser.add_argument('--input-file', help='Text file containing list of files (one per line)')
    
    # Load existing playlist
    parser.add_argument('--load', help='Load and display existing M3U/Extended M3U playlist')
    
    args = parser.parse_args()
    
    # Load existing playlist
    if args.load:
        songs = load_m3u_playlist(args.load)
        if songs:
            print(f"\nPlaylist: {args.load}")
            print(f"Total songs: {len(songs)}\n")
            for i, song in enumerate(songs[:20], 1):
                print(f"{i}. {song.get('artist', 'Unknown')} - {song.get('title', 'Unknown')}")
            if len(songs) > 20:
                print(f"\n... and {len(songs) - 20} more songs")
        return 0 if songs else 1
    
    # Validate name and output
    if not args.name:
        print("Error: --name is required for playlist creation")
        return 1
    
    # Determine output path
    if args.output:
        if os.path.isdir(args.output):
            output_path = os.path.join(args.output, f"{args.name}.m3u")
        else:
            output_path = args.output
    else:
        output_path = f"{args.name}.m3u"
    
    # Create playlist from database or inputs
    if args.inputs or args.input_file:
        # Create from files/directories
        inputs = args.inputs or []
        
        if args.input_file:
            if os.path.exists(args.input_file):
                with open(args.input_file, 'r') as f:
                    inputs.extend([line.strip() for line in f if line.strip()])
            else:
                print(f"Error: Input file not found: {args.input_file}")
                return 1
        
        if create_playlist_from_inputs(inputs, output_path, args.absolute, args.name, args.recursive):
            return 0
        else:
            return 1
    else:
        # Create from database
        conn = connect_to_database(args.db_path)
        if not conn:
            return 1
        
        # Build filters
        filters = {}
        if args.artist:
            filters['artist'] = args.artist
        if args.album:
            filters['album'] = args.album
        if args.genre:
            filters['genre'] = args.genre
        
        # Get songs
        songs = get_songs_from_database(conn, filters)
        conn.close()
        
        if not songs:
            print("No songs found matching the specified criteria.")
            return 1
        
        print(f"Found {len(songs)} songs")
        
        if create_m3u_playlist(songs, output_path, args.absolute, args.name):
            return 0
        else:
            return 1

if __name__ == '__main__':
    sys.exit(main())
