# container_manager.py
"""
Manages Docker containers for STT and TTS services.
Handles startup, shutdown, and readiness detection.
"""

import asyncio
import subprocess
import aiohttp
from utils.logger import get_logger

logger = get_logger('container_manager')

class ContainerManager:
    """Manages STT and TTS Docker containers."""
    
    # Container names from docker-compose.yml
    STT_CONTAINER = "miku-stt"
    TTS_CONTAINER = "miku-rvc-api"
    
    # Health check endpoints
    STT_HEALTH_URL = "http://miku-stt:8767/health"  # HTTP health check endpoint
    TTS_HEALTH_URL = "http://miku-rvc-api:8765/health"
    
    # Startup timeouts (time to load models and become ready)
    STT_WARMUP_TIMEOUT = 30  # seconds (Whisper model loading)
    TTS_WARMUP_TIMEOUT = 60  # seconds (RVC takes longer)
    
    @classmethod
    async def start_voice_containers(cls) -> bool:
        """
        Start STT and TTS containers and wait for them to warm up.
        
        Returns:
            bool: True if both containers started and warmed up successfully
        """
        logger.info("🚀 Starting voice chat containers...")
        
        try:
            # Start STT container using docker start (assumes container exists)
            logger.info(f"Starting {cls.STT_CONTAINER}...")
            result = subprocess.run(
                ["docker", "start", cls.STT_CONTAINER],
                capture_output=True,
                text=True
            )
            
            if result.returncode != 0:
                logger.error(f"Failed to start {cls.STT_CONTAINER}: {result.stderr}")
                return False
            
            logger.info(f"✓ {cls.STT_CONTAINER} started")
            
            # Start TTS container
            logger.info(f"Starting {cls.TTS_CONTAINER}...")
            result = subprocess.run(
                ["docker", "start", cls.TTS_CONTAINER],
                capture_output=True,
                text=True
            )
            
            if result.returncode != 0:
                logger.error(f"Failed to start {cls.TTS_CONTAINER}: {result.stderr}")
                return False
            
            logger.info(f"✓ {cls.TTS_CONTAINER} started")
            
            # Wait for models to load and become ready
            logger.info("⏳ Waiting for models to load...")
            
            stt_ready = await cls._wait_for_stt_warmup()
            if not stt_ready:
                logger.error("STT failed to become ready")
                return False
            
            tts_ready = await cls._wait_for_tts_warmup()
            if not tts_ready:
                logger.error("TTS failed to become ready")
                return False
            
            logger.info("✅ All voice containers ready!")
            return True
            
        except Exception as e:
            logger.error(f"Error starting voice containers: {e}")
            return False
    
    @classmethod
    async def stop_voice_containers(cls) -> bool:
        """
        Stop STT and TTS containers.
        
        Returns:
            bool: True if containers stopped successfully
        """
        logger.info("🛑 Stopping voice chat containers...")
        
        try:
            # Stop both containers
            result = subprocess.run(
                ["docker", "stop", cls.STT_CONTAINER, cls.TTS_CONTAINER],
                capture_output=True,
                text=True
            )
            
            if result.returncode != 0:
                logger.error(f"Failed to stop containers: {result.stderr}")
                return False
            
            logger.info("✓ Voice containers stopped")
            return True
            
        except Exception as e:
            logger.error(f"Error stopping voice containers: {e}")
            return False
    
    @classmethod
    async def _wait_for_stt_warmup(cls) -> bool:
        """
        Wait for STT container to be ready by checking health endpoint.
        
        Returns:
            bool: True if STT is ready within timeout
        """
        start_time = asyncio.get_event_loop().time()
        
        async with aiohttp.ClientSession() as session:
            while (asyncio.get_event_loop().time() - start_time) < cls.STT_WARMUP_TIMEOUT:
                try:
                    async with session.get(cls.STT_HEALTH_URL, timeout=aiohttp.ClientTimeout(total=2)) as resp:
                        if resp.status == 200:
                            data = await resp.json()
                            # New STT server returns {"status": "ready"} when models are loaded
                            if data.get("status") == "ready":
                                logger.info("✓ STT is ready")
                                return True
                except Exception:
                    # Not ready yet, wait and retry
                    pass
                
                await asyncio.sleep(2)
        
        logger.error(f"STT warmup timeout ({cls.STT_WARMUP_TIMEOUT}s)")
        return False
    
    @classmethod
    async def _wait_for_tts_warmup(cls) -> bool:
        """
        Wait for TTS container to be ready by checking health endpoint.
        
        Returns:
            bool: True if TTS is ready within timeout
        """
        start_time = asyncio.get_event_loop().time()
        
        async with aiohttp.ClientSession() as session:
            while (asyncio.get_event_loop().time() - start_time) < cls.TTS_WARMUP_TIMEOUT:
                try:
                    async with session.get(cls.TTS_HEALTH_URL, timeout=aiohttp.ClientTimeout(total=2)) as resp:
                        if resp.status == 200:
                            data = await resp.json()
                            # RVC API returns "status": "healthy", not "ready"
                            status_ok = data.get("status") in ["ready", "healthy"]
                            if status_ok and data.get("warmed_up"):
                                logger.info("✓ TTS is ready")
                                return True
                except Exception:
                    # Not ready yet, wait and retry
                    pass
                
                await asyncio.sleep(2)
        
        logger.error(f"TTS warmup timeout ({cls.TTS_WARMUP_TIMEOUT}s)")
        return False
        return False
    
    @classmethod
    async def are_containers_running(cls) -> tuple[bool, bool]:
        """
        Check if STT and TTS containers are currently running.
        
        Returns:
            tuple[bool, bool]: (stt_running, tts_running)
        """
        try:
            # Check STT
            result = subprocess.run(
                ["docker", "inspect", "-f", "{{.State.Running}}", cls.STT_CONTAINER],
                capture_output=True,
                text=True
            )
            stt_running = result.returncode == 0 and result.stdout.strip() == "true"
            
            # Check TTS
            result = subprocess.run(
                ["docker", "inspect", "-f", "{{.State.Running}}", cls.TTS_CONTAINER],
                capture_output=True,
                text=True
            )
            tts_running = result.returncode == 0 and result.stdout.strip() == "true"
            
            return (stt_running, tts_running)
            
        except Exception as e:
            logger.error(f"Error checking container status: {e}")
            return (False, False)
