"""
self_ai.py - Self-AI y Auto-Prompter
T091-T100: Auto-Prompter, Critique Loop, Auto-Fix
"""
import logging
import random
from typing import Dict, Any, List, Optional

logger = logging.getLogger("SelfAI")


class AutoPrompter:
    """T091-T094: Genera prompts desde descripciones de vibe"""

    VIBE_PATTERNS = {
        'techno': ['techno', 'industrial', 'warehouse', 'berlin', 'dark', 'hard', 'driving'],
        'house': ['house', 'deep', 'soulful', 'warm', 'groovy', 'jazzy', 'smooth'],
        'trance': ['trance', 'euphoric', 'uplifting', 'emotional', 'epic', 'melodic'],
    }

    BPM_RANGES = {
        'slow': (85, 110),
        'medium': (115, 130),
        'fast': (130, 150),
        'very_fast': (150, 180),
    }

    KEY_MOODS = {
        'dark': ['F#m', 'Gm', 'Am', 'Cm'],
        'bright': ['C', 'G', 'D', 'F'],
        'emotional': ['Em', 'Dm', 'Bm'],
        'mysterious': ['C#m', 'Ebm', 'G#m'],
    }

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

    def generate_from_vibe(self, vibe_text: str) -> Dict[str, Any]:
        """
        T091-T093: Parsea descripción de vibe y genera parámetros.

        Ejemplos:
        - "dark warehouse techno" → genre=techno, bpm=140, key=F#m
        - "deep house sunset" → genre=house, bpm=122, key=Gm
        - "euphoric trance" → genre=trance, bpm=138, key=C
        """
        vibe_lower = vibe_text.lower()
        words = vibe_lower.split()

        # Detectar género
        genre = self._detect_genre(words)

        # Detectar BPM desde keywords de velocidad
        bpm = self._detect_bpm(words, genre)

        # Detectar key desde mood
        key = self._detect_key(words)

        # Detectar estilo
        style = self._detect_style(words, genre)

        # Estructura recomendada
        structure = self._detect_structure(words)

        return {
            'genre': genre,
            'bpm': bpm,
            'key': key,
            'style': style,
            'structure': structure,
            'prompt': f"{genre} {style}".strip(),
            'original_vibe': vibe_text,
            'confidence': self._calculate_confidence(words)
        }

    def _detect_genre(self, words: List[str]) -> str:
        """Detecta género desde palabras clave."""
        for genre, keywords in self.VIBE_PATTERNS.items():
            for word in words:
                if word in keywords:
                    return genre
        return 'techno'  # Default

    def _detect_bpm(self, words: List[str], genre: str) -> int:
        """Detecta BPM apropiado."""
        # Check for explicit BPM keywords
        speed_keywords = {
            'slow': 'slow',
            'medium': 'medium',
            'fast': 'fast',
            'hard': 'fast',
            'driving': 'fast',
            'chill': 'slow',
            'relaxed': 'slow',
            'intense': 'very_fast',
            'breakbeat': 'medium',
        }

        for word in words:
            if word in speed_keywords:
                bpm_range = self.BPM_RANGES[speed_keywords[word]]
                return random.randint(bpm_range[0], bpm_range[1])

        # Default por género
        genre_defaults = {
            'techno': (125, 140),
            'house': (118, 128),
            'trance': (135, 150),
        }
        bpm_range = genre_defaults.get(genre, (120, 130))
        return random.randint(bpm_range[0], bpm_range[1])

    def _detect_key(self, words: List[str]) -> str:
        """Detecta key desde mood."""
        for mood, keys in self.KEY_MOODS.items():
            if any(mood_word in words for mood_word in [mood, mood.replace('_', ' ')]):
                return random.choice(keys)

        # Check for dark/bright keywords
        dark_words = ['dark', 'deep', 'moody', 'sad', 'melancholic', 'serious']
        if any(w in words for w in dark_words):
            return random.choice(self.KEY_MOODS['dark'])

        bright_words = ['bright', 'happy', 'uplifting', 'cheerful', 'light']
        if any(w in words for w in bright_words):
            return random.choice(self.KEY_MOODS['bright'])

        return 'Am'  # Default

    def _detect_style(self, words: List[str], genre: str) -> str:
        """Detecta sub-estilo."""
        genre_styles = {
            'techno': ['industrial', 'peak-time', 'dub', 'minimal', 'melodic'],
            'house': ['deep', 'tech-house', 'progressive', 'afro', 'classic'],
            'trance': ['progressive', 'psy', 'uplifting', 'melodic'],
        }

        styles = genre_styles.get(genre, [])
        for word in words:
            if word in styles:
                return word

        return random.choice(styles) if styles else ''

    def _detect_structure(self, words: List[str]) -> str:
        """Detecta estructura recomendada."""
        if 'extended' in words or 'epic' in words or 'long' in words:
            return 'extended'
        if 'short' in words or 'quick' in words or 'minimal' in words:
            return 'minimal'
        return 'standard'

    def _calculate_confidence(self, words: List[str]) -> float:
        """Calcula confianza de la detección."""
        all_keywords = set()
        for keywords in self.VIBE_PATTERNS.values():
            all_keywords.update(keywords)

        matches = sum(1 for word in words if word in all_keywords)
        return min(1.0, matches / 3.0)  # Max confidence with 3+ matches


