"""
Workflow Automation Module

High-level workflow functions that orchestrate multiple tools to create complete
music production workflows.
"""

from __future__ import annotations

import logging
import random
from typing import Any, Dict, List, Optional

import reapy

from reaper_mcp.core.mcp_core import mcp
from reaper_mcp.structure import tracks
from reaper_mcp.midi import midi
from reaper_mcp.midi import music_theory
from reaper_mcp.structure import markers
from reaper_mcp.analysis import song_analysis
from reaper_mcp.project import project
from reaper_mcp.structure import tempo

logger = logging.getLogger(__name__)


# Style presets for different genres
STYLE_PRESETS = {
    "rock": {
        "drum_style": "rock",
        "bass_pattern": "root-fifth",
        "chord_voicing": "power",
        "mood": "energetic",
        "complexity": 0.6,
    },
    "jazz": {
        "drum_style": "jazz",
        "bass_pattern": "walking",
        "chord_voicing": "seventh",
        "mood": "chill",
        "complexity": 0.7,
    },
    "edm": {
        "drum_style": "edm",
        "bass_pattern": "root",
        "chord_voicing": "triad",
        "mood": "energetic",
        "complexity": 0.5,
    },
    "hiphop": {
        "drum_style": "hiphop",
        "bass_pattern": "root",
        "chord_voicing": "seventh",
        "mood": "dark",
        "complexity": 0.4,
    },
    "pop": {
        "drum_style": "rock",
        "bass_pattern": "root-fifth",
        "chord_voicing": "triad",
        "mood": "happy",
        "complexity": 0.5,
    },
    "trap": {
        "drum_style": "trap",
        "bass_pattern": "root",
        "chord_voicing": "triad",
        "mood": "dark",
        "complexity": 0.6,
    },
}


@mcp.tool()
def create_song_from_scratch(
    style: str = "rock",
    key: str = "C",
    bpm: int = 120,
    length_bars: int = 8
) -> Dict[str, Any]:
    """Create a complete song arrangement from scratch with drums, bass, chords, and melody.
    
    This high-level function orchestrates multiple tools to create a full arrangement:
    - Creates tracks for drums, bass, chords, and melody
    - Generates appropriate MIDI patterns based on style
    - Sets up tempo and adds basic structure markers
    
    Args:
        style: Musical style (rock, jazz, edm, hiphop, pop, trap)
        key: Musical key (e.g., "C", "Am", "F#")
        bpm: Tempo in beats per minute
        length_bars: Length of the song in bars (4 beats per bar)
    
    Returns:
        Dict with track indices and generated content details
    """
    logger.info(f"create_song_from_scratch: style={style}, key={key}, bpm={bpm}, length_bars={length_bars}")
    
    try:
        # Normalize style
        style = style.lower()
        if style not in STYLE_PRESETS:
            return {"error": f"Unknown style '{style}'. Available: {list(STYLE_PRESETS.keys())}"}
        
        preset = STYLE_PRESETS[style]
        result = {
            "style": style,
            "key": key,
            "bpm": bpm,
            "length_bars": length_bars,
            "tracks_created": []
        }
        
        # Set tempo
        tempo.set_tempo(bpm)
        
        # Suggest chord progression based on mood
        chord_result = music_theory.suggest_chord_progression(
            key=key,
            mood=preset["mood"],
            length=4
        )
        
        if "error" in chord_result:
            return {"error": f"Failed to generate chord progression: {chord_result['error']}"}
        
        chords = chord_result["progression"]
        result["chord_progression"] = chords
        
        # Calculate duration per chord (in quarter notes)
        duration_per_chord = length_bars / len(chords)
        
        # 1. Create drum track
        drum_track = tracks.create_track(name=f"{style.capitalize()} Drums")
        if "error" not in drum_track:
            drum_idx = drum_track["index"]
            drum_notes = midi.generate_drum_pattern(
                style=preset["drum_style"],
                bars=length_bars,
                complexity=preset["complexity"],
                swing=0.0,
                velocity=100
            )
            
            if "error" not in drum_notes:
                midi.add_midi_to_track(drum_idx, drum_notes["notes"], start_time=0.0)
                result["tracks_created"].append({
                    "name": "Drums",
                    "index": drum_idx,
                    "type": "drums"
                })
        
        # 2. Create bass track
        bass_track = tracks.create_track(name=f"{style.capitalize()} Bass")
        if "error" not in bass_track:
            bass_idx = bass_track["index"]
            bass_notes = midi.generate_bass_line(
                chord_progression=chords,
                key=key,
                pattern=preset["bass_pattern"],
                octave=2,
                duration_per_chord=duration_per_chord,
                velocity=90
            )
            
            if "error" not in bass_notes:
                midi.add_midi_to_track(bass_idx, bass_notes["notes"], start_time=0.0)
                result["tracks_created"].append({
                    "name": "Bass",
                    "index": bass_idx,
                    "type": "bass"
                })
        
        # 3. Create chord track
        chord_track = tracks.create_track(name=f"{style.capitalize()} Chords")
        if "error" not in chord_track:
            chord_idx = chord_track["index"]
            chord_notes = midi.generate_chord_progression(
                chords=chords,
                key=key,
                duration_per_chord=duration_per_chord,
                voicing=preset["chord_voicing"],
                octave=4,
                velocity=80
            )
            
            if "error" not in chord_notes:
                midi.add_midi_to_track(chord_idx, chord_notes["notes"], start_time=0.0)
                result["tracks_created"].append({
                    "name": "Chords",
                    "index": chord_idx,
                    "type": "chords"
                })
        
        # 4. Create melody track
        melody_track = tracks.create_track(name=f"{style.capitalize()} Melody")
        if "error" not in melody_track:
            melody_idx = melody_track["index"]
            melody_notes = midi.generate_melody_from_chords(
                chord_progression=chords,
                key=key,
                bars=length_bars,
                note_density=0.5,
                octave=5,
                velocity=85
            )
            
            if "error" not in melody_notes:
                midi.add_midi_to_track(melody_idx, melody_notes["notes"], start_time=0.0)
                result["tracks_created"].append({
                    "name": "Melody",
                    "index": melody_idx,
                    "type": "melody"
                })
        
        # 5. Add structure markers
        proj = reapy.Project()
        song_length_seconds = proj.beats_to_time(length_bars * 4)  # 4 beats per bar
        
        markers.add_marker(0.0, "Start", 0)
        markers.add_marker(song_length_seconds, "End", 0)
        
        result["length_seconds"] = song_length_seconds
        result["success"] = True
        
        logger.info(f"Successfully created song with {len(result['tracks_created'])} tracks")
        return result
        
    except Exception as e:
        error_msg = f"Failed to create song from scratch: {e}"
        logger.error(error_msg, exc_info=True)
        return {"error": error_msg}


