"""
bus_routing_fix.py - Fix de enrutamiento de buses
T101-T104: Bus Routing System Fix

Problemas a resolver:
- Drums van a drum rack pero también a master
- FX no llegan a los returns correctos
- Vocal chops en bus de FX en lugar de Vocal
"""
import logging
from typing import Dict, Any, List, Optional
from dataclasses import dataclass

logger = logging.getLogger("BusRoutingFix")


@dataclass
class BusRoute:
    """Definición de ruta de bus"""
    source_track: str
    target_bus: str
    send_level: float = 0.0  # 0.0 = no send, 1.0 = full send
    should_go_to_master: bool = True


class BusRoutingRules:
    """T101: Reglas de enrutamiento por tipo de track"""

    # Mapeo de roles a buses
    ROLE_TO_BUS = {
        'kick': 'drums',
        'clap': 'drums',
        'snare': 'drums',
        'hat': 'drums',
        'perc': 'drums',
        'ride': 'drums',
        'top_loop': 'drums',
        'drum_loop': 'drums',
        'breakbeat': 'drums',
        'sub_bass': 'bass',
        'bass': 'bass',
        'bass_loop': 'bass',
        'chords': 'music',
        'pad': 'music',
        'pluck': 'music',
        'arp': 'music',
        'lead': 'music',
        'counter': 'music',
        'synth': 'music',
        'vocal': 'vocal',
        'vocal_chop': 'vocal',
        'vox': 'vocal',
        'voice': 'vocal',
        'riser': 'fx',
        'downlifter': 'fx',
        'impact': 'fx',
        'crash': 'fx',
        'atmos': 'fx',
        'reverse_fx': 'fx',
        'texture': 'fx',
    }

    # Buses RCA disponibles
    RCA_BUSES = ['drums', 'bass', 'music', 'vocal', 'fx']

    # Returns configurados en Live
    RETURN_TRACKS = ['Reverb', 'Delay', 'Chorus', 'Spatial']

    @classmethod
    def get_bus_for_role(cls, role: str) -> str:
        """Retorna el bus RCA apropiado para un rol."""
        role_lower = role.lower().replace('_loop', '').replace('loop_', '')

        # Check direct match
        if role_lower in cls.ROLE_TO_BUS:
            return cls.ROLE_TO_BUS[role_lower]

        # Check partial match
        for key, bus in cls.ROLE_TO_BUS.items():
            if key in role_lower or role_lower in key:
                return bus

        # Default por categoría
        if any(d in role_lower for d in ['drum', 'kick', 'snare', 'hat', 'perc']):
            return 'drums'
        if any(b in role_lower for b in ['bass', 'sub', '808', 'low']):
            return 'bass'
        if any(s in role_lower for s in ['synth', 'pad', 'chord', 'lead', 'pluck', 'melody']):
            return 'music'
        if any(v in role_lower for v in ['vocal', 'vox', 'voice', 'chant']):
            return 'vocal'
        if any(f in role_lower for f in ['fx', 'riser', 'impact', 'atmos', 'texture', 'noise']):
            return 'fx'

        return 'music'  # Default fallback


