"""
CampfireFactory for dynamic campfire creation and subtask management.

This module provides the factory pattern implementation for creating campfires
on-demand based on task requirements, managing their lifecycle, and coordinating
resource allocation across multiple campfire instances.
"""

import asyncio
import logging
import uuid
from typing import List, Dict, Any, Optional, Type, Union
from dataclasses import dataclass
from datetime import datetime, timedelta

from .campfire import Campfire
from .camper import Camper
from .torch import Torch
from .orchestration import SubTask, RoleRequirement
from ..party_box.box_driver import BoxDriver
from ..mcp.protocol import MCPProtocol
from ..core.openrouter import LLMCamperMixin, OpenRouterConfig


logger = logging.getLogger(__name__)


@dataclass
class CampfireTemplate:
    """Template for creating campfires with specific configurations."""
    name: str
    description: str
    camper_types: List[str]
    default_config: Dict[str, Any]
    resource_requirements: Dict[str, Any]
    max_concurrent_tasks: int
    timeout_minutes: int


@dataclass
class CampfireInstance:
    """Represents an active campfire instance with metadata."""
    id: str
    campfire: Campfire
    template_name: str
    created_at: datetime
    last_activity: datetime
    assigned_subtasks: List[str]
    status: str  # 'active', 'idle', 'busy', 'terminated'
    resource_usage: Dict[str, Any]


class DynamicCamper(Camper, LLMCamperMixin):
    """
    A dynamically created camper that adapts to specific role requirements.
    
    This camper can be configured with role-specific prompts, expertise areas,
    and behavioral patterns based on the requirements generated by the orchestrator.
    """
    
    def __init__(self, party_box: BoxDriver, config: Dict[str, Any], role_requirement: RoleRequirement):
        """
        Initialize a dynamic camper with role-specific configuration.
        
        Args:
            party_box: Reference to the Party Box for asset storage
            config: Base configuration dictionary
            role_requirement: Specific role requirements for this camper
        """
        super().__init__(party_box, config)
        self.role_requirement = role_requirement
        
        # Setup LLM capabilities
        openrouter_config = OpenRouterConfig(
            api_key=config.get('openrouter_api_key', '')
        )
        self.setup_llm(openrouter_config)
        
        # Configure role-specific behavior
        self._configure_role_behavior()
    
    def _configure_role_behavior(self):
        """Configure the camper's behavior based on role requirements."""
        self.role_name = self.role_requirement.role_name
        self.expertise_areas = self.role_requirement.expertise_areas
        self.capabilities = self.role_requirement.required_capabilities
        self.personality_traits = self.role_requirement.personality_traits
        
        # Create role-specific system prompt
        self.system_prompt = self._create_system_prompt()
    
    def _create_system_prompt(self) -> str:
        """Create a system prompt based on role requirements."""
        return f"""
        You are a {self.role_name} with expertise in {', '.join(self.expertise_areas)}.
        
        Your key capabilities include: {', '.join(self.capabilities)}
        
        Your personality traits: {', '.join(self.personality_traits)}
        
        When processing tasks:
        1. Apply your specific expertise to provide high-quality results
        2. Consider the context and requirements carefully
        3. Maintain consistency with your role and personality
        4. Communicate clearly and effectively
        5. Focus on delivering value within your area of expertise
        
        Always strive for excellence while staying true to your role.
        """
    
    async def process(self, torch: Torch) -> Torch:
        """
        Process a torch using role-specific expertise.
        
        Args:
            torch: The torch to process
            
        Returns:
            Processed torch with role-specific insights
        """
        try:
            # Create role-specific processing prompt
            processing_prompt = f"""
            {self.system_prompt}
            
            TASK: {torch.claim}
            CONTEXT: {torch.metadata.get('context', 'No additional context')}
            
            Process this task according to your role as {self.role_name}.
            Provide a thorough response that demonstrates your expertise in {', '.join(self.expertise_areas)}.
            """
            
            # Process using LLM
            result = await self.llm_completion_with_mcp(processing_prompt)
            
            # Create result torch
            result_torch = Torch(
                claim=result,
                confidence=0.85,  # Base confidence for role-specific processing
                metadata={
                    **torch.metadata,
                    'processed_by': self.role_name,
                    'expertise_applied': self.expertise_areas,
                    'processing_timestamp': datetime.now().isoformat()
                }
            )
            
            logger.info(f"Task processed by {self.role_name}: {torch.id}")
            return result_torch
            
        except Exception as e:
            logger.error(f"Processing failed for {self.role_name}: {e}")
            # Return original torch with error metadata
            return Torch(
                claim=torch.claim,
                confidence=0.1,
                metadata={
                    **torch.metadata,
                    'processing_error': str(e),
                    'failed_processor': self.role_name
                }
            )