@mcp.tool()
def continue_song(target_bars: int = 4, section_type: str = "verse") -> Dict[str, Any]:
    """Generate a continuation of the current song matching the existing style.
    
    Analyzes the current project structure, detects chord progressions and rhythm patterns,
    and generates a continuation that matches the existing musical context.
    
    Args:
        target_bars: Number of bars to generate for the continuation
        section_type: Type of section to create (verse, chorus, bridge, outro)
    
    Returns:
        Dict with analysis results and generated continuation details
    """
    logger.info(f"continue_song: target_bars={target_bars}, section_type={section_type}")
    
    try:
        # Analyze current project structure
        analysis = song_analysis.analyze_project_structure()
        
        if "error" in analysis:
            return {"error": f"Failed to analyze project: {analysis['error']}"}
        
        result = {
            "analysis": analysis,
            "target_bars": target_bars,
            "section_type": section_type,
            "continuation_created": []
        }
        
        # Get current song length to append after it
        proj = reapy.Project()
        current_length_beats = proj.time_to_beats(proj.length)
        start_beat = current_length_beats
        start_time = proj.length
        
        # Get suggestions for next section
        suggestions = song_analysis.suggest_next_section(analysis, section_type)
        
        if "error" in suggestions:
            return {"error": f"Failed to get suggestions: {suggestions['error']}"}
        
        result["suggestions"] = suggestions
        
        key = analysis.get("key", "C")
        tempo_val = analysis.get("tempo", 120)
        suggested_chords = suggestions.get("suggested_chords", ["I", "IV", "V", "I"])
        
        # Convert Roman numerals to actual chords if needed
        if suggested_chords and suggested_chords[0] in ["I", "II", "III", "IV", "V", "VI", "VII"]:
            chord_map = {
                "I": key, "II": key, "III": key, "IV": key,
                "V": key, "VI": key, "VII": key
            }
            # Simplified - use suggested chord progression from music theory
            mood_map = {
                "verse": "chill",
                "chorus": "energetic",
                "bridge": "epic",
                "outro": "sad"
            }
            chord_result = music_theory.suggest_chord_progression(
                key=key,
                mood=mood_map.get(section_type, "chill"),
                length=4
            )
            suggested_chords = chord_result.get("progression", suggested_chords)
        
        duration_per_chord = target_bars / len(suggested_chords)
        
        # Find existing tracks and add continuation
        tracks_list = analysis.get("tracks", [])
        
        for track_info in tracks_list:
            track_idx = track_info["index"]
            track_type = track_info.get("type", "unknown")
            
            # Generate appropriate content based on track type
            if track_type == "drums":
                # Continue drum pattern
                drum_notes = midi.generate_drum_pattern(
                    style="rock",
                    bars=target_bars,
                    complexity=0.5,
                    swing=0.0,
                    velocity=100
                )
                
                if "error" not in drum_notes:
                    midi.add_midi_to_track(track_idx, drum_notes["notes"], start_time=start_time)
                    result["continuation_created"].append({
                        "track": track_idx,
                        "type": "drums",
                        "bars": target_bars
                    })
            
            elif track_type == "bass":
                # Continue bass line
                bass_notes = midi.generate_bass_line(
                    chord_progression=suggested_chords,
                    key=key,
                    pattern="root-fifth",
                    octave=2,
                    duration_per_chord=duration_per_chord,
                    velocity=90
                )
                
                if "error" not in bass_notes:
                    midi.add_midi_to_track(track_idx, bass_notes["notes"], start_time=start_time)
                    result["continuation_created"].append({
                        "track": track_idx,
                        "type": "bass",
                        "chords": suggested_chords
                    })
            
            elif track_type in ["chords", "melody"]:
                # Continue chord or melody
                if track_type == "chords":
                    notes = midi.generate_chord_progression(
                        chords=suggested_chords,
                        key=key,
                        duration_per_chord=duration_per_chord,
                        voicing="triad",
                        octave=4,
                        velocity=80
                    )
                else:
                    notes = midi.generate_melody_from_chords(
                        chord_progression=suggested_chords,
                        key=key,
                        bars=target_bars,
                        note_density=0.5,
                        octave=5,
                        velocity=85
                    )
                
                if "error" not in notes:
                    midi.add_midi_to_track(track_idx, notes["notes"], start_time=start_time)
                    result["continuation_created"].append({
                        "track": track_idx,
                        "type": track_type,
                        "chords": suggested_chords
                    })
        
        # Add section marker
        markers.add_marker(start_time, section_type.capitalize(), 0)
        
        result["success"] = True
        result["start_time"] = start_time
        
        logger.info(f"Successfully continued song with {len(result['continuation_created'])} tracks")
        return result
        
    except Exception as e:
        error_msg = f"Failed to continue song: {e}"
        logger.error(error_msg, exc_info=True)
        return {"error": error_msg}


