import obspython as obs
import os
import subprocess
import json
from datetime import datetime

# ==================== Configuration ====================
music_folder = ""
text_source_name = ""
output_file = ""
default_text = "your_brand_name"
update_interval = 2000  # milliseconds
show_format = "{title} - {artist}"  # Customizable format
enabled = True

# ==================== State ====================
metadata_cache = {}
last_file = ""
last_song = ""
play_history = []
stats = {"total_plays": 0, "session_start": None}

# ==================== Script Info ====================
def script_description():
    return """<center><h2>🎵 VLC Now Playing Monitor</h2></center>
    
    <p>Automatically detects and displays currently playing songs from VLC Video Source.</p>
    
    <p><b>Quick Setup:</b></p>
    <ol>
        <li>Create a <b>Text (GDI+)</b> or <b>Text (FreeType 2)</b> source</li>
        <li>Select it in 'Text Source' dropdown below</li>
        <li>Set your music folder path</li>
        <li>Customize the display format</li>
        <li>Done! It updates automatically</li>
    </ol>
    
    <p><b>Format Variables:</b></p>
    <ul>
        <li><code>{title}</code> - Song title</li>
        <li><code>{artist}</code> - Artist name</li>
        <li><code>{album}</code> - Album name</li>
        <li><code>{filename}</code> - File name</li>
    </ul>
    
    <p><i>Example: "{title} - {artist}" displays "Bohemian Rhapsody - Queen"</i></p>
    
    <hr>
    <p><small>v1.0 | Compatible with OBS 27+ | Requires: lsof, mutagen (optional)</small></p>
    """

def script_properties():
    props = obs.obs_properties_create()
    
    # ===== Main Settings =====
    obs.obs_properties_add_text(
        props, "section_main", "<b>━━━ Main Settings ━━━</b>",
        obs.OBS_TEXT_INFO
    )
    
    # Music folder
    obs.obs_properties_add_path(
        props, "music_folder", "📁 Music Folder:",
        obs.OBS_PATH_DIRECTORY, "", None
    )
    
    # Text source selection
    p = obs.obs_properties_add_list(
        props, "text_source", "📺 Text Source:",
        obs.OBS_COMBO_TYPE_EDITABLE,
        obs.OBS_COMBO_FORMAT_STRING
    )
    
    # Populate with text sources
    sources = obs.obs_enum_sources()
    if sources:
        for source in sources:
            source_id = obs.obs_source_get_unversioned_id(source)
            if source_id in ["text_gdiplus", "text_ft2_source", "text_gdiplus_v2"]:
                name = obs.obs_source_get_name(source)
                obs.obs_property_list_add_string(p, name, name)
    obs.source_list_release(sources)
    
    # ===== Display Settings =====
    obs.obs_properties_add_text(
        props, "section_display", "\n<b>━━━ Display Settings ━━━</b>",
        obs.OBS_TEXT_INFO
    )
    
    # Format string
    obs.obs_properties_add_text(
        props, "show_format", "🎨 Display Format:",
        obs.OBS_TEXT_DEFAULT
    )
    
    # Default text
    obs.obs_properties_add_text(
        props, "default_text", "⏸️ Default Text (when not playing):",
        obs.OBS_TEXT_DEFAULT
    )
    
    # Update interval
    obs.obs_properties_add_int_slider(
        props, "update_interval", "⏱️ Update Interval (seconds):",
        1, 10, 1
    )
    
    # ===== Advanced Settings =====
    obs.obs_properties_add_text(
        props, "section_advanced", "\n<b>━━━ Advanced Settings ━━━</b>",
        obs.OBS_TEXT_INFO
    )
    
    # Output file (optional)
    obs.obs_properties_add_path(
        props, "output_file", "💾 Also Save To File (optional):",
        obs.OBS_PATH_FILE_SAVE, "Text files (*.txt)", None
    )
    
    # Enable/disable
    obs.obs_properties_add_bool(
        props, "enabled", "✅ Enable Monitoring"
    )
    
    # Clear cache button
    obs.obs_properties_add_button(
        props, "clear_cache", "🗑️ Clear Metadata Cache",
        clear_cache_callback
    )
    
    # ===== Status =====
    obs.obs_properties_add_text(
        props, "section_status", "\n<b>━━━ Status ━━━</b>",
        obs.OBS_TEXT_INFO
    )
    
    # Status info
    status_text = get_status_text()
    obs.obs_properties_add_text(
        props, "status", status_text,
        obs.OBS_TEXT_INFO
    )
    
    return props

