"""Service manager for coordinating local services."""

import asyncio
import logging
from typing import Dict, Optional, List, Any
from mcpbundles.services.browser import BrowserService
from mcpbundles.services.sqlite import SQLiteService
from mcpbundles.services.claude_code import ClaudeCodeService

logger = logging.getLogger(__name__)


class ServiceManager:
    """Manages lifecycle of all local services."""
    
    def __init__(self) -> None:
        self.services: Dict[str, Any] = {}
        self.browser: Optional[BrowserService] = None
        self.sqlite: Optional[SQLiteService] = None
        self.claude_code: Optional[ClaudeCodeService] = None
        
    async def discover_services(self, ports: List[int]) -> dict:
        """Scan which ports are listening on localhost.
        
        Args:
            ports: List of ports to scan
            
        Returns:
            Dict with port availability status
        """
        logger.info(f"Discovering services on ports: {ports}")
        results = {}
        
        for port in ports:
            try:
                # Try to connect to localhost:port with 2 second timeout
                reader, writer = await asyncio.wait_for(
                    asyncio.open_connection('localhost', port),
                    timeout=2.0
                )
                try:
                    writer.close()
                    await writer.wait_closed()
                except Exception:
                    pass  # Ignore close errors during discovery
                
                results[str(port)] = {
                    "available": True
                }
                logger.debug(f"Port {port}: available")
                
            except (asyncio.TimeoutError, ConnectionRefusedError, OSError):
                results[str(port)] = {
                    "available": False
                }
                logger.debug(f"Port {port}: not available")
            except Exception as e:
                logger.error(f"Error scanning port {port}: {e}")
                results[str(port)] = {
                    "available": False
                }
        
        logger.info(f"Discovery complete: {sum(1 for r in results.values() if r['available'])} services found")
        return results
    
    async def verify_service(self, target: str) -> dict:
        """Verify that a service is actually reachable.
        
        Args:
            target: Service target like "localhost:5432"
            
        Returns:
            Dict with verification result
        """
        logger.info(f"Verifying service: {target}")
        
        try:
            # Parse target
            if ':' not in target:
                return {
                    "verified": False,
                    "error": "Invalid target format. Expected 'host:port'"
                }
            
            host, port_str = target.split(':', 1)
            port = int(port_str)
            
            # Try to connect
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=5.0
            )
            
            # Connection successful
            try:
                writer.close()
                await writer.wait_closed()
            except Exception:
                pass  # Ignore close errors during verification
            
            logger.info(f"✅ Service verified: {target}")
            return {
                "verified": True,
                "target": target
            }
            
        except asyncio.TimeoutError:
            logger.warning(f"Service verification timeout: {target}")
            return {
                "verified": False,
                "target": target,
                "error": "Connection timeout"
            }
        except ConnectionRefusedError:
            logger.warning(f"Service connection refused: {target}")
            return {
                "verified": False,
                "target": target,
                "error": "Connection refused"
            }
        except ValueError as e:
            logger.error(f"Invalid port in target {target}: {e}")
            return {
                "verified": False,
                "target": target,
                "error": f"Invalid port: {e}"
            }
        except Exception as e:
            logger.error(f"Error verifying service {target}: {e}")
            return {
                "verified": False,
                "target": target,
                "error": str(e)
            }
    
    async def start_service(self, service: str, config: dict) -> dict:
        """Start a local service.
        
        Args:
            service: Service name (e.g., "browser")
            config: Service configuration
            
        Returns:
            Dict with start result
        """
        logger.info(f"Starting service: {service} with config: {config}")
        
        if service == "browser":
            return await self._start_browser(config)
        elif service == "sqlite":
            return await self._start_sqlite(config)
        elif service == "claude_code":
            return await self._start_claude_code(config)
        else:
            logger.warning(f"Unknown service type: {service}")
            return {
                "started": False,
                "error": f"Unknown service: {service}"
            }
    
    async def _start_browser(self, config: dict) -> dict:
        """Start browser service."""
        try:
            headless = config.get('headless', False)
            port = config.get('port', 9223)
            
            if self.browser and self.browser.running:
                logger.info("Browser already running")
                return {
                    "started": True,
                    "service": "browser",
                    "already_running": True,
                    "port": self.browser.port,
                    "mode": "hidden" if self.browser.headless else "visible"
                }
            
            self.browser = BrowserService(headless=headless, port=port)
            await self.browser.start()
            self.services['browser'] = self.browser
            
            return {
                "started": True,
                "service": "browser",
                "port": port,
                "mode": "hidden" if headless else "visible"
            }
            
        except ImportError:
            logger.error("Playwright not installed")
            return {
                "started": False,
                "service": "browser",
                "error": "Playwright not installed. Install with: pip install mcpbundles[browser]"
            }
        except Exception as e:
            logger.error(f"Failed to start browser: {e}")
            return {
                "started": False,
                "service": "browser",
                "error": str(e)
            }
    
    async def _start_sqlite(self, config: dict) -> dict:
        """Start SQLite service."""
        try:
            port = config.get('port', 9999)
            
            if self.sqlite and self.sqlite.running:
                logger.info("SQLite service already running")
                return {
                    "started": True,
                    "service": "sqlite",
                    "already_running": True,
                    "port": self.sqlite.port,
                }
            
            self.sqlite = SQLiteService(port=port)
            await self.sqlite.start()
            self.services['sqlite'] = self.sqlite
            
            return {
                "started": True,
                "service": "sqlite",
                "port": port,
            }
            
        except Exception as e:
            logger.error(f"Failed to start SQLite service: {e}")
            return {
                "started": False,
                "service": "sqlite",
                "error": str(e)
            }
    
    async def stop_service(self, service: str) -> dict:
        """Stop a local service.
        
        Args:
            service: Service name (e.g., "browser")
            
        Returns:
            Dict with stop result
        """
        logger.info(f"Stopping service: {service}")
        
        if service == "browser":
            return await self._stop_browser()
        elif service == "sqlite":
            return await self._stop_sqlite()
        elif service == "claude_code":
            return await self._stop_claude_code()
        else:
            logger.warning(f"Unknown service type: {service}")
            return {
                "stopped": False,
                "error": f"Unknown service: {service}"
            }
    
    async def _stop_browser(self) -> dict:
        """Stop browser service."""
        try:
            if not self.browser or not self.browser.running:
                logger.info("Browser not running")
                return {
                    "stopped": True,
                    "was_running": False
                }
            
            await self.browser.stop()
            self.browser = None
            self.services.pop('browser', None)
            
            return {
                "stopped": True,
                "service": "browser"
            }
            
        except Exception as e:
            logger.error(f"Error stopping browser: {e}")
            return {
                "stopped": False,
                "error": str(e)
            }
    
    async def _stop_sqlite(self) -> dict:
        """Stop SQLite service."""
        try:
            if not self.sqlite or not self.sqlite.running:
                logger.info("SQLite service not running")
                return {
                    "stopped": True,
                    "was_running": False
                }
            
            await self.sqlite.stop()
            self.sqlite = None
            self.services.pop('sqlite', None)
            
            return {
                "stopped": True,
                "service": "sqlite"
            }
            
        except Exception as e:
            logger.error(f"Error stopping SQLite service: {e}")
            return {
                "stopped": False,
                "error": str(e)
            }
    
    async def _start_claude_code(self, config: dict) -> dict:
        """Start Claude Code service."""
        try:
            port = config.get('port', 9300)
            
            if self.claude_code and self.claude_code.running:
                logger.info("Claude Code service already running")
                return {
                    "started": True,
                    "service": "claude_code",
                    "already_running": True,
                    "port": self.claude_code.port,
                }
            
            self.claude_code = ClaudeCodeService(port=port)
            await self.claude_code.start()
            self.services['claude_code'] = self.claude_code
            
            return {
                "started": True,
                "service": "claude_code",
                "port": port,
            }
            
        except Exception as e:
            logger.error(f"Failed to start Claude Code service: {e}")
            return {
                "started": False,
                "service": "claude_code",
                "error": str(e)
            }
    
    async def _stop_claude_code(self) -> dict:
        """Stop Claude Code service."""
        try:
            if not self.claude_code or not self.claude_code.running:
                logger.info("Claude Code service not running")
                return {
                    "stopped": True,
                    "was_running": False
                }
            
            await self.claude_code.stop()
            self.claude_code = None
            self.services.pop('claude_code', None)
            
            return {
                "stopped": True,
                "service": "claude_code"
            }
            
        except Exception as e:
            logger.error(f"Error stopping Claude Code service: {e}")
            return {
                "stopped": False,
                "error": str(e)
            }
    
    async def _update_claude_code(self, config: dict) -> dict:
        """Update Claude Code configuration."""
        try:
            if not self.claude_code or not self.claude_code.running:
                logger.warning("Claude Code not running, starting with new config")
                return await self._start_claude_code(config)
            
            # Claude Code service doesn't have runtime config changes
            # Just return success
            return {
                "updated": True,
                "service": "claude_code",
            }
            
        except Exception as e:
            logger.error(f"Error updating Claude Code: {e}")
            return {
                "updated": False,
                "error": str(e)
            }
    
    async def update_service(self, service: str, config: dict) -> dict:
        """Update service configuration.
        
        Args:
            service: Service name (e.g., "browser")
            config: New configuration
            
        Returns:
            Dict with update result
        """
        logger.info(f"Updating service: {service} with config: {config}")
        
        if service == "browser":
            return await self._update_browser(config)
        elif service == "claude_code":
            return await self._update_claude_code(config)
        else:
            logger.warning(f"Unknown service type: {service}")
            return {
                "updated": False,
                "error": f"Unknown service: {service}"
            }
    
    async def _update_browser(self, config: dict) -> dict:
        """Update browser configuration."""
        try:
            if not self.browser or not self.browser.running:
                logger.warning("Browser not running, starting with new config")
                return await self._start_browser(config)
            
            headless = config.get('headless', self.browser.headless)
            
            # Update configuration
            await self.browser.update_config(headless)
            
            return {
                "updated": True,
                "service": "browser",
                "mode": "hidden" if headless else "visible"
            }
            
        except Exception as e:
            logger.error(f"Error updating browser: {e}")
            return {
                "updated": False,
                "error": str(e)
            }
    
    async def initialize(self, config: dict):
        """Initialize services based on config (legacy support)."""
        logger.info("Initializing service manager")
        
        # Initialize browser service if enabled (for backwards compatibility)
        browser_config = config.get('browser', {})
        if browser_config.get('enabled', False):
            await self._start_browser(browser_config)
    
    async def update_config(self, config: dict):
        """Update service configuration from backend (legacy support)."""
        logger.info(f"Updating service config: {config}")
        
        # Handle browser service (backwards compatibility)
        if 'browser' in config:
            browser_config = config['browser']
            enabled = browser_config.get('enabled', False)
            
            if enabled:
                await self.start_service('browser', browser_config)
            else:
                await self.stop_service('browser')
    
    def get_status(self) -> dict:
        """Get status of all services."""
        status = {}
        
        if self.browser:
            status['browser'] = self.browser.get_status()
        else:
            status['browser'] = {
                "enabled": False,
                "headless": False,
                "port": 9223,
                "mode": "disabled"
            }
        
        if self.sqlite:
            status['sqlite'] = self.sqlite.get_status()
        else:
            status['sqlite'] = {
                "enabled": False,
                "port": 9999,
            }
        
        if self.claude_code:
            status['claude_code'] = self.claude_code.get_status()
        else:
            status['claude_code'] = {
                "enabled": False,
                "port": 9300,
            }
        
        return status
    
    async def stop_all(self):
        """Stop all running services."""
        logger.info("Stopping all services")
        
        if self.browser:
            await self.browser.stop()
            self.browser = None
        
        if self.sqlite:
            await self.sqlite.stop()
            self.sqlite = None
        
        if self.claude_code:
            await self.claude_code.stop()
            self.claude_code = None
        
        self.services.clear()