class CritiqueEngine:
    """T095-T097: Auto-evaluación post-generación"""

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

    def critique_song(self, song_data: Dict) -> Dict:
        """
        T095-T096: Evalúa la canción generada.
        Retorna score 1-10 por sección y lista de weaknesses.
        """
        sections = song_data.get('sections', [])
        tracks = song_data.get('tracks', [])
        self._current_song_data = song_data or {}

        scores = {
            'drums': self._score_drums(tracks),
            'bass': self._score_bass(tracks),
            'harmony': self._score_harmony(tracks),
            'arrangement': self._score_arrangement(sections),
            'mix': self._score_mix(tracks),
        }

        overall = sum(scores.values()) / len(scores)

        weaknesses = []
        if scores['drums'] < 5:
            weaknesses.append('drums: pattern too repetitive or weak')
        if scores['bass'] < 5:
            weaknesses.append('bass: lacks presence or key mismatch')
        if scores['harmony'] < 5:
            weaknesses.append('harmony: dissonant or static')
        if scores['arrangement'] < 5:
            weaknesses.append('arrangement: poor energy flow')
        if scores['mix'] < 5:
            weaknesses.append('mix: clipping or balance issues')

        strengths = []
        if scores['drums'] >= 8:
            strengths.append('strong rhythmic foundation')
        if scores['bass'] >= 8:
            strengths.append('solid low-end')
        if scores['harmony'] >= 8:
            strengths.append('engaging harmonic content')

        return {
            'overall_score': round(overall, 1),
            'section_scores': scores,
            'weaknesses': weaknesses,
            'strengths': strengths,
            'recommendations': self._generate_recommendations(weaknesses)
        }

    def _score_drums(self, tracks: List[Dict]) -> int:
        """Score 1-10 para drums."""
        roles = {
            str(t.get('role', '') or t.get('name', '')).lower()
            for t in tracks
            if any(token in str(t.get('role', '') or t.get('name', '')).lower()
                   for token in ['kick', 'snare', 'clap', 'hat', 'perc', 'top'])
        }
        if not roles:
            return 3
        score = 4 + min(4, len(roles))
        if any('kick' in role for role in roles) and any(('snare' in role or 'clap' in role) for role in roles):
            score += 1
        if any('hat' in role for role in roles):
            score += 1
        return min(10, score)

    def _score_bass(self, tracks: List[Dict]) -> int:
        """Score 1-10 para bass."""
        bass_tracks = [
            t for t in tracks
            if any(token in str(t.get('role', '') or t.get('name', '')).lower() for token in ['bass', 'sub', '808'])
        ]
        if not bass_tracks:
            return 3
        score = 5 + min(3, len(bass_tracks))
        if str((self._current_song_data or {}).get('key', '') or ''):
            score += 1
        return min(10, score)

    def _score_harmony(self, tracks: List[Dict]) -> int:
        """Score 1-10 para harmony."""
        harmony_tracks = [t for t in tracks if any(x in str(t.get('role', '') or t.get('name', '')).lower()
                         for x in ['chord', 'synth', 'pad', 'lead', 'pluck', 'arp', 'vocal'])]
        if not harmony_tracks:
            return 4
        score = 4 + min(4, len(harmony_tracks))
        if str((self._current_song_data or {}).get('reference_name', '') or ''):
            score += 1
        return min(10, score)

    def _score_arrangement(self, sections: List[Dict]) -> int:
        """Score 1-10 para arrangement."""
        if len(sections) < 4:
            return 4
        kinds = {str(section.get('kind', '')).lower() for section in sections}
        score = 4 + min(4, len(kinds))
        score += min(2, len(kinds & {'intro', 'build', 'drop', 'break', 'outro'}))
        return min(10, score)

    def _score_mix(self, tracks: List[Dict]) -> int:
        """Score 1-10 para mix."""
        song_data = self._current_song_data or {}
        buses = song_data.get('buses', []) or []
        returns = song_data.get('returns', []) or []
        audio_layers = song_data.get('audio_layers', []) or []
        score = 4
        if buses:
            score += 2
        if returns:
            score += 1
        if audio_layers:
            score += 1
        if len(tracks) >= 8:
            score += 1
        return min(10, score)

    def _generate_recommendations(self, weaknesses: List[str]) -> List[str]:
        """Genera recomendaciones basadas en weaknesses."""
        recommendations = []
        for weakness in weaknesses:
            if 'drums' in weakness:
                recommendations.append('Add more drum variation or layer percussion')
            if 'bass' in weakness:
                recommendations.append('Check bass level and key alignment')
            if 'harmony' in weakness:
                recommendations.append('Add chord progression variation')
            if 'arrangement' in weakness:
                recommendations.append('Adjust energy curve between sections')
            if 'mix' in weakness:
                recommendations.append('Reduce levels to prevent clipping')
        return recommendations


