"""
STARWASTE - Sound Manager
Handles all audio for biome-specific footsteps, ambient loops, and UI sounds
"""
import arcade
import random
import os
from pathlib import Path
from typing import Optional, Dict
from shared.utils import console


class SoundManager:
    """Manages game audio - footsteps, ambient loops, UI sounds"""
    
    def __init__(self):
        self.enabled = True
        self.volume_master = 0.5
        self.volume_footsteps = 0.4
        self.volume_ambient = 0.3
        self.volume_ui = 0.5
        
        # Current biome
        self.current_biome = None
        
        # Sound cache - terrain-specific footsteps
        # Format: (biome, terrain_name) -> [arcade.Sound, ...]
        self.footstep_sounds: Dict[tuple, list] = {}
        self.ambient_sounds: Dict[str, list] = {}   # biome -> [arcade.Sound, ...]
        self.ui_sounds: Dict[str, arcade.Sound] = {}
        
        # Terrain mapping (terrain_id -> terrain_name for footsteps)
        self.terrain_names = {
            "frozen_world": {
                1: "tundra",  # Snow-covered ground
                2: "ice"      # Frozen ice
            },
            "toxic_wasteland": {
                1: "wasteland",   # Dry cracked earth
                2: "desertsand"   # Sandy dunes
            }
        }
        
        # Ambient loop player
        self.ambient_player: Optional[arcade.Sound] = None
        self.ambient_loop_playing = False
        
        # Geiger counter (radiation)
        self.geiger_sound: Optional[arcade.Sound] = None
        self.geiger_player: Optional[arcade.Sound] = None
        self.geiger_playing = False
        self.geiger_last_volume = 0.0  # Track volume to avoid restarts
        self.volume_geiger = 0.6  # Base volume for Geiger
        self.radiation_threshold = 40  # Start playing at 40% radiation

        # Harvest (tree/rock) one-shots
        self.harvest_sounds: Dict[str, arcade.Sound] = {}
        self.harvest_complete_sounds: Dict[str, arcade.Sound] = {}
        self.last_harvest_time = 0.0
        self.last_harvest_target_id: Optional[int] = None
        self._harvest_last_time_by_target: Dict[int, float] = {}
        # Throttle harvest SFX so it doesn't spam while holding mouse
        # ("half as often" vs previous default)
        self.harvest_cooldown = 0.70

        # Harvest completion (played once when resource breaks)
        self.last_harvest_complete_time = 0.0
        self.last_harvest_complete_target_id: Optional[int] = None
        self.harvest_complete_cooldown = 0.30
        self._harvest_complete_last_time_by_target: Dict[int, float] = {}
        
        # Footstep state
        self.last_footstep_time = 0.0
        self.footstep_cooldown = 0.35  # Seconds between footsteps
        self.footstep_index = {}  # (biome, terrain) -> index for round-robin
        
        # Load sounds
        self._load_all_sounds()
    
    def _load_all_sounds(self):
        """Load all available sounds from assets/biomes/*/sounds/"""
        biome_paths = {
            "frozen_world": Path("assets/biomes/frozen_world/sounds"),
            "toxic_wasteland": Path("assets/biomes/toxic_wasteland/sounds")
        }
        
        for biome_id, biome_path in biome_paths.items():
            # Load terrain-specific footsteps
            footstep_base = biome_path / "footsteps"
            if footstep_base.exists():
                # Load from terrain subfolders (tundra, ice, wasteland, desertsand, etc.)
                for terrain_dir in footstep_base.iterdir():
                    if terrain_dir.is_dir():
                        terrain_name = terrain_dir.name
                        footsteps = []
                        
                        for audio_file in terrain_dir.rglob("*"):
                            if audio_file.suffix.lower() in ['.wav', '.ogg', '.mp3']:
                                try:
                                    sound = arcade.load_sound(str(audio_file))
                                    footsteps.append(sound)
                                except Exception as e:
                                    console.print(f"[yellow]⚠ Failed to load {audio_file}: {e}[/yellow]")
                        
                        if footsteps:
                            key = (biome_id, terrain_name)
                            self.footstep_sounds[key] = footsteps
                            self.footstep_index[key] = 0
                            console.print(f"[green]✓ Loaded {len(footsteps)} {terrain_name} footstep sounds for {biome_id}[/green]")
            
            # Load ambient sounds
            ambient_dir = biome_path / "ambient"
            if ambient_dir.exists():
                ambient = []
                for audio_file in ambient_dir.rglob("*"):
                    if audio_file.suffix.lower() in ['.wav', '.ogg', '.mp3']:
                        try:
                            sound = arcade.load_sound(str(audio_file))
                            ambient.append(sound)
                        except Exception as e:
                            console.print(f"[yellow]⚠ Failed to load {audio_file}: {e}[/yellow]")
                
                if ambient:
                    self.ambient_sounds[biome_id] = ambient
                    console.print(f"[green]✓ Loaded {len(ambient)} ambient sounds for {biome_id}[/green]")
        
        # Load Geiger counter sound (toxic_wasteland specific)
        geiger_path = Path("assets/biomes/toxic_wasteland/sounds/effects/geiger_counter.mp3")
        if geiger_path.exists():
            try:
                self.geiger_sound = arcade.load_sound(str(geiger_path))
                console.print(f"[green]✓ Loaded Geiger counter sound[/green]")
            except Exception as e:
                console.print(f"[yellow]⚠ Failed to load Geiger sound: {e}[/yellow]")

        # Harvest SFX (tree/rock). Prefer project assets, fall back to Arcade built-ins.
        tree_path = Path("assets/biomes/toxic_wasteland/sounds/effects/harvest_tree.ogg")
        rock_path = Path("assets/biomes/toxic_wasteland/sounds/effects/harvest_rock.ogg")

        tree_complete_path = Path("assets/biomes/toxic_wasteland/sounds/effects/harvest_complete_tree.ogg")
        rock_complete_path = Path("assets/biomes/toxic_wasteland/sounds/effects/harvest_complete_rock.ogg")

        if tree_path.exists():
            try:
                self.harvest_sounds["tree"] = arcade.load_sound(str(tree_path))
            except Exception:
                pass
        if rock_path.exists():
            try:
                self.harvest_sounds["rock"] = arcade.load_sound(str(rock_path))
            except Exception:
                pass

        if tree_complete_path.exists():
            try:
                self.harvest_complete_sounds["tree"] = arcade.load_sound(str(tree_complete_path))
            except Exception:
                pass
        if rock_complete_path.exists():
            try:
                self.harvest_complete_sounds["rock"] = arcade.load_sound(str(rock_complete_path))
            except Exception:
                pass

        if "tree" not in self.harvest_sounds:
            try:
                self.harvest_sounds["tree"] = arcade.load_sound(":resources:sounds/hit5.wav")
            except Exception:
                pass
        if "rock" not in self.harvest_sounds:
            try:
                self.harvest_sounds["rock"] = arcade.load_sound(":resources:sounds/hit2.wav")
            except Exception:
                pass

        # Completion fallbacks: reuse the base harvest sounds if dedicated ones are missing.
        if "tree" not in self.harvest_complete_sounds and "tree" in self.harvest_sounds:
            self.harvest_complete_sounds["tree"] = self.harvest_sounds["tree"]
        if "rock" not in self.harvest_complete_sounds and "rock" in self.harvest_sounds:
            self.harvest_complete_sounds["rock"] = self.harvest_sounds["rock"]

    def play_harvest(self, *, current_time: float, resource_type: str, target_id: Optional[int] = None, volume_scale: float = 1.0) -> bool:
        """Play a short harvest SFX for chopping/mining (throttled).

        Args:
            current_time: Time base (same as used for footsteps)
            resource_type: server resource type (e.g. 'tree', 'rock', 'iron_ore')
            target_id: optional entity id to allow immediate sound on target change
        """
        if not self.enabled:
            return False

        # Map server resource types to our two sound groups
        group = "tree" if resource_type == "tree" else "rock"
        sound = self.harvest_sounds.get(group)
        if not sound:
            return False

        # Per-target throttle (better for multiplayer + multiple nearby sources)
        if target_id is not None:
            last_t = float(self._harvest_last_time_by_target.get(int(target_id), 0.0) or 0.0)
            if current_time - last_t < self.harvest_cooldown:
                return False
            self._harvest_last_time_by_target[int(target_id)] = current_time

            # Keep legacy fields roughly in sync
            self.last_harvest_target_id = int(target_id)
            self.last_harvest_time = current_time
        else:
            if current_time - self.last_harvest_time < self.harvest_cooldown:
                return False
            self.last_harvest_time = current_time

        volume_scale = max(0.0, min(1.0, float(volume_scale or 0.0)))
        volume = self.volume_master * self.volume_ui * volume_scale
        if volume <= 0.0:
            return False
        arcade.play_sound(sound, volume=volume)
        return True

    def play_harvest_complete(self, *, current_time: float, resource_type: str, target_id: Optional[int] = None, volume_scale: float = 1.0) -> bool:
        """Play a harvest completion SFX (played once when a node is destroyed)."""
        if not self.enabled:
            return False

        group = "tree" if resource_type == "tree" else "rock"
        sound = self.harvest_complete_sounds.get(group)
        if not sound:
            return False

        # Avoid double-firing if we receive multiple updates on the same completion.
        if target_id is not None:
            tid = int(target_id)
            last_t = float(self._harvest_complete_last_time_by_target.get(tid, 0.0) or 0.0)
            if current_time - last_t < self.harvest_complete_cooldown:
                return False
            self._harvest_complete_last_time_by_target[tid] = current_time
            self.last_harvest_complete_target_id = tid
            self.last_harvest_complete_time = current_time
        else:
            if current_time - self.last_harvest_complete_time < self.harvest_complete_cooldown:
                return False
            self.last_harvest_complete_time = current_time

        volume_scale = max(0.0, min(1.0, float(volume_scale or 0.0)))
        volume = self.volume_master * self.volume_ui * volume_scale
        if volume <= 0.0:
            return False
        arcade.play_sound(sound, volume=volume)
        return True
    
    def set_biome(self, biome_id: str):
        """Switch to a different biome (restarts ambient loop)"""
        if biome_id == self.current_biome:
            return
        
        self.current_biome = biome_id
        self.stop_ambient()
        self.play_ambient_loop()
    
    def play_footstep(self, current_time: float, is_moving: bool, terrain_id: Optional[int] = None):
        """Play footstep sound if player is moving
        
        Args:
            current_time: Current game time
            is_moving: Whether player is moving
            terrain_id: Terrain type ID under player (1=ground, 2=upper layer)
        """
        if not self.enabled or not is_moving or not terrain_id:
            return
        
        # Check cooldown
        if current_time - self.last_footstep_time < self.footstep_cooldown:
            return
        
        # Get terrain name from terrain_id
        if self.current_biome not in self.terrain_names:
            return
        
        terrain_name = self.terrain_names[self.current_biome].get(terrain_id)
        if not terrain_name:
            return
        
        # Get footstep sounds for current biome + terrain
        key = (self.current_biome, terrain_name)
        if key not in self.footstep_sounds:
            return
        
        sounds = self.footstep_sounds[key]
        if not sounds:
            return
        
        # Round-robin through sounds (more natural than random)
        idx = self.footstep_index.get(key, 0)
        sound = sounds[idx % len(sounds)]
        self.footstep_index[key] = idx + 1
        
        # Play with volume
        volume = self.volume_master * self.volume_footsteps
        arcade.play_sound(sound, volume=volume)
        
        self.last_footstep_time = current_time
    
    def play_ambient_loop(self):
        """Start ambient loop for current biome"""
        if not self.enabled or self.ambient_loop_playing:
            return
        
        if self.current_biome not in self.ambient_sounds:
            return
        
        sounds = self.ambient_sounds[self.current_biome]
        if not sounds:
            return
        
        # Pick random ambient sound
        sound = random.choice(sounds)
        volume = self.volume_master * self.volume_ambient
        
        # Play looping
        self.ambient_player = arcade.play_sound(sound, volume=volume, loop=True)
        self.ambient_loop_playing = True
    
    def stop_ambient(self):
        """Stop ambient loop"""
        if self.ambient_player:
            arcade.stop_sound(self.ambient_player)
            self.ambient_player = None
        self.ambient_loop_playing = False
    
    def update_geiger_counter(self, radiation_level: int):
        """Update Geiger counter based on radiation level (0-100)
        
        Args:
            radiation_level: Current radiation exposure (0-100)
        """
        if not self.enabled or not self.geiger_sound:
            return
        
        # Stop if radiation too low (below 40%)
        if radiation_level < self.radiation_threshold:
            if self.geiger_playing:
                self.stop_geiger_counter()
            return
        
        # Calculate volume: 40% radiation = 0% volume, 100% radiation = 100% volume
        volume_scale = (radiation_level - self.radiation_threshold) / (100 - self.radiation_threshold)
        volume = self.volume_master * self.volume_geiger * volume_scale
        
        # Start playing if not already
        if not self.geiger_playing:
            self.geiger_player = arcade.play_sound(self.geiger_sound, volume=volume, loop=True)
            self.geiger_playing = True
            self.geiger_last_volume = volume
        else:
            # Only restart if volume changed significantly (>10%)
            if abs(volume - self.geiger_last_volume) > 0.03:
                try:
                    if self.geiger_player:
                        arcade.stop_sound(self.geiger_player)
                except:
                    pass  # Ignore stop errors
                self.geiger_player = arcade.play_sound(self.geiger_sound, volume=volume, loop=True)
                self.geiger_last_volume = volume
    
    def stop_geiger_counter(self):
        """Stop Geiger counter sound"""
        if self.geiger_player:
            try:
                arcade.stop_sound(self.geiger_player)
            except:
                pass  # Ignore errors
            self.geiger_player = None
        self.geiger_playing = False
        self.geiger_last_volume = 0.0
    
    def set_master_volume(self, volume: float):
        """Set master volume (0.0 to 1.0)"""
        self.volume_master = max(0.0, min(1.0, volume))
        
        # Update ambient loop if playing
        if self.ambient_player:
            self.stop_ambient()
            self.play_ambient_loop()
    
    def toggle_enabled(self):
        """Toggle sound on/off"""
        self.enabled = not self.enabled
        
        if not self.enabled:
            self.stop_ambient()
        else:
            self.play_ambient_loop()
        
        return self.enabled
