"""
Export Engine - Professional Export with Metadata

Handles audio export to various formats with validation and statistics.
"""

import os
import time
import struct
from typing import Dict, Optional, List
from datetime import datetime


class ExportEngine:
    """Professional export engine with metadata and validation."""
    
    # Export format constants
    WAV_BIT_DEPTH = 24
    WAV_SAMPLE_RATE = 44100
    MP3_BITRATE = 320
    MP3_MODE = "CBR"  # Constant Bit Rate
    
    # Loudness targets (LUFS)
    LUFS_TARGET = -14.0
    LUFS_TOLERANCE = 2.0
    
    def __init__(self, live_bridge):
        """
        Initialize ExportEngine.
        
        Args:
            live_bridge: LiveBridge instance for Ableton communication
        """
        self.live_bridge = live_bridge
        self.export_history: List[Dict] = []
        self.total_exports = 0
        self.successful_exports = 0
        self.failed_exports = 0
        
    def export_wav(
        self, 
        output_path: str, 
        start_bar: float = 0, 
        end_bar: Optional[float] = None
    ) -> Dict:
        """
        Export project to WAV format.
        
        Args:
            output_path: Full path for output file
            start_bar: Start position in bars (default: 0)
            end_bar: End position in bars (default: song end)
            
        Returns:
            Dict with export status and metadata
        """
        export_start_time = time.time()
        
        try:
            # Ensure output directory exists
            output_dir = os.path.dirname(output_path)
            if output_dir and not os.path.exists(output_dir):
                os.makedirs(output_dir, exist_ok=True)
            
            # Get song length if end_bar not specified
            if end_bar is None:
                arrangement_info = self.live_bridge.get_arrangement_info()
                end_bar = arrangement_info.get("length_bars", 32)
            
            # Calculate export duration
            duration_bars = end_bar - start_bar
            
            # Export via LiveBridge
            result = self.live_bridge.export_audio(
                file_path=output_path,
                file_format="wav",
                bit_depth=self.WAV_BIT_DEPTH,
                sample_rate=self.WAV_SAMPLE_RATE,
                start_bar=start_bar,
                end_bar=end_bar
            )
            
            export_duration = time.time() - export_start_time
            
            if result.get("success", False):
                self.total_exports += 1
                self.successful_exports += 1
                
                # Create export record
                export_record = {
                    "timestamp": datetime.now().isoformat(),
                    "format": "wav",
                    "output_path": output_path,
                    "start_bar": start_bar,
                    "end_bar": end_bar,
                    "duration_bars": duration_bars,
                    "duration_seconds": export_duration,
                    "bit_depth": self.WAV_BIT_DEPTH,
                    "sample_rate": self.WAV_SAMPLE_RATE,
                    "status": "success"
                }
                self.export_history.append(export_record)
                
                return {
                    "success": True,
                    "message": f"Exported to WAV: {output_path}",
                    "format": "wav",
                    "path": output_path,
                    "settings": {
                        "bit_depth": self.WAV_BIT_DEPTH,
                        "sample_rate": self.WAV_SAMPLE_RATE
                    },
                    "duration_bars": duration_bars,
                    "export_time_seconds": round(export_duration, 2)
                }
            else:
                self.total_exports += 1
                self.failed_exports += 1
                
                return {
                    "success": False,
                    "message": result.get("error", "Export failed"),
                    "format": "wav",
                    "path": output_path,
                    "error": result.get("error", "Unknown error")
                }
                
        except Exception as e:
            self.total_exports += 1
            self.failed_exports += 1
            
            return {
                "success": False,
                "message": f"Export error: {str(e)}",
                "format": "wav",
                "path": output_path,
                "error": str(e)
            }
    
    def export_mp3(
        self, 
        output_path: str, 
        bitrate: int = 320
    ) -> Dict:
        """
        Export project to MP3 format.
        
        Args:
            output_path: Full path for output file
            bitrate: MP3 bitrate in kbps (default: 320)
            
        Returns:
            Dict with export status and metadata
        """
        export_start_time = time.time()
        
        try:
            # Validate bitrate
            if bitrate not in [128, 192, 256, 320]:
                bitrate = self.MP3_BITRATE
            
            # Ensure output directory exists
            output_dir = os.path.dirname(output_path)
            if output_dir and not os.path.exists(output_dir):
                os.makedirs(output_dir, exist_ok=True)
            
            # Get song length
            arrangement_info = self.live_bridge.get_arrangement_info()
            end_bar = arrangement_info.get("length_bars", 32)
            
            # Export via LiveBridge
            result = self.live_bridge.export_audio(
                file_path=output_path,
                file_format="mp3",
                bitrate=bitrate,
                sample_rate=self.WAV_SAMPLE_RATE,
                start_bar=0,
                end_bar=end_bar
            )
            
            export_duration = time.time() - export_start_time
            
            if result.get("success", False):
                self.total_exports += 1
                self.successful_exports += 1
                
                # Create export record
                export_record = {
                    "timestamp": datetime.now().isoformat(),
                    "format": "mp3",
                    "output_path": output_path,
                    "start_bar": 0,
                    "end_bar": end_bar,
                    "duration_bars": end_bar,
                    "duration_seconds": export_duration,
                    "bitrate": bitrate,
                    "sample_rate": self.WAV_SAMPLE_RATE,
                    "status": "success"
                }
                self.export_history.append(export_record)
                
                return {
                    "success": True,
                    "message": f"Exported to MP3: {output_path}",
                    "format": "mp3",
                    "path": output_path,
                    "settings": {
                        "bitrate": bitrate,
                        "mode": self.MP3_MODE,
                        "sample_rate": self.WAV_SAMPLE_RATE
                    },
                    "export_time_seconds": round(export_duration, 2)
                }
            else:
                self.total_exports += 1
                self.failed_exports += 1
                
                return {
                    "success": False,
                    "message": result.get("error", "Export failed"),
                    "format": "mp3",
                    "path": output_path,
                    "error": result.get("error", "Unknown error")
                }
                
        except Exception as e:
            self.total_exports += 1
            self.failed_exports += 1
            
            return {
                "success": False,
                "message": f"Export error: {str(e)}",
                "format": "mp3",
                "path": output_path,
                "error": str(e)
            }
    
    def validate_export(self, file_path: str) -> Dict:
        """
        Validate exported audio file.
        
        Checks:
        - File exists
        - Size > 0
        - Duration matches expected
        - Loudness (LUFS) within range
        
        Args:
            file_path: Path to exported file
            
        Returns:
            Dict with validation results
        """
        validation_result = {
            "file_path": file_path,
            "valid": False,
            "checks": {},
            "errors": []
        }
        
        try:
            # Check 1: File exists
            exists = os.path.exists(file_path)
            validation_result["checks"]["file_exists"] = exists
            if not exists:
                validation_result["errors"].append("File does not exist")
                return validation_result
            
            # Check 2: File size > 0
            file_size = os.path.getsize(file_path)
            validation_result["checks"]["file_size"] = file_size
            validation_result["checks"]["size_valid"] = file_size > 0
            if file_size == 0:
                validation_result["errors"].append("File is empty (0 bytes)")
                return validation_result
            
            # Check 3: Parse WAV header for duration (if WAV file)
            if file_path.lower().endswith(".wav"):
                duration_info = self._parse_wav_duration(file_path)
                validation_result["checks"]["duration_seconds"] = duration_info.get("duration")
                validation_result["checks"]["sample_rate"] = duration_info.get("sample_rate")
                validation_result["checks"]["channels"] = duration_info.get("channels")
                
                if duration_info.get("duration", 0) <= 0:
                    validation_result["errors"].append("Invalid audio duration")
            
            # Check 4: Estimate loudness (basic RMS approximation)
            loudness_info = self._estimate_loudness(file_path)
            validation_result["checks"]["loudness_lufs"] = loudness_info.get("lufs")
            validation_result["checks"]["loudness_valid"] = loudness_info.get("valid", False)
            
            if not loudness_info.get("valid", False):
                lufs = loudness_info.get("lufs", 0)
                if lufs < self.LUFS_TARGET - self.LUFS_TOLERANCE:
                    validation_result["errors"].append(f"Too quiet: {lufs:.1f} LUFS")
                elif lufs > self.LUFS_TARGET + self.LUFS_TOLERANCE:
                    validation_result["errors"].append(f"Too loud: {lufs:.1f} LUFS")
            
            # Final validation status
            validation_result["valid"] = len(validation_result["errors"]) == 0
            
        except Exception as e:
            validation_result["errors"].append(f"Validation error: {str(e)}")
            validation_result["valid"] = False
        
        return validation_result
    
    def _parse_wav_duration(self, file_path: str) -> Dict:
        """Parse WAV file header to get duration info."""
        try:
            with open(file_path, "rb") as f:
                # Read RIFF header
                riff = f.read(4)
                if riff != b"RIFF":
                    return {"duration": 0, "error": "Not a valid RIFF file"}
                
                # File size (skip)
                f.read(4)
                
                # WAVE marker
                wave = f.read(4)
                if wave != b"WAVE":
                    return {"duration": 0, "error": "Not a valid WAVE file"}
                
                # Find fmt chunk
                while True:
                    chunk_id = f.read(4)
                    if not chunk_id:
                        break
                    
                    chunk_size = struct.unpack("<I", f.read(4))[0]
                    
                    if chunk_id == b"fmt ":
                        # Audio format
                        audio_format = struct.unpack("<H", f.read(2))[0]
                        num_channels = struct.unpack("<H", f.read(2))[0]
                        sample_rate = struct.unpack("<I", f.read(4))[0]
                        # Skip byte rate and block align
                        f.read(6)
                        bits_per_sample = struct.unpack("<H", f.read(2))[0]
                        
                        # Skip any extra format bytes
                        if chunk_size > 16:
                            f.read(chunk_size - 16)
                        
                        # Now find data chunk
                        while True:
                            data_id = f.read(4)
                            if not data_id:
                                break
                            
                            data_size = struct.unpack("<I", f.read(4))[0]
                            
                            if data_id == b"data":
                                # Calculate duration
                                bytes_per_sample = bits_per_sample // 8
                                total_samples = data_size // (num_channels * bytes_per_sample)
                                duration = total_samples / sample_rate
                                
                                return {
                                    "duration": round(duration, 2),
                                    "sample_rate": sample_rate,
                                    "channels": num_channels,
                                    "bits_per_sample": bits_per_sample
                                }
                            else:
                                # Skip this chunk
                                f.seek(data_size, 1)
                        
                        break
                    else:
                        # Skip this chunk
                        f.seek(chunk_size, 1)
                
                return {"duration": 0, "error": "Could not find audio data chunk"}
                
        except Exception as e:
            return {"duration": 0, "error": str(e)}
    
    def _estimate_loudness(self, file_path: str) -> Dict:
        """
        Estimate loudness using basic RMS calculation.
        
        Note: This is a simplified approximation. For true LUFS,
        use a dedicated loudness analysis library.
        """
        try:
            if file_path.lower().endswith(".wav"):
                with open(file_path, "rb") as f:
                    # Read header to find data
                    f.read(12)  # Skip RIFF header
                    
                    while True:
                        chunk_id = f.read(4)
                        if not chunk_id:
                            break
                        
                        chunk_size = struct.unpack("<I", f.read(4))[0]
                        
                        if chunk_id == b"data":
                            # Read a sample of audio data (first 1MB for efficiency)
                            sample_size = min(chunk_size, 1024 * 1024)
                            data = f.read(sample_size)
                            
                            # Calculate RMS for 16-bit samples
                            if len(data) >= 2:
                                # Parse as 16-bit samples
                                samples = struct.unpack("<" + "h" * (len(data) // 2), data)
                                
                                # Calculate RMS
                                sum_squares = sum(s * s for s in samples if s is not None)
                                rms = (sum_squares / len(samples)) ** 0.5 if samples else 0
                                
                                # Convert to approximate LUFS
                                # Reference: 0 dBFS = -14 LUFS (typical streaming target)
                                max_value = 32768.0  # 16-bit max
                                dbfs = 20 * (rms / max_value)
                                approx_lufs = -14 + dbfs
                                
                                return {
                                    "lufs": round(approx_lufs, 1),
                                    "valid": self.LUFS_TARGET - self.LUFS_TOLERANCE <= approx_lufs <= self.LUFS_TARGET + self.LUFS_TOLERANCE
                                }
                        else:
                            f.seek(chunk_size, 1)
                
                return {"lufs": 0, "valid": False, "error": "Could not analyze audio data"}
            else:
                # For MP3, return unknown (would require decoding)
                return {"lufs": None, "valid": True, "note": "Loudness check skipped for MP3"}
                
        except Exception as e:
            return {"lufs": 0, "valid": False, "error": str(e)}
    
    def get_export_stats(self) -> Dict:
        """
        Get export statistics.
        
        Returns:
            Dict with export statistics
        """
        recent_exports = self.export_history[-10:] if self.export_history else []
        
        # Calculate format distribution
        formats = {}
        for record in self.export_history:
            fmt = record.get("format", "unknown")
            formats[fmt] = formats.get(fmt, 0) + 1
        
        # Calculate success rate
        success_rate = 0.0
        if self.total_exports > 0:
            success_rate = (self.successful_exports / self.total_exports) * 100
        
        return {
            "total_exports": self.total_exports,
            "successful_exports": self.successful_exports,
            "failed_exports": self.failed_exports,
            "success_rate_percent": round(success_rate, 1),
            "formats": formats,
            "recent_exports": recent_exports,
            "export_history_count": len(self.export_history)
        }
