"""Session View orchestrator - ensures MIDI tracks have instruments loaded."""
from typing import Dict, List, Optional
import logging

logger = logging.getLogger("SessionOrchestrator")


class SessionOrchestrator:
    """Validates and fixes Session View MIDI tracks."""
    
    INSTRUMENT_MAP = {
        'piano': 'Grand Piano',
        'keys': 'Electric Piano',
        'synth': 'Wavetable',
        'pad': 'Wavetable',
        'bass': 'Operator',
        'sub_bass': 'Operator',
        'lead': 'Wavetable',
        'pluck': 'Operator',
        'drums': 'Wavetable',  # For drum racks
    }
    
    # MIDI note ranges for different instrument types
    INSTRUMENT_RANGES = {
        'piano': (21, 108),      # A0 to C8
        'keys': (28, 103),       # E1 to G7
        'synth': (24, 96),       # C1 to C7
        'pad': (24, 84),         # C1 to C6
        'bass': (24, 60),        # C1 to C4
        'sub_bass': (20, 48),    # E0 to C3
        'lead': (36, 96),        # C2 to C7
        'pluck': (36, 96),       # C2 to C7
        'drums': (36, 51),       # C1 to D#2 (standard drum rack)
    }
    
    def __init__(self, ableton_connection):
        self.ableton = ableton_connection
    
    def validate_midi_track(self, track_index: int) -> Dict:
        """
        Check if MIDI track has:
        - Instrument/device loaded
        - Clips with notes
        - Proper configuration
        
        Returns: {"valid": bool, "issues": [...], "suggestions": [...]}
        """
        issues = []
        suggestions = []
        
        try:
            # Get track from Ableton
            if not hasattr(self.ableton, 'song'):
                return {"valid": False, "issues": ["No Ableton connection"], "suggestions": []}
            
            song = self.ableton.song()
            tracks = list(song.tracks)
            
            if track_index >= len(tracks):
                return {"valid": False, "issues": [f"Track index {track_index} out of range"], "suggestions": []}
            
            track = tracks[track_index]
            
            # Check if track has devices
            devices = list(track.devices)
            if not devices:
                issues.append("No instrument loaded on track")
                suggestions.append("Load appropriate instrument based on track name")
            
            # Check for MIDI clips
            has_clips = False
            has_notes = False
            
            if hasattr(track, 'arrangement_clips'):
                clips = list(track.arrangement_clips)
                has_clips = len(clips) > 0
                
                for clip in clips:
                    if hasattr(clip, 'get_notes'):
                        notes = clip.get_notes()
                        if notes and len(notes) > 0:
                            has_notes = True
                            break
            
            if not has_clips:
                issues.append("No MIDI clips on track")
                suggestions.append("Create MIDI clips or add content")
            elif not has_notes:
                issues.append("MIDI clips have no notes")
                suggestions.append("Add MIDI notes to clips")
            
            # Check if it's a MIDI track
            is_midi_track = hasattr(track, 'is_midi_track') and track.is_midi_track
            if not is_midi_track:
                # Check if it has audio input (might be audio track trying to play MIDI)
                if hasattr(track, 'has_audio_input') and track.has_audio_input:
                    issues.append("Audio track cannot play MIDI")
                    suggestions.append("Convert to MIDI track or use audio samples")
            
            valid = len(issues) == 0
            
            return {
                "valid": valid,
                "issues": issues,
                "suggestions": suggestions,
                "track_name": track.name if hasattr(track, 'name') else "Unknown",
                "device_count": len(devices),
                "clip_count": len(clips) if has_clips else 0
            }
            
        except Exception as e:
            logger.error(f"Error validating track {track_index}: {e}")
            return {"valid": False, "issues": [str(e)], "suggestions": ["Check Ableton connection"]}
    
    def load_instrument(self, track_index: int, instrument_type: str) -> bool:
        """
        Load appropriate instrument on MIDI track.
        
        Args:
            track_index: Track index
            instrument_type: Key from INSTRUMENT_MAP
            
        Returns:
            True if successful, False otherwise
        """
        try:
            if not hasattr(self.ableton, 'song'):
                logger.error("No Ableton connection available")
                return False
            
            song = self.ableton.song()
            tracks = list(song.tracks)
            
            if track_index >= len(tracks):
                logger.error(f"Track index {track_index} out of range")
                return False
            
            track = tracks[track_index]
            instrument_name = self.INSTRUMENT_MAP.get(instrument_type, 'Wavetable')
            
            # Check if instrument already loaded
            existing_devices = [d.name for d in track.devices]
            if instrument_name in existing_devices:
                logger.info(f"Instrument '{instrument_name}' already loaded on track {track_index}")
                return True
            
            # Use live_bridge to insert device
            try:
                from .live_bridge import LiveBridge
                bridge = LiveBridge(self.ableton)
                bridge.insert_device(track_index, instrument_name)
                logger.info(f"Loaded '{instrument_name}' on track {track_index}")
                return True
            except ImportError:
                logger.warning("LiveBridge not available, cannot load instrument")
                return False
            except Exception as e:
                logger.error(f"Failed to load instrument via LiveBridge: {e}")
                return False
                
        except Exception as e:
            logger.error(f"Error loading instrument on track {track_index}: {e}")
            return False
    
    def auto_fix_session(self, track_indices: List[int]) -> Dict:
        """
        Automatically fix all MIDI tracks in session.
        
        Detects track type from name and loads appropriate instrument.
        
        Returns: {"fixed": [...], "failed": [...]}
        """
        fixed = []
        failed = []
        
        try:
            if not hasattr(self.ableton, 'song'):
                return {"fixed": [], "failed": track_indices, "error": "No Ableton connection"}
            
            song = self.ableton.song()
            tracks = list(song.tracks)
            
            for track_index in track_indices:
                if track_index >= len(tracks):
                    failed.append({"index": track_index, "reason": "Track index out of range"})
                    continue
                
                track = tracks[track_index]
                track_name = track.name if hasattr(track, 'name') else ""
                
                # Detect instrument type from name
                instrument_type = self.detect_track_type(track_name)
                
                if instrument_type:
                    success = self.load_instrument(track_index, instrument_type)
                    if success:
                        fixed.append({
                            "index": track_index,
                            "name": track_name,
                            "instrument": self.INSTRUMENT_MAP.get(instrument_type)
                        })
                    else:
                        failed.append({
                            "index": track_index,
                            "name": track_name,
                            "reason": "Failed to load instrument"
                        })
                else:
                    # Could not detect type, validate anyway
                    validation = self.validate_midi_track(track_index)
                    if not validation["valid"]:
                        failed.append({
                            "index": track_index,
                            "name": track_name,
                            "reason": "Could not detect instrument type and has issues: " + ", ".join(validation["issues"])
                        })
                    else:
                        fixed.append({
                            "index": track_index,
                            "name": track_name,
                            "instrument": "Already valid"
                        })
            
            return {
                "fixed": fixed,
                "failed": failed,
                "total_processed": len(track_indices)
            }
            
        except Exception as e:
            logger.error(f"Error in auto_fix_session: {e}")
            return {"fixed": fixed, "failed": failed + [{"index": i, "reason": str(e)} for i in track_indices if i not in [f["index"] for f in fixed]]}
    
    def detect_track_type(self, track_name: str) -> Optional[str]:
        """
        Detect instrument type from track name.
        
        Examples:
        - "Piano" -> "piano"
        - "Sub Bass" -> "sub_bass"
        - "Lead" -> "lead"
        """
        name_lower = track_name.lower()
        
        # Check for specific multi-word patterns first
        if 'sub bass' in name_lower or 'subbass' in name_lower:
            return 'sub_bass'
        if 'electric piano' in name_lower or 'e-piano' in name_lower or 'rhodes' in name_lower:
            return 'keys'
        if 'drum rack' in name_lower or 'drums' in name_lower:
            return 'drums'
        if 'bass' in name_lower and ('synth' in name_lower or 'fm' in name_lower):
            return 'bass'
        
        # Check single keywords
        for key in self.INSTRUMENT_MAP.keys():
            if key in name_lower:
                return key
        
        # Check for common synonyms
        if 'piano' in name_lower:
            return 'piano'
        if 'rhodes' in name_lower or 'electric' in name_lower:
            return 'keys'
        if '808' in name_lower or 'sub' in name_lower:
            return 'sub_bass'
        if 'bass' in name_lower:
            return 'bass'
        if 'melody' in name_lower or 'arp' in name_lower:
            return 'lead'
        if 'pad' in name_lower or 'chord' in name_lower:
            return 'pad'
        if 'stab' in name_lower or 'hit' in name_lower:
            return 'pluck'
        
        return None
    
    def get_instrument_range(self, instrument_type: str) -> Optional[tuple]:
        """
        Get the recommended MIDI note range for an instrument type.
        
        Args:
            instrument_type: Key from INSTRUMENT_MAP
            
        Returns:
            Tuple of (min_note, max_note) or None if type not found
        """
        return self.INSTRUMENT_RANGES.get(instrument_type)
    
    def suggest_instrument_for_melody(self, melody_notes: List[int]) -> str:
        """
        Suggest an appropriate instrument based on the note range of a melody.
        
        Args:
            melody_notes: List of MIDI note numbers
            
        Returns:
            Suggested instrument type key
        """
        if not melody_notes:
            return 'synth'
        
        min_note = min(melody_notes)
        max_note = max(melody_notes)
        note_range = max_note - min_note
        
        # Low notes -> bass instruments
        if max_note <= 48:
            return 'sub_bass' if min_note < 28 else 'bass'
        
        # Very high notes -> lead or pluck
        if min_note >= 72:
            return 'lead'
        
        # Mid range -> could be keys or lead depending on range
        if note_range <= 12:
            return 'pluck'  # Small range suggests stab/pluck
        elif note_range <= 24:
            return 'keys'   # Medium range suggests keys
        else:
            return 'synth'  # Large range suggests versatile synth