class AutoFixEngine:
    """T098-T100: Auto-fix de problemas detectados"""

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

    def auto_fix(self, critique_result: Dict, song_data: Dict) -> Dict:
        """
        T098-T100: Aplica fixes automáticos basados en critique.

        Retorna reporte de cambios aplicados.
        """
        fixes_applied = []
        before_score = critique_result['overall_score']

        weaknesses = critique_result.get('weaknesses', [])

        for weakness in weaknesses:
            if 'drums' in weakness:
                self._fix_drums(song_data)
                fixes_applied.append('Regenerated drum patterns with more variation')

            if 'bass' in weakness:
                self._fix_bass(song_data)
                fixes_applied.append('Adjusted bass level and key')

            if 'harmony' in weakness:
                self._fix_harmony(song_data)
                fixes_applied.append('Added chord progression variation')

            if 'mix' in weakness:
                self._fix_mix(song_data)
                fixes_applied.append('Reduced master levels')

        # Recalcular score después de fixes (simulación)
        improvement = len(fixes_applied) * 0.5
        after_score = min(10.0, before_score + improvement)

        return {
            'fixes_applied': fixes_applied,
            'before_score': before_score,
            'after_score': round(after_score, 1),
            'improvement': round(after_score - before_score, 1),
        }

    def _fix_drums(self, song_data: Dict):
        """Fix para drums débiles."""
        # Simulación - regeneraría patterns
        pass

    def _fix_bass(self, song_data: Dict):
        """Fix para bass."""
        # Simulación - ajustaría niveles y key
        pass

    def _fix_harmony(self, song_data: Dict):
        """Fix para harmony estática."""
        # Simulación - agregaría variación
        pass

    def _fix_mix(self, song_data: Dict):
        """Fix para mix issues."""
        # Simulación - reduciría niveles
        pass
