import os
import json
import requests
import bcrypt
import re
import datetime
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def send_player_feedback(config, message, status="active", playlist_version=None, error_details=None):
    """
    Send feedback to the server about player status.
    
    Args:
        config (dict): Configuration containing server details
        message (str): Main feedback message
        status (str): Player status - "active", "playing", "error", "restarting"
        playlist_version (int, optional): Current playlist version being played
        error_details (str, optional): Error details if status is "error"
    
    Returns:
        bool: True if feedback sent successfully, False otherwise
    """
    try:
        server = config.get("server_ip", "")
        host = config.get("screen_name", "")
        quick = config.get("quickconnect_key", "")
        port = config.get("port", "")
        
        # Construct server URL
        # Remove protocol if already present
        server_clean = server.replace('http://', '').replace('https://', '')
        ip_pattern = r'^\d+\.\d+\.\d+\.\d+$'
        if re.match(ip_pattern, server_clean):
            feedback_url = f'http://{server_clean}:{port}/api/player-feedback'
        else:
            # Use original server if it has protocol, otherwise add http://
            if server.startswith(('http://', 'https://')):
                feedback_url = f'{server}/api/player-feedback'
            else:
                feedback_url = f'http://{server}/api/player-feedback'
        
        # Prepare feedback data
        feedback_data = {
            'hostname': host,
            'quickconnect_code': quick,
            'message': message,
            'status': status,
            'timestamp': datetime.datetime.now().isoformat(),
            'playlist_version': playlist_version,
            'error_details': error_details
        }
        
        logger.info(f"Sending feedback to {feedback_url}: {feedback_data}")
        
        # Send POST request
        response = requests.post(feedback_url, json=feedback_data, timeout=10)
        
        if response.status_code == 200:
            logger.info(f"Feedback sent successfully: {message}")
            return True
        else:
            logger.warning(f"Feedback failed with status {response.status_code}: {response.text}")
            return False
            
    except requests.exceptions.RequestException as e:
        logger.error(f"Failed to send feedback: {e}")
        return False
    except Exception as e:
        logger.error(f"Unexpected error sending feedback: {e}")
        return False

def send_playlist_check_feedback(config, playlist_version=None):
    """
    Send feedback when playlist is checked for updates.
    
    Args:
        config (dict): Configuration containing server details
        playlist_version (int, optional): Current playlist version
    
    Returns:
        bool: True if feedback sent successfully, False otherwise
    """
    player_name = config.get("screen_name", "unknown")
    version_info = f"v{playlist_version}" if playlist_version else "unknown"
    message = f"player {player_name}, is active, Playing {version_info}"
    
    return send_player_feedback(
        config=config,
        message=message,
        status="active",
        playlist_version=playlist_version
    )

def send_playlist_restart_feedback(config, playlist_version=None):
    """
    Send feedback when playlist loop ends and restarts.
    
    Args:
        config (dict): Configuration containing server details
        playlist_version (int, optional): Current playlist version
    
    Returns:
        bool: True if feedback sent successfully, False otherwise
    """
    player_name = config.get("screen_name", "unknown")
    version_info = f"v{playlist_version}" if playlist_version else "unknown"
    message = f"player {player_name}, playlist loop completed, restarting {version_info}"
    
    return send_player_feedback(
        config=config,
        message=message,
        status="restarting",
        playlist_version=playlist_version
    )

def send_player_error_feedback(config, error_message, playlist_version=None):
    """
    Send feedback when an error occurs in the player.
    
    Args:
        config (dict): Configuration containing server details
        error_message (str): Description of the error
        playlist_version (int, optional): Current playlist version
    
    Returns:
        bool: True if feedback sent successfully, False otherwise
    """
    player_name = config.get("screen_name", "unknown")
    message = f"player {player_name}, error occurred"
    
    return send_player_feedback(
        config=config,
        message=message,
        status="error",
        playlist_version=playlist_version,
        error_details=error_message
    )

