"""
Advanced Automation Engine for AbletonMCP_AI

Creates professional automation curves including:
- Filter sweeps (build-ups, breakdowns)
- Sidechain compression curves
- Send automation (reverb/delay)
- Volume ramps and fades
- Complete build-up automation packages

Author: Agent 6 - Advanced Automation Specialist
"""

import math
from typing import Dict, List, Tuple, Optional, Any
from dataclasses import dataclass
from enum import Enum


class CurveType(Enum):
    """Types of automation curves."""
    LINEAR = "linear"
    EXPONENTIAL = "exponential"
    LOGARITHMIC = "logarithmic"
    S_CURVE = "s_curve"
    BEZIER = "bezier"
    STEP = "step"


@dataclass
class AutomationPoint:
    """Represents a single automation point."""
    time: float  # In beats (can be fractional for precise positioning)
    value: float
    curve_type: str = "linear"  # "linear", "bezier", "s_curve"


class AdvancedAutomation:
    """
    Advanced automation engine for creating professional curves.
    
    Handles:
    - Filter sweeps (exponential/logarithmic)
    - Sidechain ducking curves
    - Send automation (reverb/delay)
    - Volume ramps
    - Build-up automation packages
    """
    
    def __init__(self, live_bridge):
        """
        Initialize with LiveBridge for direct Ableton API access.
        
        Args:
            live_bridge: LiveBridge instance for executing automation commands
        """
        self.live_bridge = live_bridge
        self._automation_history = []
        
    def _generate_curve_points(
        self,
        start_val: float,
        end_val: float,
        start_time: float,
        end_time: float,
        num_points: int = 16,
        curve_type: str = "linear"
    ) -> List[AutomationPoint]:
        """
        Generate automation points following a curve.
        
        Args:
            start_val: Starting value
            end_val: Ending value
            start_time: Starting time in beats
            end_time: Ending time in beats
            num_points: Number of control points to generate
            curve_type: Type of curve (linear, exponential, logarithmic, s_curve)
            
        Returns:
            List of AutomationPoint objects
        """
        points = []
        duration = end_time - start_time
        
        for i in range(num_points + 1):
            t = i / num_points  # Normalized time 0-1
            
            # Apply curve function
            if curve_type == "linear":
                factor = t
            elif curve_type == "exponential":
                # Exponential curve: starts slow, ends fast
                factor = t ** 2 if start_val < end_val else 1 - (1 - t) ** 2
                if start_val > end_val:
                    factor = 1 - (1 - t) ** 2
                else:
                    factor = t ** 2
            elif curve_type == "logarithmic":
                # Logarithmic curve: starts fast, ends slow
                factor = math.sqrt(t) if start_val < end_val else 1 - math.sqrt(1 - t)
                if start_val > end_val:
                    factor = 1 - math.sqrt(1 - t)
                else:
                    factor = math.sqrt(t)
            elif curve_type == "s_curve":
                # S-curve: smooth ease-in-out
                factor = t ** 2 * (3 - 2 * t)
            elif curve_type == "bezier":
                # Cubic bezier approximation
                factor = t ** 3 * (10 - 15 * t + 6 * t ** 2)
            else:
                factor = t
            
            # Calculate value
            if curve_type == "exponential" and start_val < end_val:
                # For exponential upward sweeps, use proper log scale
                if start_val > 0 and end_val > 0:
                    log_start = math.log10(start_val)
                    log_end = math.log10(end_val)
                    value = 10 ** (log_start + factor * (log_end - log_start))
                else:
                    value = start_val + factor * (end_val - start_val)
            else:
                value = start_val + factor * (end_val - start_val)
            
            time = start_time + t * duration
            points.append(AutomationPoint(time=time, value=value, curve_type=curve_type))
        
        return points
    
    def create_filter_sweep(
        self,
        track_index: int,
        start_bar: float,
        end_bar: float,
        start_freq: float = 200,
        end_freq: float = 20000,
        curve_type: str = "exponential",
        filter_device: str = "Auto Filter",
        parameter_name: str = "Frequency"
    ) -> Dict:
        """
        Create a filter sweep automation curve.
        
        Args:
            track_index: Target track index
            start_bar: Starting bar position
            end_bar: Ending bar position
            start_freq: Starting frequency in Hz (default 200)
            end_freq: Ending frequency in Hz (default 20000)
            curve_type: Curve type - "linear", "exponential", "logarithmic", "s_curve"
            filter_device: Name of filter device (default "Auto Filter")
            parameter_name: Parameter to automate (default "Frequency")
            
        Returns:
            Dict with automation details and status
        """
        try:
            # Convert bars to beats
            start_beats = start_bar * 4
            end_beats = end_bar * 4
            
            # Generate exponential curve points for natural filter sweep
            points = self._generate_curve_points(
                start_val=start_freq,
                end_val=end_freq,
                start_time=start_beats,
                end_time=end_beats,
                num_points=24,  # More points for smooth sweep
                curve_type=curve_type
            )
            
            # Convert to format expected by LiveBridge
            automation_points = [[p.time, p.value] for p in points]
            
            # Apply automation via LiveBridge
            result = self.live_bridge.add_automation(
                track_index=track_index,
                parameter_name=parameter_name,
                points=automation_points,
                device_name=filter_device
            )
            
            sweep_data = {
                "track_index": track_index,
                "device": filter_device,
                "parameter": parameter_name,
                "start_bar": start_bar,
                "end_bar": end_bar,
                "start_freq": start_freq,
                "end_freq": end_freq,
                "curve_type": curve_type,
                "point_count": len(points),
                "status": "success" if result else "failed"
            }
            
            self._automation_history.append(sweep_data)
            return sweep_data
            
        except Exception as e:
            return {
                "track_index": track_index,
                "status": "error",
                "error": str(e)
            }
    
    def create_sidechain_curve(
        self,
        track_index: int,
        ducking_amount: float = 0.7,
        attack_ms: float = 5,
        release_ms: float = 100,
        every_n_bars: float = 1.0,
        total_bars: float = 16.0
    ) -> Dict:
        """
        Create sidechain compression ducking curves synced to the beat.
        
        Args:
            track_index: Target track index (bass/synths)
            ducking_amount: Amount of ducking 0.0-1.0 (0.7 = -12dB reduction)
            attack_ms: Attack time in milliseconds (default 5)
            release_ms: Release time in milliseconds (default 100)
            every_n_bars: Duck every N bars (default 1.0 = every bar)
            total_bars: Total duration in bars (default 16)
            
        Returns:
            Dict with automation details and status
        """
        try:
            # Calculate BPM-dependent times
            bpm = 95  # Assume default, could be fetched from Live
            ms_per_beat = 60000 / bpm
            
            # Convert ms to beats
            attack_beats = attack_ms / ms_per_beat
            release_beats = release_ms / ms_per_beat
            
            # Ducking value (1.0 = no ducking, lower = more reduction)
            duck_val = 1.0 - ducking_amount
            
            points = []
            bar = 0.0
            
            while bar < total_bars:
                bar_start = bar * 4  # Convert to beats
                
                # Start of duck (on the beat)
                points.append([bar_start, 1.0])
                
                # Attack phase (quick drop)
                points.append([bar_start + attack_beats, duck_val])
                
                # Sustain (hold duck)
                sustain_end = bar_start + (2 * 4 * every_n_bars) / 3  # 2/3 through
                points.append([sustain_end, duck_val])
                
                # Release phase (recover)
                bar_end = (bar + every_n_bars) * 4
                points.append([bar_end, 1.0])
                
                bar += every_n_bars
            
            # Apply volume automation via LiveBridge
            result = self.live_bridge.add_automation(
                track_index=track_index,
                parameter_name="volume",
                points=points
            )
            
            sidechain_data = {
                "track_index": track_index,
                "ducking_amount": ducking_amount,
                "attack_ms": attack_ms,
                "release_ms": release_ms,
                "every_n_bars": every_n_bars,
                "total_bars": total_bars,
                "point_count": len(points),
                "status": "success" if result else "failed"
            }
            
            self._automation_history.append(sidechain_data)
            return sidechain_data
            
        except Exception as e:
            return {
                "track_index": track_index,
                "status": "error",
                "error": str(e)
            }
    
    def create_send_automation(
        self,
        track_index: int,
        send_index: int,
        points: List[Tuple[float, float]],
        curve_type: str = "linear"
    ) -> Dict:
        """
        Create send level automation (reverb/delay sends).
        
        Args:
            track_index: Target track index
            send_index: Index of the return track (0 for A, 1 for B, etc.)
            points: List of (bar_position, send_amount) tuples
                   Send amount should be 0.0-1.0
            curve_type: Interpolation curve type
            
        Returns:
            Dict with automation details and status
        """
        try:
            # Convert bar positions to beats
            automation_points = []
            for bar_pos, amount in points:
                beats = bar_pos * 4
                automation_points.append([beats, amount])
            
            # Apply send automation via LiveBridge
            result = self.live_bridge.add_automation(
                track_index=track_index,
                parameter_name="send",
                points=automation_points,
                send_index=send_index
            )
            
            send_data = {
                "track_index": track_index,
                "send_index": send_index,
                "send_letter": chr(ord('A') + send_index),
                "point_count": len(points),
                "curve_type": curve_type,
                "status": "success" if result else "failed"
            }
            
            self._automation_history.append(send_data)
            return send_data
            
        except Exception as e:
            return {
                "track_index": track_index,
                "send_index": send_index,
                "status": "error",
                "error": str(e)
            }
    
    def create_volume_ramp(
        self,
        track_index: int,
        start_bar: float,
        end_bar: float,
        start_vol: float,
        end_vol: float,
        curve_type: str = "linear"
    ) -> Dict:
        """
        Create a volume ramp/fade automation.
        
        Args:
            track_index: Target track index
            start_bar: Starting bar position
            end_bar: Ending bar position
            start_vol: Starting volume (0.0-1.0, where 1.0 = 0dB)
            end_vol: Ending volume (0.0-1.0)
            curve_type: Curve type for the ramp
            
        Returns:
            Dict with automation details and status
        """
        try:
            start_beats = start_bar * 4
            end_beats = end_bar * 4
            
            # Generate curve points
            points = self._generate_curve_points(
                start_val=start_vol,
                end_val=end_vol,
                start_time=start_beats,
                end_time=end_beats,
                num_points=16,
                curve_type=curve_type
            )
            
            automation_points = [[p.time, p.value] for p in points]
            
            # Apply volume automation
            result = self.live_bridge.add_automation(
                track_index=track_index,
                parameter_name="volume",
                points=automation_points
            )
            
            ramp_data = {
                "track_index": track_index,
                "start_bar": start_bar,
                "end_bar": end_bar,
                "start_vol": start_vol,
                "end_vol": end_vol,
                "curve_type": curve_type,
                "point_count": len(points),
                "status": "success" if result else "failed"
            }
            
            self._automation_history.append(ramp_data)
            return ramp_data
            
        except Exception as e:
            return {
                "track_index": track_index,
                "status": "error",
                "error": str(e)
            }
    
    def add_build_up_automation(
        self,
        track_indices: List[int],
        build_start: float,
        drop_position: float,
        include_filter: bool = True,
        filter_end_freq: float = 15000,
        include_volume: bool = True,
        volume_boost: float = 0.1,
        include_reverb_send: bool = True,
        reverb_send_final: float = 0.6
    ) -> Dict:
        """
        Create a complete build-up automation package.
        
        Includes:
        - Filter sweeps (opening up to drop)
        - Volume ramps (slight boost leading to drop)
        - Reverb send increases (wash before drop)
        
        Args:
            track_indices: List of track indices to apply automation to
            build_start: Starting bar of build section
            drop_position: Bar position where drop hits
            include_filter: Add filter sweep (default True)
            filter_end_freq: Filter frequency at drop (default 15000)
            include_volume: Add volume automation (default True)
            volume_boost: Volume increase during build (default 0.1)
            include_reverb_send: Add reverb send automation (default True)
            reverb_send_final: Reverb send amount at drop (default 0.6)
            
        Returns:
            Dict with complete build-up automation details
        """
        results = {
            "track_count": len(track_indices),
            "build_start": build_start,
            "drop_position": drop_position,
            "track_automations": [],
            "status": "success"
        }
        
        try:
            for track_index in track_indices:
                track_results = {
                    "track_index": track_index,
                    "automations": []
                }
                
                # 1. Filter sweep (opening up)
                if include_filter:
                    filter_result = self.create_filter_sweep(
                        track_index=track_index,
                        start_bar=build_start,
                        end_bar=drop_position,
                        start_freq=400,
                        end_freq=filter_end_freq,
                        curve_type="exponential"
                    )
                    track_results["automations"].append({
                        "type": "filter_sweep",
                        "result": filter_result
                    })
                
                # 2. Volume ramp (slight boost)
                if include_volume:
                    vol_result = self.create_volume_ramp(
                        track_index=track_index,
                        start_bar=build_start,
                        end_bar=drop_position - 0.5,  # Peak just before drop
                        start_vol=0.85,
                        end_vol=0.85 + volume_boost,
                        curve_type="s_curve"
                    )
                    track_results["automations"].append({
                        "type": "volume_ramp",
                        "result": vol_result
                    })
                    
                    # Drop spike then normalize
                    spike_result = self.create_volume_ramp(
                        track_index=track_index,
                        start_bar=drop_position - 0.25,
                        end_bar=drop_position + 0.25,
                        start_vol=0.85 + volume_boost,
                        end_vol=0.85,
                        curve_type="linear"
                    )
                    track_results["automations"].append({
                        "type": "volume_spike",
                        "result": spike_result
                    })
                
                # 3. Reverb send increase
                if include_reverb_send:
                    # Reverb wash building up
                    reverb_points = [
                        (build_start, 0.1),
                        (build_start + (drop_position - build_start) * 0.5, 0.3),
                        (drop_position - 0.5, reverb_send_final),
                        (drop_position, 0.15)  # Drop reverb at impact
                    ]
                    
                    reverb_result = self.create_send_automation(
                        track_index=track_index,
                        send_index=0,  # Return A (Reverb)
                        points=reverb_points,
                        curve_type="exponential"
                    )
                    track_results["automations"].append({
                        "type": "reverb_send",
                        "result": reverb_result
                    })
                
                results["track_automations"].append(track_results)
            
            return results
            
        except Exception as e:
            results["status"] = "error"
            results["error"] = str(e)
            return results
    
    def create_drop_impact(
        self,
        track_index: int,
        drop_bar: float,
        impact_duration: float = 0.5,
        impact_boost_db: float = 3.0
    ) -> Dict:
        """
        Create a volume spike automation for drop impact.
        
        Args:
            track_index: Target track index
            drop_bar: Bar position of the drop
            impact_duration: Duration of impact spike in bars (default 0.5)
            impact_boost_db: Boost in dB (default 3.0)
            
        Returns:
            Dict with impact automation details
        """
        try:
            # Convert dB boost to linear
            impact_boost_linear = 10 ** (impact_boost_db / 20)
            
            # Create impact curve
            base_vol = 0.85
            peak_vol = min(base_vol * impact_boost_linear, 1.0)
            
            impact_start = drop_bar
            impact_peak = drop_bar + 0.125  # Peak at 1/8th note
            impact_end = drop_bar + impact_duration
            
            points = [
                [impact_start * 4, base_vol],
                [impact_peak * 4, peak_vol],
                [impact_end * 4, base_vol]
            ]
            
            result = self.live_bridge.add_automation(
                track_index=track_index,
                parameter_name="volume",
                points=points
            )
            
            return {
                "track_index": track_index,
                "drop_bar": drop_bar,
                "impact_duration": impact_duration,
                "impact_boost_db": impact_boost_db,
                "peak_vol": peak_vol,
                "status": "success" if result else "failed"
            }
            
        except Exception as e:
            return {
                "track_index": track_index,
                "status": "error",
                "error": str(e)
            }
    
    def create_breakdown_automation(
        self,
        track_indices: List[int],
        breakdown_start: float,
        breakdown_end: float,
        reverb_increase: float = 0.5
    ) -> Dict:
        """
        Create automation for breakdown sections (reverb swells, filter).
        
        Args:
            track_indices: List of track indices
            breakdown_start: Starting bar of breakdown
            breakdown_end: Ending bar of breakdown
            reverb_increase: Amount to increase reverb sends (default 0.5)
            
        Returns:
            Dict with breakdown automation details
        """
        results = {
            "section": "breakdown",
            "tracks": [],
            "status": "success"
        }
        
        try:
            for track_index in track_indices:
                track_data = {"track_index": track_index, "automations": []}
                
                # Filter closing for breakdown
                filter_close = self.create_filter_sweep(
                    track_index=track_index,
                    start_bar=breakdown_start,
                    end_bar=breakdown_start + 2,
                    start_freq=20000,
                    end_freq=800,
                    curve_type="exponential"
                )
                track_data["automations"].append({
                    "type": "filter_close",
                    "result": filter_close
                })
                
                # Reverb swell
                mid_point = (breakdown_start + breakdown_end) / 2
                reverb_points = [
                    (breakdown_start, 0.1),
                    (mid_point, 0.1 + reverb_increase),
                    (breakdown_end - 1, 0.1 + reverb_increase),
                    (breakdown_end, 0.1)
                ]
                
                reverb_result = self.create_send_automation(
                    track_index=track_index,
                    send_index=0,
                    points=reverb_points,
                    curve_type="s_curve"
                )
                track_data["automations"].append({
                    "type": "reverb_swell",
                    "result": reverb_result
                })
                
                # Filter reopen at end
                filter_open = self.create_filter_sweep(
                    track_index=track_index,
                    start_bar=breakdown_end - 2,
                    end_bar=breakdown_end,
                    start_freq=800,
                    end_freq=20000,
                    curve_type="exponential"
                )
                track_data["automations"].append({
                    "type": "filter_reopen",
                    "result": filter_open
                })
                
                results["tracks"].append(track_data)
            
            return results
            
        except Exception as e:
            results["status"] = "error"
            results["error"] = str(e)
            return results
    
    def get_automation_history(self) -> List[Dict]:
        """Return history of all automation operations."""
        return self._automation_history.copy()
    
    def clear_history(self):
        """Clear automation history."""
        self._automation_history = []