@mcp.tool()
def create_variation(
    track_index: int,
    variation_type: str = "subtle"
) -> Dict[str, Any]:
    """Create a variation of an existing track.
    
    Analyzes the MIDI content of a track and creates a variation with the specified
    type of changes (subtle, rhythmic, melodic, or harmonic).
    
    Args:
        track_index: Index of the track to create variation from
        variation_type: Type of variation (subtle, rhythmic, melodic, harmonic)
    
    Returns:
        Dict with details of the variation created
    """
    logger.info(f"create_variation: track_index={track_index}, variation_type={variation_type}")
    
    try:
        # Get existing MIDI notes from track
        notes_result = project.get_all_midi_notes_from_track(track_index)
        
        if "error" in notes_result:
            return {"error": f"Failed to get MIDI notes: {notes_result['error']}"}
        
        original_notes = notes_result.get("notes", [])
        
        if not original_notes:
            return {"error": "Track has no MIDI notes to vary"}
        
        result = {
            "track_index": track_index,
            "variation_type": variation_type,
            "original_note_count": len(original_notes),
            "varied_notes": []
        }
        
        # Create a copy of notes for variation
        varied_notes = [note.copy() for note in original_notes]
        
        if variation_type == "subtle":
            # Subtle variation: humanize timing and velocity
            varied_notes = midi.humanize_midi(
                varied_notes,
                timing_variance=0.02,
                velocity_variance=10
            )
            if "notes" in varied_notes:
                varied_notes = varied_notes["notes"]
        
        elif variation_type == "rhythmic":
            # Rhythmic variation: shift some note positions slightly
            for note in varied_notes:
                if random.random() < 0.3:  # 30% of notes
                    # Shift by up to 1/16th note
                    shift = random.uniform(-0.25, 0.25)
                    note["position"] = max(0, note["position"] + shift)
        
        elif variation_type == "melodic":
            # Melodic variation: transpose some notes by octave or small intervals
            for note in varied_notes:
                if random.random() < 0.4:  # 40% of notes
                    # Transpose by small interval (-2 to +2 semitones)
                    transpose = random.choice([-2, -1, 1, 2])
                    note["pitch"] = max(0, min(127, note["pitch"] + transpose))
        
        elif variation_type == "harmonic":
            # Harmonic variation: add or remove notes to change harmony
            # Transpose all notes by a musical interval
            transpose_semitones = random.choice([-5, -3, 3, 5, 7])  # Fourth, minor 3rd, major 3rd, fifth, etc.
            for note in varied_notes:
                note["pitch"] = max(0, min(127, note["pitch"] + transpose_semitones))
            result["transposition"] = transpose_semitones
        
        else:
            return {"error": f"Unknown variation type '{variation_type}'. Use: subtle, rhythmic, melodic, harmonic"}
        
        # Create new track for the variation
        track_name_result = tracks.get_track_name(track_index)
        original_name = track_name_result.get("name", "Track")
        new_track_name = f"{original_name} ({variation_type} var)"
        
        new_track = tracks.create_track(name=new_track_name)
        
        if "error" in new_track:
            return {"error": f"Failed to create variation track: {new_track['error']}"}
        
        new_track_idx = new_track["index"]
        
        # Add varied notes to new track
        midi.add_midi_to_track(new_track_idx, varied_notes, start_time=0.0)
        
        result["new_track_index"] = new_track_idx
        result["new_track_name"] = new_track_name
        result["varied_note_count"] = len(varied_notes)
        result["success"] = True
        
        logger.info(f"Successfully created {variation_type} variation on track {new_track_idx}")
        return result
        
    except Exception as e:
        error_msg = f"Failed to create variation: {e}"
        logger.error(error_msg, exc_info=True)
        return {"error": error_msg}