def script_defaults(settings):
    obs.obs_data_set_default_string(settings, "default_text", "BeatBoy")
    obs.obs_data_set_default_int(settings, "update_interval", 2)
    obs.obs_data_set_default_string(settings, "show_format", "{title} - {artist}")
    obs.obs_data_set_default_string(settings, "music_folder", "/home/turka/radio/music")
    obs.obs_data_set_default_string(settings, "output_file", "")
    obs.obs_data_set_default_bool(settings, "enabled", True)

def script_update(settings):
    global music_folder, output_file, default_text, text_source_name
    global update_interval, show_format, enabled
    
    music_folder = obs.obs_data_get_string(settings, "music_folder")
    output_file = obs.obs_data_get_string(settings, "output_file")
    default_text = obs.obs_data_get_string(settings, "default_text")
    text_source_name = obs.obs_data_get_string(settings, "text_source")
    show_format = obs.obs_data_get_string(settings, "show_format")
    enabled = obs.obs_data_get_bool(settings, "enabled")
    update_interval = obs.obs_data_get_int(settings, "update_interval") * 1000
    
    # Restart timer
    obs.timer_remove(update_song)
    if enabled and text_source_name and music_folder:
        obs.timer_add(update_song, update_interval)
        script_log("Monitoring started")
    else:
        script_log("Monitoring stopped")

def script_load(settings):
    global stats
    stats["session_start"] = datetime.now()
    script_log("Script loaded successfully")

def script_unload():
    obs.timer_remove(update_song)
    script_log(f"Script unloaded. Total plays this session: {stats['total_plays']}")

def script_save(settings):
    # Save state if needed
    pass

# ==================== Helper Functions ====================
def script_log(message):
    """Log message to OBS script log"""
    print(f"[VLC Monitor] {message}")

def get_status_text():
    """Generate status text for UI"""
    status_lines = []
    
    if stats["session_start"]:
        uptime = datetime.now() - stats["session_start"]
        status_lines.append(f"⏱️ Uptime: {str(uptime).split('.')[0]}")
    
    status_lines.append(f"📊 Songs played: {stats['total_plays']}")
    status_lines.append(f"💾 Cache size: {len(metadata_cache)} items")
    
    if last_song:
        status_lines.append(f"🎵 Current: {last_song}")
    
    return "\n".join(status_lines)

def clear_cache_callback(props, prop):
    """Clear metadata cache"""
    global metadata_cache
    old_size = len(metadata_cache)
    metadata_cache.clear()
    script_log(f"Cleared {old_size} cached items")
    return True

def get_playing_file():
    """Find which music file is currently being read"""
    if not music_folder or not os.path.exists(music_folder):
        return None
    
    try:
        result = subprocess.run(
            ['lsof', '+D', music_folder],
            capture_output=True,
            text=True,
            timeout=2
        )
        
        lines = result.stdout.split('\n')
        extensions = ('.mp3', '.flac', '.ogg', '.m4a', '.wav', '.opus', '.aac')
        
        for line in lines:
            if any(ext in line.lower() for ext in extensions):
                parts = line.split()
                if len(parts) > 8:
                    filepath = ' '.join(parts[8:])
                    if os.path.exists(filepath) and os.path.isfile(filepath):
                        return filepath
        
        # Fallback: try obs process specifically
        result = subprocess.run(
            ['lsof', '-c', 'obs'],
            capture_output=True,
            text=True,
            timeout=2
        )
        
        for line in result.stdout.split('\n'):
            if music_folder in line and any(ext in line.lower() for ext in extensions):
                parts = line.split()
                if len(parts) > 8:
                    filepath = ' '.join(parts[8:])
                    if os.path.exists(filepath) and os.path.isfile(filepath):
                        return filepath
        
        return None
        
    except Exception as e:
        script_log(f"Error getting file: {e}")
        return None