class BusRoutingFixer:
    """T102-T104: Aplica fixes de enrutamiento"""

    def __init__(self):
        self.rules = BusRoutingRules()
        self.issues_found: List[Dict] = []
        self.fixes_applied: List[Dict] = []

    def diagnose_routing(self, tracks_data: List[Dict]) -> List[Dict]:
        """
        T102: Diagnostica problemas de enrutamiento.

        Args:
            tracks_data: Lista de tracks con sus configuraciones

        Returns:
            Lista de problemas encontrados
        """
        issues = []

        for track in tracks_data:
            track_name = track.get('name', 'Unknown')
            track_role = track.get('role', '')
            current_bus = track.get('output_bus', 'master')

            # Determinar bus correcto
            correct_bus = self.rules.get_bus_for_role(track_role or track_name)

            # Verificar si está en bus incorrecto
            if current_bus != correct_bus and current_bus != 'master':
                issues.append({
                    'track': track_name,
                    'role': track_role,
                    'current_bus': current_bus,
                    'correct_bus': correct_bus,
                    'issue': 'wrong_bus',
                    'severity': 'high' if correct_bus != 'music' else 'medium'
                })

            # Verificar sends incorrectos (ej: drums enviando a reverb fuerte)
            sends = track.get('sends', {})
            if track_role in ['kick', 'sub_bass']:
                reverb_send = sends.get('Reverb', 0)
                if reverb_send > 0.3:
                    issues.append({
                        'track': track_name,
                        'role': track_role,
                        'issue': 'excessive_reverb_on_low',
                        'current_send': reverb_send,
                        'recommended': 0.1,
                        'severity': 'medium'
                    })

            # Verificar que FX tracks no van a master directo
            if correct_bus == 'fx' and track.get('audio_output') == 'Master':
                issues.append({
                    'track': track_name,
                    'role': track_role,
                    'issue': 'fx_to_master_bypass',
                    'severity': 'low'
                })

        self.issues_found = issues
        return issues

    def apply_routing_fixes(self, ableton_connection, tracks_data: List[Dict]) -> Dict:
        """
        T103: Aplica fixes de enrutamiento en Ableton.

        Args:
            ableton_connection: Conexión a Ableton Live
            tracks_data: Datos de tracks a corregir

        Returns:
            Reporte de fixes aplicados
        """
        fixes = []

        for track in tracks_data:
            track_name = track.get('name')
            track_index = track.get('index')
            track_role = track.get('role', '')

            # Determinar bus correcto
            correct_bus = self.rules.get_bus_for_role(track_role or track_name)

            try:
                # 1. Cambiar output del track al bus RCA
                # Esto requiere que los buses RCA existan como tracks de audio
                self._set_track_output(ableton_connection, track_index, correct_bus)

                # 2. Ajustar sends si es necesario
                if track_role in ['kick', 'sub_bass']:
                    self._adjust_send(ableton_connection, track_index, 'Reverb', 0.1)

                fixes.append({
                    'track': track_name,
                    'action': f'routed_to_{correct_bus}',
                    'success': True
                })

            except Exception as e:
                fixes.append({
                    'track': track_name,
                    'action': 'routing_fix',
                    'success': False,
                    'error': str(e)
                })

        self.fixes_applied = fixes
        return {
            'total_tracks': len(tracks_data),
            'fixes_applied': len([f for f in fixes if f.get('success')]),
            'fixes_failed': len([f for f in fixes if not f.get('success')]),
            'details': fixes
        }

    def _set_track_output(self, ableton_connection, track_index: int, output_bus: str):
        """Setea output de un track a un bus específico."""
        # Comando MCP para cambiar output
        cmd = {
            'command': 'set_track_output',
            'track_index': track_index,
            'output': output_bus
        }
        ableton_connection.send_command(cmd)

    def _adjust_send(self, ableton_connection, track_index: int, send_name: str, level: float):
        """Ajusta nivel de send."""
        cmd = {
            'command': 'set_send_level',
            'track_index': track_index,
            'send_name': send_name,
            'level': level
        }
        ableton_connection.send_command(cmd)

    def validate_routing(self, tracks_data: List[Dict]) -> Dict:
        """
        T104: Valida que el enrutamiento esté correcto.

        Returns:
            Reporte de validación
        """
        issues = self.diagnose_routing(tracks_data)

        critical = [i for i in issues if i.get('severity') == 'high']
        warnings = [i for i in issues if i.get('severity') in ['medium', 'low']]

        return {
            'valid': len(critical) == 0,
            'critical_issues': len(critical),
            'warnings': len(warnings),
            'total_issues': len(issues),
            'issues': issues
        }

    def get_bus_routing_config(self) -> Dict[str, Any]:
        """Retorna configuración completa de enrutamiento."""
        return {
            'buses': self.rules.RCA_BUSES,
            'returns': self.rules.RETURN_TRACKS,
            'role_mapping': self.rules.ROLE_TO_BUS,
            'validation_rules': {
                'kick_reverb_max': 0.1,
                'sub_bass_reverb_max': 0.05,
                'drums_to_fx_send': 0.0,
            }
        }


# Instancia global
_routing_fixer: Optional[BusRoutingFixer] = None


def get_routing_fixer() -> BusRoutingFixer:
    """Obtiene instancia global del fixer."""
    global _routing_fixer
    if _routing_fixer is None:
        _routing_fixer = BusRoutingFixer()
    return _routing_fixer
