"""
audio_arrangement.py - DJ Arrangement y Estructura
T063-T077: Song Structure, Energy Curve, Transitions
"""
import random
import logging
from typing import List, Dict, Any, Optional
from dataclasses import dataclass

logger = logging.getLogger("AudioArrangement")


@dataclass
class Section:
    """Representa una sección musical"""
    name: str
    kind: str  # intro, build, drop, break, outro
    bars: int
    energy: float  # 0.0 - 1.0


class DJArrangementEngine:
    """T063-T077: Engine de estructuras DJ-friendly"""

    # Energy levels por tipo de sección
    ENERGY_PROFILES = {
        'intro': 0.30,
        'build': 0.70,
        'drop': 1.00,
        'break': 0.50,
        'outro': 0.20,
    }

    def __init__(self, seed: int = 42):
        self.rng = random.Random(seed)

    def generate_structure(self, structure_type: str = "standard") -> List[Section]:
        """
        T063-T066: Genera estructura de canción.

        - standard: 64 bars (Intro 16, Build 16, Drop 16, Break 16, Drop 16, Outro 16)
        - minimal: 48 bars (Intro 8, Build 8, Drop 16, Break 8, Drop 8, Outro 8)
        - extended: 128 bars con A/B drop alternation
        """
        if structure_type == "minimal":
            return [
                Section("Intro", "intro", 8, self.ENERGY_PROFILES['intro']),
                Section("Build 1", "build", 8, self.ENERGY_PROFILES['build']),
                Section("Drop A", "drop", 16, self.ENERGY_PROFILES['drop']),
                Section("Break", "break", 8, self.ENERGY_PROFILES['break']),
                Section("Drop B", "drop", 8, self.ENERGY_PROFILES['drop']),
                Section("Outro", "outro", 8, self.ENERGY_PROFILES['outro']),
            ]
        elif structure_type == "extended":
            return [
                Section("Intro", "intro", 16, self.ENERGY_PROFILES['intro']),
                Section("Build 1", "build", 16, self.ENERGY_PROFILES['build']),
                Section("Drop A", "drop", 16, self.ENERGY_PROFILES['drop']),
                Section("Break 1", "break", 16, self.ENERGY_PROFILES['break']),
                Section("Build 2", "build", 16, self.ENERGY_PROFILES['build']),
                Section("Drop B", "drop", 16, self.ENERGY_PROFILES['drop']),
                Section("Break 2", "break", 16, self.ENERGY_PROFILES['break']),
                Section("Build 3", "build", 16, self.ENERGY_PROFILES['build']),
                Section("Drop C", "drop", 16, self.ENERGY_PROFILES['drop']),
                Section("Outro", "outro", 16, self.ENERGY_PROFILES['outro']),
            ]
        else:  # standard
            return [
                Section("Intro", "intro", 16, self.ENERGY_PROFILES['intro']),
                Section("Build 1", "build", 16, self.ENERGY_PROFILES['build']),
                Section("Drop A", "drop", 16, self.ENERGY_PROFILES['drop']),
                Section("Break", "break", 16, self.ENERGY_PROFILES['break']),
                Section("Drop B", "drop", 16, self.ENERGY_PROFILES['drop']),
                Section("Outro", "outro", 16, self.ENERGY_PROFILES['outro']),
            ]

    def is_dj_friendly(self, structure: List[Section]) -> bool:
        """Verifica si la estructura es DJ-friendly (intro/outro ≥16 beats)."""
        if not structure:
            return False
        intro = structure[0]
        outro = structure[-1]
        # 16 bars = 64 beats
        return intro.bars >= 4 and outro.bars >= 4

    def get_energy_at_position(self, structure: List[Section], bar: int) -> float:
        """T067-T070: Retorna nivel de energía en posición específica."""
        current_bar = 0
        for section in structure:
            if current_bar <= bar < current_bar + section.bars:
                return section.energy
            current_bar += section.bars
        return 0.0

    def generate_energy_automation(self, structure: List[Section]) -> List[Dict]:
        """Genera curva de automatización de energía."""
        automation = []
        current_bar = 0
        for section in structure:
            automation.append({
                'bar': current_bar,
                'energy': section.energy,
                'section': section.name
            })
            current_bar += section.bars
        return automation


class TransitionEngine:
    """T071-T077: Engine de transiciones automáticas"""

    def __init__(self):
        self.logger = logging.getLogger("TransitionEngine")

    def auto_riser(self, section_start: float, n_beats: int = 8) -> Dict:
        """T071: Auto-riser N beats antes de drop."""
        return {
            'type': 'riser',
            'trigger_at': max(0, section_start - n_beats),
            'duration': n_beats,
            'intensity': 'build',
            'auto_trigger': True
        }

    def auto_snare_roll(self, section_start: float, duration_beats: int = 4) -> Dict:
        """T072: Snare roll automático."""
        return {
            'type': 'snare_roll',
            'trigger_at': max(0, section_start - duration_beats),
            'duration': duration_beats,
            'pattern': '1/16 notes',
            'velocity_ramp': True
        }

    def auto_filter_sweep(self, section_start: float, section_end: float,
                          direction: str = "up") -> Dict:
        """T073: Filter sweep en breaks."""
        return {
            'type': 'filter_sweep',
            'direction': direction,
            'start_at': section_start,
            'end_at': section_end,
            'filter_type': 'lowpass',
            'target_freq': 20000 if direction == 'up' else 200
        }

    def auto_downlifter(self, build_section_end: float, drop_section_start: float) -> Dict:
        """T074: Downlifter en build→drop."""
        gap = drop_section_start - build_section_end
        return {
            'type': 'downlifter',
            'trigger_at': build_section_end,
            'duration': min(2.0, gap) if gap > 0 else 2.0,
            'sync_to_drop': True
        }

    def auto_fill(self, section_end: float, density: str = 'medium') -> Dict:
        """T075: Drum fill automático."""
        fill_beats = {'low': 1, 'medium': 2, 'high': 4}.get(density, 2)
        return {
            'type': 'drum_fill',
            'trigger_at': max(0, section_end - fill_beats),
            'duration': fill_beats,
            'density': density
        }

    def generate_all_transitions(self, structure: List[Section]) -> List[Dict]:
        """T076-T077: Genera todas las transiciones para la estructura."""
        events = []
        current_bar = 0

        for i, section in enumerate(structure):
            section_start = current_bar * 4  # Convert bars to beats
            section_end = section_start + (section.bars * 4)

            if section.kind == 'drop':
                # Riser + snare roll antes de drop
                events.append(self.auto_riser(section_start, 8))
                events.append(self.auto_snare_roll(section_start, 4))

            if section.kind == 'break':
                # Filter sweep durante break
                events.append(self.auto_filter_sweep(section_start, section_end, 'up'))

            if section.kind == 'build' and i + 1 < len(structure):
                next_section = structure[i + 1]
                if next_section.kind == 'drop':
                    # Downlifter build→drop
                    events.append(self.auto_downlifter(section_end, section_end + 1))

            # Drum fill al final de secciones intensas
            if section.kind in ['drop', 'build']:
                events.append(self.auto_fill(section_end, 'medium'))

            current_bar += section.bars

        return events
