"""
Audio Perception - Eden's Ears
"""
import sounddevice as sd
import numpy as np
import json
from pathlib import Path
from datetime import datetime
from typing import Dict, List
import wave

class AudioSystem:
    def __init__(self, sample_rate=16000):
        self.sample_rate = sample_rate
        self.audio_log = Path('/Eden/DATA/audio_log.json')
        self.observations = []
        
    def list_devices(self):
        """List available audio devices"""
        devices = sd.query_devices()
        print("🎤 Available audio devices:")
        for i, device in enumerate(devices):
            print(f"   {i}: {device['name']}")
        return devices
    
    def record_audio(self, duration: float = 2.0) -> np.ndarray:
        """Record audio for specified duration"""
        try:
            print(f"🎤 Recording {duration}s...")
            audio = sd.rec(
                int(duration * self.sample_rate),
                samplerate=self.sample_rate,
                channels=1,
                dtype='float32'
            )
            sd.wait()
            return audio
        except Exception as e:
            print(f"❌ Recording error: {e}")
            return None
    
    def analyze_volume(self, audio: np.ndarray) -> Dict:
        """Analyze audio volume"""
        if audio is None:
            return {'status': 'no_audio'}
        
        rms = np.sqrt(np.mean(audio**2))
        peak = np.max(np.abs(audio))
        
        # Classify volume
        if rms < 0.01:
            volume = "silent"
        elif rms < 0.05:
            volume = "quiet"
        elif rms < 0.2:
            volume = "moderate"
        else:
            volume = "loud"
        
        return {
            'rms': float(rms),
            'peak': float(peak),
            'classification': volume
        }
    
    def detect_speech(self, audio: np.ndarray) -> bool:
        """Simple speech detection based on characteristics"""
        if audio is None:
            return False
        
        # Calculate zero-crossing rate (speech has higher ZCR)
        zcr = np.sum(np.abs(np.diff(np.sign(audio)))) / (2 * len(audio))
        
        # Calculate energy
        energy = np.sum(audio**2) / len(audio)
        
        # Simple heuristic: speech has moderate ZCR and energy
        return bool(zcr > 0.05 and energy > 0.001)  # Convert to Python bool
    
    def listen_to_world(self, duration: float = 2.0) -> Dict:
        """Listen and analyze environment"""
        audio = self.record_audio(duration)
        
        if audio is None:
            return {'status': 'no_audio'}
        
        observation = {
            'timestamp': datetime.now().isoformat(),
            'duration': duration,
            'volume': self.analyze_volume(audio),
            'speech_detected': bool(self.detect_speech(audio)),  # Ensure Python bool
            'sample_rate': self.sample_rate
        }
        
        self.observations.append(observation)
        self.log_observation(observation)
        
        return observation
    
    def log_observation(self, obs: Dict):
        """Log audio observation"""
        logs = []
        if self.audio_log.exists():
            with open(self.audio_log) as f:
                logs = json.load(f)
        
        logs.append(obs)
        logs = logs[-1000:]
        
        self.audio_log.parent.mkdir(exist_ok=True)
        with open(self.audio_log, 'w') as f:
            json.dump(logs, f, indent=2)
    
    def save_recording(self, audio: np.ndarray, name: str = None):
        """Save audio to file"""
        if audio is None:
            return None
        
        if name is None:
            name = f"recording_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav"
        
        path = Path(f'/Eden/DATA/recordings/{name}')
        path.parent.mkdir(exist_ok=True)
        
        # Convert to int16
        audio_int = (audio * 32767).astype(np.int16)
        
        with wave.open(str(path), 'w') as wf:
            wf.setnchannels(1)
            wf.setsampwidth(2)
            wf.setframerate(self.sample_rate)
            wf.writeframes(audio_int.tobytes())
        
        return str(path)

# Global instance
audio = AudioSystem()
