from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

import reapy

from reaper_mcp.core.mcp_core import mcp

logger = logging.getLogger(__name__)


@mcp.tool()
def apply_mastering_chain(target_loudness: float = -14.0, ceiling: float = -1.0) -> Dict[str, Any]:
    """Apply a mastering effects chain to the master track.
    
    Adds compression, EQ, and limiting to the master track to achieve professional
    loudness and polish. This is a typical mastering chain for final output.
    
    Args:
        target_loudness: Target loudness in LUFS. Common values:
                        -14.0 (streaming platforms like Spotify, YouTube)
                        -16.0 (broadcast/TV)
                        -9.0 to -11.0 (CD mastering, louder)
        ceiling: Maximum true peak level in dBFS (typically -1.0 to -0.3).
                Lower values (e.g., -1.0) leave more headroom for encoding.
                Higher values (e.g., -0.3) allow louder output but risk clipping.
    
    Returns:
        Dict with 'master_chain' containing added FX details, or 'error' on failure.
    
    Note:
        - Adds ReaComp (compressor), ReaEQ (subtle enhancement), and ReaLimit (limiter)
        - Settings are approximations; fine-tune parameters for specific material
        - Apply this after mixing is complete
        - The chain is added to the master track (track 0 or master output)
    """
    try:
        project = reapy.Project()
        
        # Get master track
        # In REAPER, master track can be accessed via project.master_track
        # If that's not available, we'll work with track 0 or handle appropriately
        try:
            master = project.master_track
        except AttributeError:
            # Fallback: some versions might not have master_track directly
            tracks = list(project.tracks)
            if len(tracks) == 0:
                return {"error": "No tracks found. Create tracks before mastering."}
            # Use the first track as a proxy (not ideal, but functional)
            master = tracks[0]
            logger.warning("Using first track as master proxy. Ideally, target actual master track.")
        
        added_fx = []
        
        # 1. Add ReaComp (Compressor) for glue and dynamic control
        try:
            comp = master.add_fx(name="ReaComp (Cockos)", even_if_exists=True)
            if comp:
                # Set gentle mastering compression (approximated parameters)
                # ReaComp parameters vary, these are typical mastering settings
                # Param indices are approximate - may need adjustment
                try:
                    params = list(comp.params)
                    if len(params) >= 6:
                        params[0].normalized = 0.35  # Threshold (around -10 to -15 dB)
                        params[1].normalized = 0.25  # Ratio (around 2:1 to 3:1)
                        params[2].normalized = 0.15  # Attack (fast, around 5-10ms)
                        params[3].normalized = 0.50  # Release (medium, around 100-200ms)
                except Exception as e:
                    logger.warning(f"Could not set ReaComp parameters: {e}")
                
                added_fx.append({
                    "name": "ReaComp",
                    "purpose": "Glue compression",
                    "index": comp.index
                })
        except Exception as e:
            logger.warning(f"Could not add ReaComp: {e}")
        
        # 2. Add ReaEQ (Equalizer) for subtle enhancement
        try:
            eq = master.add_fx(name="ReaEQ (Cockos)", even_if_exists=True)
            if eq:
                # Subtle mastering EQ (parameters are approximated)
                try:
                    params = list(eq.params)
                    # Enable low-end boost and high-end air (typical mastering moves)
                    # These are very approximate - ReaEQ has many parameters
                    if len(params) >= 10:
                        params[0].normalized = 0.55  # Slight low-end boost
                        params[5].normalized = 0.60  # Slight high-end air
                except Exception as e:
                    logger.warning(f"Could not set ReaEQ parameters: {e}")
                
                added_fx.append({
                    "name": "ReaEQ",
                    "purpose": "Tonal balance",
                    "index": eq.index
                })
        except Exception as e:
            logger.warning(f"Could not add ReaEQ: {e}")
        
        # 3. Add ReaLimit (Limiter) for final loudness and ceiling
        try:
            limiter = master.add_fx(name="ReaLimit (Cockos)", even_if_exists=True)
            if limiter:
                # Set limiter ceiling and target loudness
                try:
                    params = list(limiter.params)
                    if len(params) >= 2:
                        # Ceiling parameter (typically param 0 or 1)
                        # Convert ceiling dBFS to normalized value
                        # -1.0 dBFS ≈ 0.9 normalized, -0.3 dBFS ≈ 0.97 normalized
                        ceiling_normalized = (ceiling + 12) / 12  # Rough conversion
                        ceiling_normalized = max(0.0, min(1.0, ceiling_normalized))
                        params[0].normalized = ceiling_normalized
                        
                        # Adjust gain/threshold based on target_loudness
                        # More negative LUFS = less gain needed
                        loudness_gain = (target_loudness + 14) / 10  # Rough scaling
                        loudness_gain = max(0.0, min(1.0, 0.5 + loudness_gain))
                        if len(params) >= 3:
                            params[2].normalized = loudness_gain
                except Exception as e:
                    logger.warning(f"Could not set ReaLimit parameters: {e}")
                
                added_fx.append({
                    "name": "ReaLimit",
                    "purpose": "Final limiting",
                    "index": limiter.index,
                    "ceiling_db": ceiling,
                    "target_loudness_lufs": target_loudness
                })
        except Exception as e:
            logger.warning(f"Could not add ReaLimit: {e}")
        
        if not added_fx:
            return {"error": "Could not add any mastering FX. Check REAPER plugin availability."}
        
        return {
            "master_chain_applied": True,
            "target_loudness": target_loudness,
            "ceiling": ceiling,
            "fx_added": added_fx,
            "note": "Mastering chain applied. Fine-tune FX parameters for your specific material."
        }
    except Exception as e:
        return {"error": f"Failed to apply mastering chain: {e}"}


