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 auto_level_tracks(target_lufs: float = -14.0) -> Dict[str, Any]:
    """Analyze and automatically balance track volumes to achieve target LUFS.
    
    This function adjusts track volumes to balance the mix, targeting a specific
    loudness level. It analyzes all tracks and adjusts their volumes proportionally.
    
    Args:
        target_lufs: Target loudness in LUFS (Loudness Units relative to Full Scale).
                    Default is -14.0 LUFS (streaming standard).
                    Common values: -14.0 (streaming), -16.0 (broadcast), -9.0 (CD mastering)
    
    Returns:
        Dict with 'adjusted_tracks' list and 'target_lufs' on success, or 'error' on failure.
    
    Note:
        - This is a simplified implementation that adjusts relative track volumes
        - For accurate LUFS measurement, external analysis tools may be needed
        - Tracks are balanced based on their current volume relationships
    """
    try:
        project = reapy.Project()
        tracks = list(project.tracks)
        
        if len(tracks) == 0:
            return {"error": "No tracks found in project"}
        
        adjusted = []
        
        # Get current volumes and calculate average
        volumes = []
        for i, track in enumerate(tracks):
            try:
                vol = track.volume
                volumes.append(vol)
            except Exception:
                volumes.append(1.0)
        
        # Calculate average volume (in linear scale)
        avg_volume = sum(volumes) / len(volumes) if volumes else 1.0
        
        # Adjust each track relative to average
        # This is a simplified approach - real LUFS would require audio analysis
        for i, track in enumerate(tracks):
            try:
                current_vol = volumes[i]
                # Normalize around average, then apply target scaling
                # target_lufs affects overall gain: -14 LUFS is moderate, -9 is louder
                lufs_scale = 10 ** ((target_lufs + 14) / 20)  # Convert LUFS to linear scale
                new_vol = current_vol * lufs_scale
                
                # Clamp to reasonable range
                new_vol = max(0.0, min(4.0, new_vol))
                
                track.volume = new_vol
                adjusted.append({
                    "track_index": i,
                    "track_name": track.name,
                    "old_volume": current_vol,
                    "new_volume": new_vol
                })
            except Exception as e:
                logger.warning(f"Failed to adjust track {i}: {e}")
        
        return {
            "adjusted_tracks": adjusted,
            "target_lufs": target_lufs,
            "tracks_processed": len(adjusted)
        }
    except Exception as e:
        return {"error": f"Failed to auto-level tracks: {e}"}


