"""
Smart Defaults System for Zero-Config Agents

This module generates optimal agent configurations based on:
- Behavior type (coding, research, helpful, etc.)
- Environment capabilities (models, memory, tools)
- Resource constraints and optimization
"""

import os
import logging
from typing import Dict, List, Optional, Any, Union
from dataclasses import dataclass

from .detection import EnvironmentCapabilities, EnvironmentDetector

logger = logging.getLogger(__name__)

@dataclass
class BehaviorProfile:
    """Configuration profile for a specific behavior"""
    name: str
    description: str
    preferred_models: List[str]
    max_tokens: int
    temperature: float
    tools: List[str]
    memory_need: str  # "low", "medium", "high"
    streaming: bool
    response_style: str
    capabilities: List[str]

class SmartDefaults:
    """Generate optimal configuration based on behavior and environment"""
    
    def __init__(self, capabilities: EnvironmentCapabilities):
        self.capabilities = capabilities
        self.behavior_profiles = self._load_behavior_profiles()
        self.tool_registry = self._load_tool_registry()
    
    def generate_agent_config(self, agent_id: str, behavior: str = "helpful", **overrides):
        """Generate complete agent configuration from minimal input"""
        logger.info(f"🎯 Generating config for agent '{agent_id}' with behavior '{behavior}'")
        
        # Get behavior profile
        profile = self.behavior_profiles.get(behavior, self.behavior_profiles["helpful"])
        
        # Select optimal model for behavior
        model = self._select_behavior_model(profile)
        
        # Auto-select tools based on behavior and availability
        tools = self._select_behavior_tools(profile)
        
        # Configure memory based on behavior and resources
        memory_config = self._configure_memory(profile)
        
        # Generate agent name if not provided
        agent_name = self._generate_agent_name(agent_id, behavior)
        
        # Determine streaming based on behavior and capabilities
        streaming = self._should_enable_streaming(profile)
        
        # Late import to avoid circular dependency
        from .config import AgentConfig
        
        # Create base configuration
        config = AgentConfig(
            id=agent_id,
            name=agent_name,
            model=model,
            behavior=behavior,
            tools=tools,
            memory=memory_config,
            streaming=streaming,
            max_tokens=profile.max_tokens,
            temperature=profile.temperature,
            system_prompt=None  # Generated by behavior system
        )
        
        # Apply any explicit overrides
        for key, value in overrides.items():
            if hasattr(config, key) and value is not None:
                setattr(config, key, value)
        
        logger.info(f"✅ Generated config: model={model}, tools={len(tools)}, memory={memory_config}")
        
        return config
    
    def _load_behavior_profiles(self) -> Dict[str, BehaviorProfile]:
        """Load predefined behavior profiles with optimal settings"""
        profiles = {
            "helpful": BehaviorProfile(
                name="Helpful Assistant",
                description="General-purpose helpful assistant for everyday tasks",
                preferred_models=["gpt-4o-mini", "claude-3-haiku-20240307", "gpt-4o"],
                max_tokens=4000,
                temperature=0.7,
                tools=["filesystem"],
                memory_need="medium",
                streaming=True,
                response_style="friendly",
                capabilities=["files"]
            ),
            
            "coding": BehaviorProfile(
                name="Coding Assistant",
                description="Programming assistant for code development and debugging",
                preferred_models=["gpt-4o", "claude-3-5-sonnet-20241022", "gpt-4o-mini"],
                max_tokens=8000,
                temperature=0.1,
                tools=["filesystem", "github", "codebase_indexer"],
                memory_need="high",
                streaming=True,
                response_style="precise",
                capabilities=["files", "code", "analysis"]
            ),
            
            "research": BehaviorProfile(
                name="Research Assistant",
                description="Research assistant for information gathering and analysis",
                preferred_models=["gpt-4o", "claude-3-5-sonnet-20241022", "gemini-pro"],
                max_tokens=6000,
                temperature=0.3,
                tools=["filesystem", "github", "aggregation", "consensus"],
                memory_need="high",
                streaming=True,
                response_style="analytical",
                capabilities=["files", "web", "memory", "analysis"]
            ),
            
            "creative": BehaviorProfile(
                name="Creative Assistant",
                description="Creative assistant for writing, brainstorming, and content generation",
                preferred_models=["gpt-4o", "claude-3-5-sonnet-20241022", "gpt-4o-mini"],
                max_tokens=4000,
                temperature=0.8,
                tools=["filesystem", "files"],
                memory_need="medium",
                streaming=True,
                response_style="creative",
                capabilities=["files"]
            ),
            
            "analytical": BehaviorProfile(
                name="Analytical Assistant",
                description="Data analysis and logical reasoning assistant",
                preferred_models=["gpt-4o", "claude-3-5-sonnet-20241022", "gemini-pro"],
                max_tokens=6000,
                temperature=0.2,
                tools=["filesystem", "aggregation", "multi_agent_reranking"],
                memory_need="high",
                streaming=False,  # Analysis often benefits from complete responses
                response_style="methodical",
                capabilities=["files", "analysis", "memory"]
            ),
            
            "support": BehaviorProfile(
                name="Support Assistant",
                description="Customer support assistant for help and troubleshooting",
                preferred_models=["gpt-4o-mini", "claude-3-haiku-20240307", "gpt-3.5-turbo"],
                max_tokens=3000,
                temperature=0.5,
                tools=["filesystem", "files"],
                memory_need="medium",
                streaming=True,
                response_style="patient",
                capabilities=["files"]
            )
        }
        
        # Load custom profiles from environment or config
        custom_profiles = self._load_custom_profiles()
        profiles.update(custom_profiles)
        
        return profiles
    
    def _load_custom_profiles(self) -> Dict[str, BehaviorProfile]:
        """Load custom behavior profiles from configuration"""
        # This could be extended to load from files or environment variables
        custom_profiles = {}
        
        # Example: Load from environment variable
        custom_behavior = os.getenv("LANGSWARM_CUSTOM_BEHAVIOR")
        if custom_behavior:
            # Parse custom behavior definition
            # For now, just return empty dict
            pass
        
        return custom_profiles
    
    def _load_tool_registry(self) -> Dict[str, Dict[str, Any]]:
        """Load registry of available tools and their capabilities"""
        return {
            "filesystem": {
                "type": "mcpfilesystem",
                "capabilities": ["files", "directories"],
                "resource_usage": "low",
                "always_available": True
            },
            "files": {
                "type": "files",
                "capabilities": ["files"],
                "resource_usage": "low",
                "always_available": True
            },
            "github": {
                "type": "mcpgithubtool",
                "capabilities": ["web", "code", "repositories"],
                "resource_usage": "medium",
                "requires_api": True
            },
            "codebase_indexer": {
                "type": "codebase_indexer",
                "capabilities": ["code", "analysis"],
                "resource_usage": "high",
                "requires_memory": True
            },
            "aggregation": {
                "type": "aggregation",
                "capabilities": ["analysis", "web"],
                "resource_usage": "medium",
                "requires_api": True
            },
            "consensus": {
                "type": "consensus",
                "capabilities": ["analysis"],
                "resource_usage": "high",
                "requires_memory": True
            },
            "multi_agent_reranking": {
                "type": "multi_agent_reranking",
                "capabilities": ["analysis"],
                "resource_usage": "high",
                "requires_api": True
            },
            "dynamic_forms": {
                "type": "dynamic_forms",
                "capabilities": ["forms", "ui"],
                "resource_usage": "low",
                "always_available": True
            },
            "message_queue_publisher": {
                "type": "message_queue_publisher",
                "capabilities": ["messaging", "async"],
                "resource_usage": "low",
                "requires_api": False
            },
            "sql_database": {
                "type": "mcpsql_database",
                "capabilities": ["database", "sql", "analytics", "data_retrieval"],
                "resource_usage": "low",
                "always_available": True,
                "description": "Secure SQL database access with natural language query building and validation"
            },
            "daytona_environment": {
                "type": "daytona_environment",
                "capabilities": ["environments", "code_execution", "development"],
                "resource_usage": "low",
                "requires_api": True,
                "description": "Secure cloud-based development environments with Daytona"
            },
            "daytona_self_hosted": {
                "type": "daytona_self_hosted",
                "capabilities": ["environments", "code_execution", "development", "on_premises"],
                "resource_usage": "medium",
                "requires_cli": True,
                "description": "Self-hosted Daytona development environments"
            }
        }
    
    def _select_behavior_model(self, profile: BehaviorProfile) -> str:
        """Choose optimal model for specific behavior"""
        available_models = self.capabilities.available_models
        
        # Try preferred models for this behavior in order
        for model in profile.preferred_models:
            if model in available_models:
                logger.info(f"🎯 Selected preferred model for {profile.name}: {model}")
                return model
        
        # Fallback to best available model
        if self.capabilities.preferred_model in available_models:
            logger.info(f"🎯 Using fallback model: {self.capabilities.preferred_model}")
            return self.capabilities.preferred_model
        
        # Last resort fallback
        if available_models:
            model = available_models[0]
            logger.warning(f"⚠️  Using first available model: {model}")
            return model
        
        # Ultimate fallback
        logger.error("❌ No models available! Using fallback.")
        return "gpt-4o-mini"
    
    def _select_behavior_tools(self, profile: BehaviorProfile) -> List[str]:
        """Auto-select tools based on behavior and availability"""
        requested_tools = profile.tools.copy()
        available_tools = self._get_available_tools()
        
        # Filter tools based on availability and constraints
        selected_tools = []
        
        for tool in requested_tools:
            if tool not in self.tool_registry:
                logger.warning(f"⚠️  Unknown tool: {tool}")
                continue
            
            tool_info = self.tool_registry[tool]
            
            # Check if tool is available
            if tool not in available_tools and not tool_info.get("always_available", False):
                logger.info(f"🚫 Tool {tool} not available, skipping")
                continue
            
            # Check resource constraints
            if not self._check_tool_constraints(tool, tool_info):
                logger.info(f"🚫 Tool {tool} doesn't meet resource constraints, skipping")
                continue
            
            selected_tools.append(tool)
        
        # Ensure at least filesystem is available
        if not selected_tools and "filesystem" in available_tools:
            selected_tools = ["filesystem"]
            logger.info("📁 Added filesystem as fallback tool")
        
        logger.info(f"🔧 Selected tools: {selected_tools}")
        
        return selected_tools
    
    def _check_tool_constraints(self, tool_id: str, tool_info: Dict[str, Any]) -> bool:
        """Check if tool meets resource and capability constraints"""
        # Check memory constraints
        if tool_info.get("requires_memory") and self.capabilities.available_memory_mb < 1000:
            return False
        
        # Check API constraints
        if tool_info.get("requires_api") and not self.capabilities.internet_access:
            return False
        
        # Check resource usage vs available resources
        resource_usage = tool_info.get("resource_usage", "low")
        if resource_usage == "high" and self.capabilities.available_memory_mb < 2000:
            return False
        
        # Environment-specific constraints
        if self.capabilities.environment_type == "production":
            # In production, prefer lower resource usage
            if resource_usage == "high" and self.capabilities.cpu_cores < 4:
                return False
        
        return True
    
    def _get_available_tools(self) -> List[str]:
        """Get list of actually available tools in the system"""
        # This would normally check the LangSwarm tool registry
        # For now, return a reasonable set based on common availability
        
        base_tools = ["filesystem", "files"]
        
        # Add tools based on capabilities
        if self.capabilities.internet_access:
            base_tools.extend(["github", "aggregation"])
        
        if self.capabilities.available_memory_mb > 2000:
            base_tools.extend(["codebase_indexer", "consensus"])
        
        if self.capabilities.available_memory_mb > 4000:
            base_tools.append("multi_agent_reranking")
        
        # Always available tools
        base_tools.extend(["dynamic_forms"])
        
        # Environment-specific tools
        if self.capabilities.environment_type == "development":
            # More tools available in development
            pass
        
        return list(set(base_tools))  # Remove duplicates
    
    def _configure_memory(self, profile: BehaviorProfile) -> Union[bool, Dict[str, Any]]:
        """Configure memory based on behavior and system resources"""
        memory_need = profile.memory_need
        available_memory = self.capabilities.available_memory_mb
        environment = self.capabilities.environment_type
        
        # Memory need thresholds
        memory_configs = {
            "high": {
                "min_memory": 4000,  # 4GB
                "optimal_memory": 8000,  # 8GB
                "settings": {"max_memory_size": "500MB", "persistence": True}
            },
            "medium": {
                "min_memory": 2000,  # 2GB
                "optimal_memory": 4000,  # 4GB
                "settings": {"max_memory_size": "100MB", "persistence": False}
            },
            "low": {
                "min_memory": 1000,  # 1GB
                "optimal_memory": 2000,  # 2GB
                "settings": {"max_memory_size": "50MB", "persistence": False}
            }
        }
        
        config = memory_configs.get(memory_need, memory_configs["medium"])
        
        # Check if we can enable memory
        if available_memory < config["min_memory"]:
            logger.info(f"🧠 Insufficient memory for {memory_need} memory need, disabling")
            return False
        
        # Configure based on available resources
        if available_memory >= config["optimal_memory"]:
            # Full memory configuration
            memory_config = {
                "enabled": True,
                "backend": "auto",
                "settings": config["settings"]
            }
            logger.info(f"🧠 Configured {memory_need} memory: {config['settings']['max_memory_size']}")
        else:
            # Reduced memory configuration
            reduced_settings = config["settings"].copy()
            reduced_settings["max_memory_size"] = "50MB"
            reduced_settings["persistence"] = False
            
            memory_config = {
                "enabled": True,
                "backend": "auto",
                "settings": reduced_settings
            }
            logger.info(f"🧠 Configured reduced memory: 50MB")
        
        # Environment-specific adjustments
        if environment == "production":
            # Optimize for production
            memory_config["settings"]["max_memory_size"] = "100MB"
            memory_config["settings"]["cleanup_interval"] = "1h"
        elif environment == "development":
            # More generous in development
            if "max_memory_size" in memory_config["settings"]:
                current_size = memory_config["settings"]["max_memory_size"]
                if current_size == "50MB":
                    memory_config["settings"]["max_memory_size"] = "100MB"
                elif current_size == "100MB":
                    memory_config["settings"]["max_memory_size"] = "200MB"
        
        return memory_config
    
    def _generate_agent_name(self, agent_id: str, behavior: str) -> str:
        """Generate a human-readable agent name"""
        if behavior in self.behavior_profiles:
            profile = self.behavior_profiles[behavior]
            return profile.name
        
        # Fallback: capitalize and format the ID
        name_parts = agent_id.replace("-", " ").replace("_", " ").split()
        return " ".join(word.capitalize() for word in name_parts)
    
    def _should_enable_streaming(self, profile: BehaviorProfile) -> bool:
        """Determine if streaming should be enabled based on behavior and capabilities"""
        # Use profile preference as base
        enable_streaming = profile.streaming
        
        # Adjust based on capabilities
        if self.capabilities.max_concurrent_requests < 2:
            # Limited concurrency - streaming might help with responsiveness
            enable_streaming = True
        
        # Environment-specific adjustments
        if self.capabilities.environment_type == "production":
            # In production, prefer streaming for better UX
            enable_streaming = True
        elif self.capabilities.environment_type == "development":
            # In development, use profile preference
            pass
        
        # Resource-based adjustments
        if self.capabilities.available_memory_mb < 2000:
            # Low memory - streaming can help reduce memory usage
            enable_streaming = True
        
        logger.info(f"📡 Streaming {'enabled' if enable_streaming else 'disabled'} for {profile.name}")
        
        return enable_streaming
    
    def expand_capabilities(self, capabilities: List[str]) -> List[str]:
        """Expand capability names to actual tool IDs"""
        capability_map = {
            "files": ["filesystem", "files"],
            "web": ["github", "aggregation"],
            "memory": [],  # Handled separately in memory config
            "code": ["filesystem", "github", "codebase_indexer"],
            "analysis": ["aggregation", "consensus", "multi_agent_reranking"],
            "forms": ["dynamic_forms"],
            "messaging": ["message_queue_publisher"]
        }
        
        tools = []
        for capability in capabilities:
            if capability in capability_map:
                tools.extend(capability_map[capability])
            else:
                # Assume it's a direct tool name
                tools.append(capability)
        
        # Remove duplicates and filter available tools
        available_tools = self._get_available_tools()
        expanded_tools = list(set(tool for tool in tools if tool in available_tools))
        
        logger.info(f"🎯 Expanded capabilities {capabilities} to tools: {expanded_tools}")
        
        return expanded_tools
    
    def get_behavior_info(self, behavior: str) -> Optional[BehaviorProfile]:
        """Get information about a specific behavior"""
        return self.behavior_profiles.get(behavior)
    
    def list_available_behaviors(self) -> List[str]:
        """Get list of all available behaviors"""
        return list(self.behavior_profiles.keys())
    
    def suggest_behavior(self, description: str) -> str:
        """Suggest the best behavior based on a description"""
        description_lower = description.lower()
        
        # Simple keyword matching
        behavior_keywords = {
            "coding": ["code", "program", "develop", "debug", "software"],
            "research": ["research", "analyze", "study", "investigate"],
            "creative": ["creative", "write", "story", "brainstorm", "idea"],
            "analytical": ["analyze", "data", "statistics", "metrics"],
            "support": ["help", "support", "assist", "customer", "troubleshoot"]
        }
        
        for behavior, keywords in behavior_keywords.items():
            if any(keyword in description_lower for keyword in keywords):
                logger.info(f"🎯 Suggested behavior '{behavior}' for description: '{description}'")
                return behavior
        
        # Default fallback
        logger.info(f"🎯 No specific behavior matched, using 'helpful' for: '{description}'")
        return "helpful"