@mcp.tool()
def analyze_loudness() -> Dict[str, Any]:
    """Measure project loudness and dynamic range.
    
    Analyzes the master output to measure loudness metrics including integrated LUFS,
    true peak, and dynamic range. Compares against industry standards.
    
    Returns:
        Dict with loudness measurements, standards comparison, or 'error' on failure.
    
    Note:
        - This is a simplified analysis based on track volumes and master level
        - For accurate LUFS measurement, use external tools like:
          * Youlean Loudness Meter (free)
          * iZotope Insight
          * REAPER's built-in loudness meter (SWS extensions)
        - Standards: Streaming (-14 LUFS), Broadcast (-23 to -16 LUFS), CD (-9 to -11 LUFS)
        - True peak should be below -1.0 dBFS for streaming to avoid intersample clipping
    """
    try:
        project = reapy.Project()
        
        # Get master track
        try:
            master = project.master_track
        except AttributeError:
            tracks = list(project.tracks)
            if len(tracks) == 0:
                return {"error": "No tracks found in project"}
            master = tracks[0]
            logger.warning("Using first track as master proxy")
        
        # Get master volume (this is a simplified approach)
        try:
            master_volume = master.volume
            master_db = 20 * (master_volume ** 0.5) if master_volume > 0 else -96
            # Rough conversion to dB (not exact)
            master_db = min(12, max(-96, master_db))
        except Exception:
            master_volume = 1.0
            master_db = 0.0
        
        # Estimate LUFS based on track count and levels (very rough approximation)
        tracks = list(project.tracks)
        track_count = len(tracks)
        
        # Calculate average track volume
        total_volume = 0
        for track in tracks:
            try:
                total_volume += track.volume
            except Exception:
                total_volume += 1.0
        
        avg_track_volume = total_volume / track_count if track_count > 0 else 1.0
        
        # Rough LUFS estimation (this is NOT accurate, just an approximation)
        # Real LUFS requires time-weighted, frequency-weighted audio analysis
        estimated_lufs = -23 + (master_db + 10) * 0.7
        estimated_lufs = max(-40, min(-5, estimated_lufs))
        
        # Estimate true peak (simplified)
        estimated_true_peak = master_db + 1.0  # Add headroom estimation
        
        # Estimate dynamic range (simplified)
        # Real DR requires crest factor analysis
        if avg_track_volume > 0:
            estimated_dr = max(6, min(20, 14 - abs(estimated_lufs + 14) / 2))
        else:
            estimated_dr = 10
        
        # Compare against standards
        standards = {
            "streaming": {
                "target_lufs": -14.0,
                "description": "Spotify, YouTube, Apple Music",
                "true_peak_limit": -1.0
            },
            "broadcast": {
                "target_lufs": -16.0,
                "description": "TV, radio (varies by region)",
                "true_peak_limit": -1.0
            },
            "cd_mastering": {
                "target_lufs": -9.0,
                "description": "CD, loud commercial releases",
                "true_peak_limit": -0.3
            }
        }
        
        # Determine which standard is closest
        closest_standard = "streaming"
        closest_diff = abs(estimated_lufs - standards["streaming"]["target_lufs"])
        for std_name, std_data in standards.items():
            diff = abs(estimated_lufs - std_data["target_lufs"])
            if diff < closest_diff:
                closest_diff = diff
                closest_standard = std_name
        
        # Check if within acceptable range
        target_lufs = standards[closest_standard]["target_lufs"]
        within_range = abs(estimated_lufs - target_lufs) <= 2.0
        
        return {
            "estimated_lufs": round(estimated_lufs, 1),
            "estimated_true_peak_db": round(estimated_true_peak, 1),
            "estimated_dynamic_range_db": round(estimated_dr, 1),
            "master_volume_db": round(master_db, 1),
            "track_count": track_count,
            "closest_standard": closest_standard,
            "target_lufs": target_lufs,
            "within_acceptable_range": within_range,
            "standards_reference": standards,
            "warning": "These are ESTIMATES based on track levels. For accurate LUFS, use dedicated loudness meters.",
            "recommendations": get_loudness_recommendations(estimated_lufs, estimated_true_peak)
        }
    except Exception as e:
        return {"error": f"Failed to analyze loudness: {e}"}


def get_loudness_recommendations(lufs: float, true_peak: float) -> List[str]:
    """Generate recommendations based on loudness analysis."""
    recommendations = []
    
    if lufs < -20:
        recommendations.append("Mix is very quiet. Increase overall levels or use mastering compression/limiting.")
    elif lufs < -16:
        recommendations.append("Mix is quiet. Consider increasing levels for streaming platforms.")
    elif lufs > -9:
        recommendations.append("Mix is very loud. May cause distortion; consider reducing levels.")
    elif lufs > -11:
        recommendations.append("Mix is loud. Acceptable for CD, but may be reduced by streaming platforms.")
    else:
        recommendations.append("Loudness is in good range for most platforms.")
    
    if true_peak > -0.5:
        recommendations.append("True peak is high. Risk of clipping; use limiter with lower ceiling.")
    elif true_peak > -1.0:
        recommendations.append("True peak is moderate. Acceptable for CD, but -1.0 dBFS safer for streaming.")
    else:
        recommendations.append("True peak has good headroom for encoding and streaming.")
    
    return recommendations