@mcp.tool()
def apply_mixing_template(template_name: str) -> Dict[str, Any]:
    """Apply genre-specific mixing template with volume, pan, and FX settings.
    
    Applies a predefined mixing template based on genre conventions. Templates include
    typical volume balances, panning positions, and suggested effects chains.
    
    Args:
        template_name: Name of the template to apply. Available templates:
                      - 'rock': Rock band mix (drums center, bass center, guitars panned)
                      - 'edm': Electronic dance music (wide stereo, heavy bass)
                      - 'jazz': Jazz ensemble (natural spread, dynamic range)
                      - 'hiphop': Hip-hop/trap (centered vocals and bass, wide samples)
                      - 'pop': Pop music (balanced, vocal-focused)
    
    Returns:
        Dict with 'template_applied', 'tracks_configured' on success, or 'error' on failure.
    
    Note:
        - Applies settings to existing tracks in order (drums, bass, melody, etc.)
        - Does not add FX, only suggests settings and applies volume/pan
        - Track count should match template expectations for best results
    """
    templates = {
        'rock': {
            'description': 'Rock band mix',
            'tracks': [
                {'name': 'Drums', 'volume': 1.0, 'pan': 0.0},
                {'name': 'Bass', 'volume': 0.9, 'pan': 0.0},
                {'name': 'Guitar L', 'volume': 0.75, 'pan': -0.5},
                {'name': 'Guitar R', 'volume': 0.75, 'pan': 0.5},
                {'name': 'Vocals', 'volume': 1.0, 'pan': 0.0},
            ]
        },
        'edm': {
            'description': 'Electronic dance music',
            'tracks': [
                {'name': 'Kick', 'volume': 1.2, 'pan': 0.0},
                {'name': 'Bass', 'volume': 1.1, 'pan': 0.0},
                {'name': 'Synth L', 'volume': 0.8, 'pan': -0.7},
                {'name': 'Synth R', 'volume': 0.8, 'pan': 0.7},
                {'name': 'Lead', 'volume': 0.9, 'pan': 0.0},
            ]
        },
        'jazz': {
            'description': 'Jazz ensemble',
            'tracks': [
                {'name': 'Drums', 'volume': 0.8, 'pan': 0.0},
                {'name': 'Bass', 'volume': 0.85, 'pan': -0.2},
                {'name': 'Piano', 'volume': 0.9, 'pan': 0.2},
                {'name': 'Sax/Lead', 'volume': 0.95, 'pan': 0.0},
                {'name': 'Aux', 'volume': 0.7, 'pan': 0.3},
            ]
        },
        'hiphop': {
            'description': 'Hip-hop/trap',
            'tracks': [
                {'name': 'Drums', 'volume': 1.1, 'pan': 0.0},
                {'name': 'Bass/808', 'volume': 1.3, 'pan': 0.0},
                {'name': 'Vocals', 'volume': 1.0, 'pan': 0.0},
                {'name': 'Sample L', 'volume': 0.7, 'pan': -0.6},
                {'name': 'Sample R', 'volume': 0.7, 'pan': 0.6},
            ]
        },
        'pop': {
            'description': 'Pop music',
            'tracks': [
                {'name': 'Drums', 'volume': 0.9, 'pan': 0.0},
                {'name': 'Bass', 'volume': 0.85, 'pan': 0.0},
                {'name': 'Synth/Keys', 'volume': 0.75, 'pan': 0.3},
                {'name': 'Guitar/Pad', 'volume': 0.7, 'pan': -0.3},
                {'name': 'Lead Vocal', 'volume': 1.1, 'pan': 0.0},
            ]
        }
    }
    
    try:
        template_name_lower = template_name.lower()
        if template_name_lower not in templates:
            return {
                "error": f"Unknown template: {template_name}. Available: {', '.join(templates.keys())}"
            }
        
        project = reapy.Project()
        tracks = list(project.tracks)
        
        if len(tracks) == 0:
            return {"error": "No tracks found in project"}
        
        template = templates[template_name_lower]
        configured = []
        
        # Apply template to as many tracks as exist
        for i, track_config in enumerate(template['tracks']):
            if i >= len(tracks):
                break
            
            track = tracks[i]
            try:
                track.volume = track_config['volume']
                track.pan = track_config['pan']
                configured.append({
                    "track_index": i,
                    "track_name": track.name,
                    "template_role": track_config['name'],
                    "volume": track_config['volume'],
                    "pan": track_config['pan']
                })
            except Exception as e:
                logger.warning(f"Failed to configure track {i}: {e}")
        
        return {
            "template_applied": template_name_lower,
            "description": template['description'],
            "tracks_configured": configured,
            "total_tracks": len(tracks)
        }
    except Exception as e:
        return {"error": f"Failed to apply mixing template: {e}"}