@mcp.tool()
def arrange_song_structure(
    sections: List[Dict[str, Any]]
) -> Dict[str, Any]:
    """Create markers and regions for a song structure.
    
    Creates an organized song structure with markers for section starts and regions
    for each section. Useful for planning arrangements.
    
    Args:
        sections: List of section dicts, each containing:
            - name: Section name (e.g., "Intro", "Verse 1", "Chorus")
            - bars: Length in bars (4 beats per bar)
            - type: Optional type (verse, chorus, bridge, intro, outro) for coloring
    
    Returns:
        Dict with created markers and regions
    
    Example:
        sections = [
            {"name": "Intro", "bars": 4, "type": "intro"},
            {"name": "Verse 1", "bars": 8, "type": "verse"},
            {"name": "Chorus", "bars": 8, "type": "chorus"},
            {"name": "Verse 2", "bars": 8, "type": "verse"},
            {"name": "Chorus", "bars": 8, "type": "chorus"},
            {"name": "Bridge", "bars": 4, "type": "bridge"},
            {"name": "Outro", "bars": 4, "type": "outro"}
        ]
    """
    logger.info(f"arrange_song_structure: {len(sections)} sections")
    
    try:
        if not sections:
            return {"error": "No sections provided"}
        
        result = {
            "sections": sections,
            "markers_created": [],
            "regions_created": []
        }
        
        # Color mapping for different section types
        section_colors = {
            "intro": 16777215,      # White
            "verse": 8421631,       # Purple
            "chorus": 255,          # Red
            "bridge": 16776960,     # Yellow
            "outro": 8421504,       # Gray
            "default": 0            # Default color
        }
        
        proj = reapy.Project()
        current_time = 0.0
        current_beat = 0.0
        
        for i, section in enumerate(sections):
            name = section.get("name", f"Section {i+1}")
            bars = section.get("bars", 4)
            section_type = section.get("type", "default").lower()
            
            # Calculate section length in beats and seconds
            beats = bars * 4  # 4 beats per bar
            section_length = proj.beats_to_time(beats)
            
            # Get color for this section type
            color = section_colors.get(section_type, section_colors["default"])
            
            # Add marker at section start
            marker_result = markers.add_marker(current_time, name, color)
            if "error" not in marker_result:
                result["markers_created"].append({
                    "name": name,
                    "position": current_time,
                    "index": marker_result.get("index")
                })
            
            # Add region for the section
            region_end = current_time + section_length
            region_result = markers.add_region(current_time, region_end, name, color)
            if "error" not in region_result:
                result["regions_created"].append({
                    "name": name,
                    "start": current_time,
                    "end": region_end,
                    "bars": bars,
                    "index": region_result.get("index")
                })
            
            # Move to next section
            current_time += section_length
            current_beat += beats
        
        # Add final end marker
        final_marker = markers.add_marker(current_time, "End", 0)
        if "error" not in final_marker:
            result["markers_created"].append({
                "name": "End",
                "position": current_time,
                "index": final_marker.get("index")
            })
        
        result["total_length_seconds"] = current_time
        result["total_bars"] = sum(s.get("bars", 4) for s in sections)
        result["success"] = True
        
        logger.info(f"Successfully created structure with {len(result['markers_created'])} markers and {len(result['regions_created'])} regions")
        return result
        
    except Exception as e:
        error_msg = f"Failed to arrange song structure: {e}"
        logger.error(error_msg, exc_info=True)
        return {"error": error_msg}