def send_playing_status_feedback(config, playlist_version=None, current_media=None):
    """
    Send feedback about current playing status.
    
    Args:
        config (dict): Configuration containing server details
        playlist_version (int, optional): Current playlist version
        current_media (str, optional): Currently playing media file
    
    Returns:
        bool: True if feedback sent successfully, False otherwise
    """
    player_name = config.get("screen_name", "unknown")
    version_info = f"v{playlist_version}" if playlist_version else "unknown"
    media_info = f" - {current_media}" if current_media else ""
    message = f"player {player_name}, is active, Playing {version_info}{media_info}"
    
    return send_player_feedback(
        config=config,
        message=message,
        status="playing",
        playlist_version=playlist_version
    )

def fetch_server_playlist(config):
    """Fetch the updated playlist from the server using a config dict."""
    server = config.get("server_ip", "")
    host = config.get("screen_name", "")
    quick = config.get("quickconnect_key", "")
    port = config.get("port", "")
    try:
        # Remove protocol if already present
        server_clean = server.replace('http://', '').replace('https://', '')
        ip_pattern = r'^\d+\.\d+\.\d+\.\d+$'
        if re.match(ip_pattern, server_clean):
            server_url = f'http://{server_clean}:{port}/api/playlists'
        else:
            # Use original server if it has protocol, otherwise add http://
            if server.startswith(('http://', 'https://')):
                server_url = f'{server}/api/playlists'
            else:
                server_url = f'http://{server}/api/playlists'
        params = {
            'hostname': host,
            'quickconnect_code': quick
        }
        logger.info(f"Fetching playlist from URL: {server_url} with params: {params}")
        response = requests.get(server_url, params=params)
        if response.status_code == 200:
            response_data = response.json()
            logger.info(f"Server response: {response_data}")
            playlist = response_data.get('playlist', [])
            version = response_data.get('playlist_version', None)
            hashed_quickconnect = response_data.get('hashed_quickconnect', None)
            if version is not None and hashed_quickconnect is not None:
                if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')):
                    logger.info("Fetched updated playlist from server.")
                    return {'playlist': playlist, 'version': version}
                else:
                    logger.error("Quickconnect code validation failed.")
            else:
                logger.error("Failed to retrieve playlist or hashed quickconnect from the response.")
        else:
            logger.error(f"Failed to fetch playlist. Status Code: {response.status_code}")
    except requests.exceptions.RequestException as e:
        logger.error(f"Failed to fetch playlist: {e}")
    return {'playlist': [], 'version': 0}

def save_playlist_with_version(playlist_data, playlist_dir):
    version = playlist_data.get('version', 0)
    playlist_file = os.path.join(playlist_dir, f'server_playlist_v{version}.json')
    with open(playlist_file, 'w') as f:
        json.dump(playlist_data, f, indent=2)
    logger.info(f"Playlist saved to {playlist_file}")
    return playlist_file

def download_media_files(playlist, media_dir):
    """Download media files from the server and save them to media_dir."""
    if not os.path.exists(media_dir):
        os.makedirs(media_dir)
        logger.info(f"Created directory {media_dir} for media files.")

    updated_playlist = []
    for media in playlist:
        file_name = media.get('file_name', '')
        file_url = media.get('url', '')
        duration = media.get('duration', 10)
        local_path = os.path.join(media_dir, file_name)
        logger.info(f"Preparing to download {file_name} from {file_url}...")
        if os.path.exists(local_path):
            logger.info(f"File {file_name} already exists. Skipping download.")
        else:
            try:
                response = requests.get(file_url, timeout=10)
                if response.status_code == 200:
                    with open(local_path, 'wb') as file:
                        file.write(response.content)
                    logger.info(f"Successfully downloaded {file_name} to {local_path}")
                else:
                    logger.error(f"Failed to download {file_name}. Status Code: {response.status_code}")
                    continue
            except requests.exceptions.RequestException as e:
                logger.error(f"Error downloading {file_name}: {e}")
                continue
        updated_media = {
            'file_name': file_name,
            'url': os.path.relpath(local_path, os.path.dirname(media_dir)),
            'duration': duration
        }
        updated_playlist.append(updated_media)
    return updated_playlist