def validate_and_fix_track(ableton, track_index: int, track_name: str) -> bool:
    """Convenience function to validate and fix single track."""
    orchestrator = SessionOrchestrator(ableton)
    track_type = orchestrator.detect_track_type(track_name)
    
    if track_type:
        return orchestrator.load_instrument(track_index, track_type)
    
    return False


def ensure_session_ready(ableton, track_indices: List[int] = None) -> Dict:
    """
    Ensure all MIDI tracks in session have instruments loaded.
    
    Convenience function that auto-detects MIDI tracks and fixes them.
    
    Args:
        ableton: Ableton Live connection
        track_indices: Optional specific track indices to check. If None, checks all tracks.
        
    Returns:
        Result dict with fixed and failed tracks
    """
    try:
        if not hasattr(ableton, 'song'):
            return {"error": "No Ableton connection", "fixed": [], "failed": []}
        
        song = ableton.song()
        tracks = list(song.tracks)
        
        if track_indices is None:
            # Auto-detect MIDI tracks
            track_indices = []
            for i, track in enumerate(tracks):
                # Check if it's a MIDI track or has MIDI content
                is_midi = False
                if hasattr(track, 'is_midi_track') and track.is_midi_track:
                    is_midi = True
                elif hasattr(track, 'has_midi_input') and track.has_midi_input:
                    is_midi = True
                
                if is_midi:
                    track_indices.append(i)
        
        orchestrator = SessionOrchestrator(ableton)
        return orchestrator.auto_fix_session(track_indices)
        
    except Exception as e:
        logger.error(f"Error ensuring session ready: {e}")
        return {"error": str(e), "fixed": [], "failed": []}