@mcp.tool()
def suggest_track_panning(track_count: int) -> Dict[str, Any]:
    """Calculate optimal pan positions for the given number of tracks.
    
    Suggests stereo panning positions to create a balanced and spacious mix.
    Uses standard mixing conventions to spread tracks across the stereo field.
    
    Args:
        track_count: Number of tracks to generate panning suggestions for (1-32).
    
    Returns:
        Dict with 'pan_suggestions' list containing pan positions for each track,
        or 'error' on failure.
    
    Note:
        - Pan values range from -1.0 (full left) to 1.0 (full right), 0.0 is center
        - Common practice: keep bass/kick/vocals centered, spread other elements
        - Suggestions follow the "LCR" and gradual spread mixing approaches
    """
    try:
        if track_count < 1 or track_count > 32:
            return {"error": f"Track count must be between 1 and 32, got {track_count}"}
        
        suggestions = []
        
        # Define panning strategy based on track count
        if track_count == 1:
            suggestions = [{"track_index": 0, "pan": 0.0, "position": "center"}]
        elif track_count == 2:
            suggestions = [
                {"track_index": 0, "pan": -0.3, "position": "slight left"},
                {"track_index": 1, "pan": 0.3, "position": "slight right"}
            ]
        elif track_count == 3:
            suggestions = [
                {"track_index": 0, "pan": 0.0, "position": "center"},
                {"track_index": 1, "pan": -0.5, "position": "left"},
                {"track_index": 2, "pan": 0.5, "position": "right"}
            ]
        else:
            # For 4+ tracks, use a balanced distribution
            # First track typically center (bass/kick), then alternate
            suggestions.append({"track_index": 0, "pan": 0.0, "position": "center"})
            
            # Distribute remaining tracks across stereo field
            remaining = track_count - 1
            for i in range(1, track_count):
                # Alternate left/right with varying widths
                if i % 2 == 1:  # Odd positions go left
                    pan_value = -0.3 - (0.35 * ((i - 1) // 2) / max(1, remaining // 2))
                    pan_value = max(-1.0, pan_value)
                    position = "left" if pan_value < -0.5 else "slight left"
                else:  # Even positions go right
                    pan_value = 0.3 + (0.35 * ((i - 2) // 2) / max(1, remaining // 2))
                    pan_value = min(1.0, pan_value)
                    position = "right" if pan_value > 0.5 else "slight right"
                
                suggestions.append({
                    "track_index": i,
                    "pan": round(pan_value, 2),
                    "position": position
                })
        
        return {
            "pan_suggestions": suggestions,
            "track_count": track_count,
            "note": "First track (typically bass/kick/vocals) is centered. Others spread for stereo width."
        }
    except Exception as e:
        return {"error": f"Failed to suggest track panning: {e}"}


@mcp.tool()
def detect_frequency_conflicts(track_indices: Optional[List[int]] = None) -> Dict[str, Any]:
    """Analyze frequency content and detect potential masking between tracks.
    
    Identifies tracks that may compete in the same frequency ranges, which can
    cause muddy or cluttered mixes. Suggests EQ adjustments to resolve conflicts.
    
    Args:
        track_indices: Optional list of track indices to analyze. If None, analyzes all tracks.
    
    Returns:
        Dict with 'conflicts' list, 'suggestions' for EQ adjustments, or 'error' on failure.
    
    Note:
        - This is a simplified analysis based on track types and typical frequency ranges
        - Real-time audio FFT analysis would require additional audio processing
        - Suggestions are based on common mixing practices and frequency allocation
        - Typical ranges: Sub (20-60Hz), Bass (60-250Hz), Low-Mid (250-500Hz),
          Mid (500Hz-2kHz), High-Mid (2-6kHz), High (6-20kHz)
    """
    try:
        project = reapy.Project()
        tracks = list(project.tracks)
        
        if len(tracks) == 0:
            return {"error": "No tracks found in project"}
        
        # Determine which tracks to analyze
        if track_indices is None:
            analyze_tracks = list(range(len(tracks)))
        else:
            analyze_tracks = track_indices
            # Validate indices
            for idx in analyze_tracks:
                if idx < 0 or idx >= len(tracks):
                    return {"error": f"Track index {idx} out of range (0-{len(tracks)-1})"}
        
        # Analyze track names and guess frequency content
        # This is a heuristic approach based on common naming conventions
        track_info = []
        for idx in analyze_tracks:
            track = tracks[idx]
            name_lower = track.name.lower()
            
            # Guess frequency profile based on track name
            freq_profile = guess_frequency_profile(name_lower)
            track_info.append({
                "index": idx,
                "name": track.name,
                "profile": freq_profile
            })
        
        # Detect conflicts (tracks competing in same frequency ranges)
        conflicts = []
        suggestions = []
        
        for i, track_a in enumerate(track_info):
            for j, track_b in enumerate(track_info[i+1:], start=i+1):
                # Check for overlapping frequency ranges
                overlap = find_frequency_overlap(track_a['profile'], track_b['profile'])
                if overlap:
                    conflict = {
                        "track_a_index": track_a['index'],
                        "track_a_name": track_a['name'],
                        "track_b_index": track_b['index'],
                        "track_b_name": track_b['name'],
                        "conflict_range": overlap
                    }
                    conflicts.append(conflict)
                    
                    # Generate EQ suggestions
                    suggestion = generate_eq_suggestion(track_a, track_b, overlap)
                    if suggestion:
                        suggestions.append(suggestion)
        
        return {
            "tracks_analyzed": len(track_info),
            "conflicts_found": len(conflicts),
            "conflicts": conflicts,
            "eq_suggestions": suggestions,
            "note": "Suggestions based on typical frequency allocation. Adjust to taste and use spectrum analyzer for precise EQ."
        }
    except Exception as e:
        return {"error": f"Failed to detect frequency conflicts: {e}"}


def guess_frequency_profile(track_name: str) -> List[str]:
    """Guess the primary frequency ranges based on track name."""
    freq_ranges = []
    
    # Low frequency instruments
    if any(word in track_name for word in ['kick', 'bass', '808', 'sub']):
        freq_ranges.extend(['sub', 'bass'])
    
    # Mid-range instruments
    if any(word in track_name for word in ['snare', 'tom', 'guitar', 'piano', 'key', 'synth', 'pad']):
        freq_ranges.extend(['low-mid', 'mid', 'high-mid'])
    
    # High frequency instruments
    if any(word in track_name for word in ['hihat', 'cymbal', 'hi-hat', 'shaker', 'percussion']):
        freq_ranges.extend(['high-mid', 'high'])
    
    # Vocals cover wide range
    if any(word in track_name for word in ['vocal', 'voice', 'lead', 'melody']):
        freq_ranges.extend(['mid', 'high-mid', 'high'])
    
    # Default to mid range if no match
    if not freq_ranges:
        freq_ranges = ['low-mid', 'mid', 'high-mid']
    
    return freq_ranges


def find_frequency_overlap(profile_a: List[str], profile_b: List[str]) -> Optional[str]:
    """Find overlapping frequency ranges between two profiles."""
    overlap = set(profile_a) & set(profile_b)
    if overlap:
        # Return the most significant overlap
        priority = ['mid', 'low-mid', 'high-mid', 'bass', 'high', 'sub']
        for freq_range in priority:
            if freq_range in overlap:
                return freq_range
    return None


def generate_eq_suggestion(track_a: Dict, track_b: Dict, conflict_range: str) -> Dict[str, Any]:
    """Generate EQ suggestions to resolve frequency conflicts."""
    freq_centers = {
        'sub': '40Hz',
        'bass': '100Hz',
        'low-mid': '350Hz',
        'mid': '1kHz',
        'high-mid': '4kHz',
        'high': '10kHz'
    }
    
    center_freq = freq_centers.get(conflict_range, '1kHz')
    
    return {
        "conflict_range": conflict_range,
        "center_frequency": center_freq,
        "suggestion": f"Cut {center_freq} on '{track_a['name']}' or '{track_b['name']}' to reduce masking",
        "alternative": f"Consider panning '{track_a['name']}' and '{track_b['name']}' to different positions",
        "tracks": [track_a['index'], track_b['index']]
    }