class CampfireFactory:
    """
    Factory for creating and managing campfire instances dynamically.
    
    Handles the lifecycle of campfires, from creation based on task requirements
    to resource management and cleanup. Supports template-based creation and
    automatic scaling based on workload.
    """
    
    def __init__(self, 
                 party_box: BoxDriver, 
                 mcp_protocol: Optional[MCPProtocol] = None,
                 config: Dict[str, Any] = None,
                 start_background_tasks: bool = True):
        """
        Initialize the campfire factory.
        
        Args:
            party_box: Shared Party Box for all campfires
            mcp_protocol: MCP protocol for communication
            config: Factory configuration
            start_background_tasks: Whether to start background maintenance tasks
        """
        self.party_box = party_box
        self.mcp_protocol = mcp_protocol
        self.config = config or {}
        
        # Factory state
        self.active_instances: Dict[str, CampfireInstance] = {}
        self.templates: Dict[str, CampfireTemplate] = {}
        self.resource_pool: Dict[str, Any] = {}
        
        # Configuration
        self.max_concurrent_campfires = self.config.get('max_concurrent_campfires', 10)
        self.default_timeout_minutes = self.config.get('default_timeout_minutes', 30)
        self.cleanup_interval_minutes = self.config.get('cleanup_interval_minutes', 5)
        
        # Initialize default templates
        self._initialize_default_templates()
        
        # Start background tasks
        self._cleanup_task = None
        if start_background_tasks:
            self._start_background_tasks()
    
    def _initialize_default_templates(self):
        """Initialize default campfire templates."""
        # General purpose template
        self.templates['general'] = CampfireTemplate(
            name='general',
            description='General purpose campfire for diverse tasks',
            camper_types=['dynamic'],
            default_config={
                'max_concurrent_tasks': 3,
                'torch_ttl_hours': 24
            },
            resource_requirements={'memory': 'low', 'cpu': 'low'},
            max_concurrent_tasks=3,
            timeout_minutes=30
        )
        
        # Analysis-focused template
        self.templates['analysis'] = CampfireTemplate(
            name='analysis',
            description='Specialized campfire for analytical tasks',
            camper_types=['dynamic'],
            default_config={
                'max_concurrent_tasks': 2,
                'torch_ttl_hours': 48
            },
            resource_requirements={'memory': 'medium', 'cpu': 'medium'},
            max_concurrent_tasks=2,
            timeout_minutes=45
        )
        
        # Creative template
        self.templates['creative'] = CampfireTemplate(
            name='creative',
            description='Campfire optimized for creative tasks',
            camper_types=['dynamic'],
            default_config={
                'max_concurrent_tasks': 1,
                'torch_ttl_hours': 12
            },
            resource_requirements={'memory': 'high', 'cpu': 'low'},
            max_concurrent_tasks=1,
            timeout_minutes=60
        )
    
    def _start_background_tasks(self):
        """Start background maintenance tasks."""
        if not self._cleanup_task:
            self._cleanup_task = asyncio.create_task(self._periodic_cleanup())
    
    async def _periodic_cleanup(self):
        """Periodically clean up idle and expired campfire instances."""
        while True:
            try:
                await asyncio.sleep(self.cleanup_interval_minutes * 60)
                await self._cleanup_idle_instances()
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Cleanup task error: {e}")
    
    async def _cleanup_idle_instances(self):
        """Clean up idle and expired campfire instances."""
        current_time = datetime.now()
        instances_to_remove = []
        
        for instance_id, instance in self.active_instances.items():
            # Check if instance has timed out
            timeout_threshold = instance.created_at + timedelta(
                minutes=self.templates[instance.template_name].timeout_minutes
            )
            
            # Check if instance is idle
            idle_threshold = current_time - timedelta(minutes=10)  # 10 minutes idle
            
            if (current_time > timeout_threshold or 
                (instance.status == 'idle' and instance.last_activity < idle_threshold)):
                instances_to_remove.append(instance_id)
        
        # Remove expired instances
        for instance_id in instances_to_remove:
            await self.terminate_campfire(instance_id)
            logger.info(f"Cleaned up expired campfire instance: {instance_id}")
    
    async def create_campfire_for_subtask(self, 
                                        subtask: SubTask, 
                                        role_requirement: RoleRequirement,
                                        template_name: str = 'general') -> str:
        """
        Create a campfire instance specifically for a subtask.
        
        Args:
            subtask: The subtask requiring a campfire
            role_requirement: Role requirements for the campfire
            template_name: Template to use for campfire creation
            
        Returns:
            Instance ID of the created campfire
        """
        if len(self.active_instances) >= self.max_concurrent_campfires:
            raise RuntimeError("Maximum concurrent campfires reached")
        
        if template_name not in self.templates:
            template_name = 'general'
        
        template = self.templates[template_name]
        instance_id = f"cf_{uuid.uuid4().hex[:8]}"
        
        try:
            # Create dynamic camper for the role
            camper_config = {
                **template.default_config,
                'name': f"{role_requirement.role_name}_camper",
                'openrouter_api_key': self.config.get('openrouter_api_key', ''),
                'model': self.config.get('model', 'meta-llama/llama-3.2-3b-instruct:free')
            }
            
            dynamic_camper = DynamicCamper(
                party_box=self.party_box,
                config=camper_config,
                role_requirement=role_requirement
            )
            
            # Create campfire
            campfire = Campfire(
                name=f"subtask_{subtask.id}",
                campers=[dynamic_camper],
                party_box=self.party_box,
                mcp_protocol=self.mcp_protocol,
                config=template.default_config
            )
            
            # Create instance record
            instance = CampfireInstance(
                id=instance_id,
                campfire=campfire,
                template_name=template_name,
                created_at=datetime.now(),
                last_activity=datetime.now(),
                assigned_subtasks=[subtask.id],
                status='active',
                resource_usage=template.resource_requirements.copy()
            )
            
            self.active_instances[instance_id] = instance
            
            logger.info(f"Created campfire instance {instance_id} for subtask {subtask.id}")
            return instance_id
            
        except Exception as e:
            logger.error(f"Failed to create campfire for subtask {subtask.id}: {e}")
            raise
    
    async def create_campfire_from_template(self, 
                                          template_name: str, 
                                          custom_config: Dict[str, Any] = None) -> str:
        """
        Create a campfire instance from a template.
        
        Args:
            template_name: Name of the template to use
            custom_config: Custom configuration overrides
            
        Returns:
            Instance ID of the created campfire
        """
        if template_name not in self.templates:
            raise ValueError(f"Template '{template_name}' not found")
        
        if len(self.active_instances) >= self.max_concurrent_campfires:
            raise RuntimeError("Maximum concurrent campfires reached")
        
        template = self.templates[template_name]
        instance_id = f"cf_{uuid.uuid4().hex[:8]}"
        
        try:
            # Merge configurations
            config = {**template.default_config}
            if custom_config:
                config.update(custom_config)
            
            # Create basic camper (would be enhanced based on template)
            camper_config = {
                **config,
                'name': f"{template_name}_camper",
                'openrouter_api_key': self.config.get('openrouter_api_key', ''),
                'model': self.config.get('model', 'meta-llama/llama-3.2-3b-instruct:free')
            }
            
            # For now, create a basic dynamic camper
            # This would be enhanced to support different camper types
            basic_role = RoleRequirement(
                role_name=template_name,
                expertise_areas=['general'],
                required_capabilities=['task_processing'],
                personality_traits=['reliable'],
                context_sources=[]
            )
            
            camper = DynamicCamper(
                party_box=self.party_box,
                config=camper_config,
                role_requirement=basic_role
            )
            
            # Create campfire
            campfire = Campfire(
                name=f"{template_name}_{instance_id}",
                campers=[camper],
                party_box=self.party_box,
                mcp_protocol=self.mcp_protocol,
                config=config
            )
            
            # Create instance record
            instance = CampfireInstance(
                id=instance_id,
                campfire=campfire,
                template_name=template_name,
                created_at=datetime.now(),
                last_activity=datetime.now(),
                assigned_subtasks=[],
                status='active',
                resource_usage=template.resource_requirements.copy()
            )
            
            self.active_instances[instance_id] = instance
            
            logger.info(f"Created campfire instance {instance_id} from template {template_name}")
            return instance_id
            
        except Exception as e:
            logger.error(f"Failed to create campfire from template {template_name}: {e}")
            raise
    
    async def assign_subtask_to_campfire(self, instance_id: str, subtask: SubTask) -> bool:
        """
        Assign a subtask to an existing campfire instance.
        
        Args:
            instance_id: ID of the campfire instance
            subtask: Subtask to assign
            
        Returns:
            True if assignment was successful
        """
        if instance_id not in self.active_instances:
            return False
        
        instance = self.active_instances[instance_id]
        template = self.templates[instance.template_name]
        
        # Check if campfire can handle more tasks
        if len(instance.assigned_subtasks) >= template.max_concurrent_tasks:
            return False
        
        # Assign the subtask
        instance.assigned_subtasks.append(subtask.id)
        instance.last_activity = datetime.now()
        instance.status = 'busy'
        
        logger.info(f"Assigned subtask {subtask.id} to campfire {instance_id}")
        return True
    
    async def process_torch_in_campfire(self, instance_id: str, torch: Torch) -> Optional[Torch]:
        """
        Process a torch in a specific campfire instance.
        
        Args:
            instance_id: ID of the campfire instance
            torch: Torch to process
            
        Returns:
            Processed torch or None if processing failed
        """
        if instance_id not in self.active_instances:
            logger.error(f"Campfire instance {instance_id} not found")
            return None
        
        instance = self.active_instances[instance_id]
        
        try:
            # Update instance activity
            instance.last_activity = datetime.now()
            instance.status = 'busy'
            
            # Process torch in the campfire
            result_torch = await instance.campfire.process_torch(torch)
            
            # Update instance status
            instance.status = 'active' if instance.assigned_subtasks else 'idle'
            
            return result_torch
            
        except Exception as e:
            logger.error(f"Failed to process torch in campfire {instance_id}: {e}")
            instance.status = 'active'
            return None
    
    async def terminate_campfire(self, instance_id: str) -> bool:
        """
        Terminate a campfire instance and clean up resources.
        
        Args:
            instance_id: ID of the campfire instance to terminate
            
        Returns:
            True if termination was successful
        """
        if instance_id not in self.active_instances:
            return False
        
        try:
            instance = self.active_instances[instance_id]
            
            # Stop the campfire
            if instance.campfire.is_running:
                await instance.campfire.stop()
            
            # Update status
            instance.status = 'terminated'
            
            # Remove from active instances
            del self.active_instances[instance_id]
            
            logger.info(f"Terminated campfire instance {instance_id}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to terminate campfire {instance_id}: {e}")
            return False
    
    def get_instance_status(self, instance_id: str) -> Optional[Dict[str, Any]]:
        """Get the status of a campfire instance."""
        if instance_id not in self.active_instances:
            return None
        
        instance = self.active_instances[instance_id]
        return {
            'id': instance.id,
            'template_name': instance.template_name,
            'status': instance.status,
            'created_at': instance.created_at.isoformat(),
            'last_activity': instance.last_activity.isoformat(),
            'assigned_subtasks': instance.assigned_subtasks,
            'resource_usage': instance.resource_usage
        }
    
    def list_active_instances(self) -> List[Dict[str, Any]]:
        """List all active campfire instances."""
        return [self.get_instance_status(instance_id) 
                for instance_id in self.active_instances.keys()]
    
    def add_template(self, template: CampfireTemplate):
        """Add a new campfire template."""
        self.templates[template.name] = template
        logger.info(f"Added campfire template: {template.name}")
    
    def get_resource_usage(self) -> Dict[str, Any]:
        """Get current resource usage across all instances."""
        total_instances = len(self.active_instances)
        active_instances = sum(1 for i in self.active_instances.values() if i.status == 'active')
        busy_instances = sum(1 for i in self.active_instances.values() if i.status == 'busy')
        
        return {
            'total_instances': total_instances,
            'active_instances': active_instances,
            'busy_instances': busy_instances,
            'idle_instances': total_instances - active_instances - busy_instances,
            'max_concurrent': self.max_concurrent_campfires,
            'utilization_percent': (total_instances / self.max_concurrent_campfires) * 100
        }
    
    async def shutdown(self):
        """Shutdown the factory and clean up all resources."""
        logger.info("Shutting down CampfireFactory")
        
        # Cancel cleanup task
        if self._cleanup_task:
            self._cleanup_task.cancel()
            try:
                await self._cleanup_task
            except asyncio.CancelledError:
                pass
        
        # Terminate all active instances
        instance_ids = list(self.active_instances.keys())
        for instance_id in instance_ids:
            await self.terminate_campfire(instance_id)
        
        logger.info("CampfireFactory shutdown complete")