def get_song_metadata(filepath):
    """Get song metadata from file"""
    global metadata_cache
    
    # Check cache
    try:
        mtime = os.path.getmtime(filepath)
        cache_key = f"{filepath}:{mtime}"
        
        if cache_key in metadata_cache:
            return metadata_cache[cache_key]
    except:
        pass
    
    # Default metadata
    metadata = {
        'title': os.path.splitext(os.path.basename(filepath))[0].replace('_', ' '),
        'artist': '',
        'album': '',
        'filename': os.path.basename(filepath)
    }
    
    # Try to read with mutagen
    try:
        from mutagen import File as MutagenFile
        
        audio = MutagenFile(filepath)
        if audio:
            # Get title
            if 'title' in audio:
                title = audio['title']
                metadata['title'] = str(title[0]) if isinstance(title, list) else str(title)
            
            # Get artist
            if 'artist' in audio:
                artist = audio['artist']
                metadata['artist'] = str(artist[0]) if isinstance(artist, list) else str(artist)
            
            # Get album
            if 'album' in audio:
                album = audio['album']
                metadata['album'] = str(album[0]) if isinstance(album, list) else str(album)
            
            # Clean up
            metadata = {k: v.strip() if v else '' for k, v in metadata.items()}
            
    except ImportError:
        script_log("Mutagen not installed - using filename only")
    except Exception as e:
        script_log(f"Error reading metadata: {e}")
    
    # Cache it
    try:
        cache_key = f"{filepath}:{mtime}"
        metadata_cache[cache_key] = metadata
        
        # Limit cache size
        if len(metadata_cache) > 500:
            # Remove oldest half
            keys = list(metadata_cache.keys())
            for key in keys[:250]:
                del metadata_cache[key]
    except:
        pass
    
    return metadata

def format_song_display(metadata):
    """Format song info according to user template"""
    try:
        # Use user's format string
        display = show_format.format(**metadata)
        
        # Clean up empty fields
        display = display.replace(' - ', ' ').replace('  ', ' ').strip()
        
        # If nothing meaningful, use title
        if not display or display == '-':
            display = metadata.get('title', default_text)
        
        return display
    except Exception as e:
        script_log(f"Format error: {e}")
        # Fallback
        if metadata.get('artist'):
            return f"{metadata['title']} - {metadata['artist']}"
        return metadata.get('title', default_text)

def update_text_source(text):
    """Update the OBS text source"""
    if not text_source_name:
        return False
    
    source = obs.obs_get_source_by_name(text_source_name)
    if source:
        settings = obs.obs_data_create()
        obs.obs_data_set_string(settings, "text", f" {text}")
        obs.obs_source_update(source, settings)
        obs.obs_data_release(settings)
        obs.obs_source_release(source)
        return True
    return False

def write_to_file(text):
    """Write to output file if specified"""
    if not output_file:
        return
    
    try:
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(f" {text}")
    except Exception as e:
        script_log(f"File write error: {e}")

def update_song():
    """Main update callback - runs every interval"""
    global last_file, last_song, stats
    
    if not enabled:
        return
    
    current_file = get_playing_file()
    
    if current_file and current_file != last_file:
        # New song detected
        metadata = get_song_metadata(current_file)
        current_song = format_song_display(metadata)
        
        if current_song != last_song:
            # Update display
            update_text_source(current_song)
            write_to_file(current_song)
            
            # Log
            script_log(f"♫ Now Playing: {current_song}")
            
            # Update stats
            stats["total_plays"] += 1
            last_song = current_song
        
        last_file = current_file
        
    elif not current_file and last_file:
        # Nothing playing
        update_text_source(default_text)
        write_to_file(default_text)
        last_file = ""
        last_song = ""