def delete_old_playlists_and_media(current_version, playlist_dir, media_dir, keep_versions=1):
    """
    Delete old playlist files and media files not referenced by the latest playlist version.
    keep_versions: number of latest versions to keep (default 1)
    """
    # Find all playlist files
    playlist_files = [f for f in os.listdir(playlist_dir) if f.startswith('server_playlist_v') and f.endswith('.json')]
    # Keep only the latest N versions
    versions = sorted([int(f.split('_v')[-1].split('.json')[0]) for f in playlist_files], reverse=True)
    keep = set(versions[:keep_versions])
    # Delete old playlist files
    for f in playlist_files:
        v = int(f.split('_v')[-1].split('.json')[0])
        if v not in keep:
            os.remove(os.path.join(playlist_dir, f))
    # Collect all media files referenced by the kept playlists
    referenced = set()
    for v in keep:
        path = os.path.join(playlist_dir, f'server_playlist_v{v}.json')
        if os.path.exists(path):
            with open(path, 'r') as f:
                data = json.load(f)
            for item in data.get('playlist', []):
                referenced.add(item.get('file_name'))
    # Delete media files not referenced
    for f in os.listdir(media_dir):
        if f not in referenced:
            try:
                os.remove(os.path.join(media_dir, f))
            except Exception as e:
                logger.warning(f"Failed to delete media file {f}: {e}")

def update_playlist_if_needed(local_playlist_path, config, media_dir, playlist_dir):
    """
    Fetch the server playlist once, compare versions, and update if needed.
    Returns True if updated, False if already up to date.
    Also sends feedback to server about playlist check.
    """
    server_data = fetch_server_playlist(config)
    server_version = server_data.get('version', 0)
    if not os.path.exists(local_playlist_path):
        local_version = 0
    else:
        with open(local_playlist_path, 'r') as f:
            local_data = json.load(f)
        local_version = local_data.get('version', 0)
    
    logger.info(f"Local playlist version: {local_version}, Server playlist version: {server_version}")
    
    # Send feedback about playlist check
    send_playlist_check_feedback(config, server_version if server_version > 0 else local_version)
    
    if local_version != server_version:
        if server_data and server_data.get('playlist'):
            updated_playlist = download_media_files(server_data['playlist'], media_dir)
            server_data['playlist'] = updated_playlist
            save_playlist_with_version(server_data, playlist_dir)
            # Delete old playlists and unreferenced media
            delete_old_playlists_and_media(server_version, playlist_dir, media_dir)
            
            # Send feedback about playlist update
            player_name = config.get("screen_name", "unknown")
            update_message = f"player {player_name}, playlist updated to v{server_version}"
            send_player_feedback(config, update_message, "active", server_version)
            
            return True
        else:
            logger.warning("No playlist data fetched from server or playlist is empty.")
            
            # Send error feedback
            send_player_error_feedback(config, "No playlist data fetched from server or playlist is empty", local_version)
            
            return False
    else:
        logger.info("Local playlist is already up to date.")
        return False

def is_playlist_up_to_date(local_playlist_path, config):
    """
    Compare the version of the local playlist with the server playlist.
    Returns True if up-to-date, False otherwise.
    """
    if not os.path.exists(local_playlist_path):
        logger.info(f"Local playlist file not found: {local_playlist_path}")
        return False
    with open(local_playlist_path, 'r') as f:
        local_data = json.load(f)
    local_version = local_data.get('version', 0)
    server_data = fetch_server_playlist(config)
    server_version = server_data.get('version', 0)
    logger.info(f"Local playlist version: {local_version}, Server playlist version: {server_version}")
    return local_version == server_version