# Convenience functions
def create_smart_defaults(capabilities: Optional[EnvironmentCapabilities] = None) -> SmartDefaults:
    """Create SmartDefaults instance with environment detection if needed"""
    if capabilities is None:
        detector = EnvironmentDetector()
        capabilities = detector.detect_capabilities()
    
    return SmartDefaults(capabilities)

def generate_zero_config_agent(
    agent_id: str, 
    behavior: str = "helpful",
    capabilities: Optional[EnvironmentCapabilities] = None,
    **overrides
):
    """Quick function to generate a zero-config agent"""
    smart_defaults = create_smart_defaults(capabilities)
    return smart_defaults.generate_agent_config(agent_id, behavior, **overrides)


# Example usage and testing
if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    
    print("🎯 LangSwarm Smart Defaults System")
    print("=" * 50)
    
    # Test environment detection and smart defaults
    from .detection import detect_environment
    
    capabilities = detect_environment()
    smart_defaults = SmartDefaults(capabilities)
    
    # Test different behaviors
    behaviors = ["helpful", "coding", "research", "creative", "analytical", "support"]
    
    print("\n🤖 Generated Agent Configurations:")
    for behavior in behaviors:
        print(f"\n--- {behavior.upper()} ---")
        
        config = smart_defaults.generate_agent_config(f"{behavior}-agent", behavior)
        
        print(f"Model: {config.model}")
        print(f"Tools: {config.tools}")
        print(f"Memory: {config.memory}")
        print(f"Max Tokens: {config.max_tokens}")
        print(f"Temperature: {config.temperature}")
        print(f"Streaming: {config.streaming}")
    
    print("\n🎯 Capability Expansion Test:")
    test_capabilities = ["files", "web", "code", "analysis"]
    expanded = smart_defaults.expand_capabilities(test_capabilities)
    print(f"Input: {test_capabilities}")
    print(f"Expanded: {expanded}")
    
    print("\n📋 Available Behaviors:")
    for behavior in smart_defaults.list_available_behaviors():
        profile = smart_defaults.get_behavior_info(behavior)
        print(f"- {behavior}: {profile.description}")
    
    print("\n🔍 Behavior Suggestions:")
    test_descriptions = [
        "I need help writing Python code",
        "I want to research market trends",
        "I need customer support assistance",
        "I want to write a creative story"
    ]
    
    for desc in test_descriptions:
        suggested = smart_defaults.suggest_behavior(desc)
        print(f"'{desc}' → {suggested}") 