"""
AgentFactory - Create Agent instances dynamically from specifications.

This module provides utilities for creating agents with generic LLM-powered logic
based on agent specifications generated by PlannerLLM.
"""

import logging
from typing import Any, Optional
from synqed.agent import Agent, AgentLogicContext, get_interaction_protocol

logger = logging.getLogger(__name__)


def create_generic_agent_logic(
    agent_name: str,
    agent_description: str,
    agent_capabilities: list[str],
    provider: str = "anthropic",
    api_key: Optional[str] = None,
    model: Optional[str] = None,
    custom_instructions: str = "",
):
    """
    Create a generic LLM-powered agent logic function.
    
    This factory function generates an async logic function that uses
    an LLM to process messages and generate responses. The agent follows
    the interaction protocol and coordinates with other agents.
    
    Args:
        agent_name: Name of the agent
        agent_description: Description of the agent's purpose
        agent_capabilities: List of agent capabilities
        provider: LLM provider ("anthropic" or "openai")
        api_key: API key for the LLM
        model: Model name (e.g., "claude-sonnet-4-20250514", "gpt-4o")
        custom_instructions: Optional custom instructions for the agent
        
    Returns:
        Async function that can be used as agent logic
        
    Example:
        ```python
        logic_fn = create_generic_agent_logic(
            agent_name="researcher",
            agent_description="Research specialist",
            agent_capabilities=["research", "analysis", "fact-checking"],
            provider="anthropic",
            api_key=os.getenv("ANTHROPIC_API_KEY"),
            model="claude-sonnet-4-20250514"
        )
        
        agent = Agent(
            name="researcher",
            description="Research specialist",
            logic=logic_fn,
            capabilities=["research", "analysis", "fact-checking"]
        )
        ```
    """
    # Validate required parameters
    if not api_key:
        raise ValueError(f"API key is required for agent '{agent_name}'")
    
    # Initialize LLM client based on provider
    if provider == "anthropic":
        try:
            from anthropic import AsyncAnthropic
            client = AsyncAnthropic(api_key=api_key)
        except ImportError:
            raise ImportError(
                "anthropic package is required. Install with: pip install anthropic"
            )
    elif provider == "openai":
        try:
            from openai import AsyncOpenAI
            client = AsyncOpenAI(api_key=api_key)
        except ImportError:
            raise ImportError(
                "openai package is required. Install with: pip install openai"
            )
    else:
        raise ValueError(f"Unsupported provider: {provider}")
    
    # Use default model if not provided
    if not model:
        model = "claude-sonnet-4-20250514" if provider == "anthropic" else "gpt-4o"
    
    async def agent_logic(context: AgentLogicContext) -> dict:
        """Generic LLM-powered agent logic."""
        latest = context.latest_message
        if not latest or not latest.content:
            return None
        
        # Get interaction protocol with team roster
        protocol = get_interaction_protocol(exclude_agent=agent_name)
        
        # Build system prompt
        capabilities_str = ", ".join(agent_capabilities)
        system_prompt = f"""{protocol}

YOUR ROLE: {agent_name}
YOUR DESCRIPTION: {agent_description}
YOUR CAPABILITIES: {capabilities_str}
DEFAULT COORDINATION STYLE: respond_to_sender

{custom_instructions}

Focus on tasks that align with your capabilities. Delegate to other agents when appropriate.
Always respond with valid JSON in the format: {{"send_to": "agent_name", "content": "your message"}}
"""
        
        # Get conversation history
        history = context.get_conversation_history(workspace_wide=True)
        
        # Add shared plan context if available
        plan_context = ""
        if context.shared_plan:
            plan_context = f"\n\nShared Plan:\n{context.shared_plan}"
        
        # Build user message
        user_message = f"Conversation:\n{history}{plan_context}\n\nRespond with JSON:"
        
        # Call LLM based on provider
        try:
            if provider == "anthropic":
                response = await client.messages.create(
                    model=model,
                    max_tokens=500,
                    system=system_prompt,
                    messages=[{"role": "user", "content": user_message}],
                )
                return response.content[0].text.strip()
            
            elif provider == "openai":
                response = await client.chat.completions.create(
                    model=model,
                    max_tokens=500,
                    messages=[
                        {"role": "system", "content": system_prompt},
                        {"role": "user", "content": user_message}
                    ],
                    response_format={"type": "json_object"},
                )
                return response.choices[0].message.content.strip()
            
        except Exception as e:
            logger.error(f"Error in agent {agent_name} logic: {e}")
            # Return error message to sender
            return context.reply(f"Error processing request: {str(e)}")
    
    return agent_logic


def create_agent_from_spec(
    agent_spec: dict[str, Any],
    custom_instructions: str = "",
) -> Agent:
    """
    Create an Agent instance from an agent specification.
    
    This function takes an agent specification (typically generated by
    PlannerLLM.create_agents_from_task) and creates a fully functional
    Agent instance with generic LLM-powered logic.
    
    Args:
        agent_spec: Agent specification dictionary containing:
            - name: Agent name
            - description: Agent description
            - capabilities: List of capabilities
            - role: Team/domain identifier
            - provider: LLM provider
            - api_key: API key
            - model: Model name
        custom_instructions: Optional custom instructions for the agent
        
    Returns:
        Configured Agent instance
        
    Raises:
        ValueError: If required fields are missing from agent_spec
        
    Example:
        ```python
        agent_spec = {
            "name": "researcher",
            "description": "Research specialist",
            "capabilities": ["research", "analysis"],
            "role": "research_team",
            "provider": "anthropic",
            "api_key": os.getenv("ANTHROPIC_API_KEY"),
            "model": "claude-sonnet-4-20250514"
        }
        
        agent = create_agent_from_spec(agent_spec)
        ```
    """
    # Validate required fields
    required_fields = ["name", "description", "capabilities", "role", "provider", "api_key"]
    missing_fields = [field for field in required_fields if field not in agent_spec]
    if missing_fields:
        raise ValueError(f"Agent spec missing required fields: {missing_fields}")
    
    # Extract fields
    name = agent_spec["name"]
    description = agent_spec["description"]
    capabilities = agent_spec["capabilities"]
    role = agent_spec["role"]
    provider = agent_spec["provider"]
    api_key = agent_spec["api_key"]
    model = agent_spec.get("model")
    
    # Create generic logic function
    logic_fn = create_generic_agent_logic(
        agent_name=name,
        agent_description=description,
        agent_capabilities=capabilities,
        provider=provider,
        api_key=api_key,
        model=model,
        custom_instructions=custom_instructions,
    )
    
    # Create Agent instance
    agent = Agent(
        name=name,
        description=description,
        logic=logic_fn,
        role=role,
        capabilities=capabilities,
        default_coordination="respond_to_sender",
    )
    
    logger.info(f"Created agent '{name}' from specification")
    return agent


def create_agents_from_specs(
    agent_specs: list[dict[str, Any]],
    custom_instructions: str = "",
) -> list[Agent]:
    """
    Create multiple Agent instances from agent specifications.
    
    Args:
        agent_specs: List of agent specifications
        custom_instructions: Optional custom instructions for all agents
        
    Returns:
        List of configured Agent instances
        
    Example:
        ```python
        planner = PlannerLLM(provider="anthropic", api_key="...")
        agent_specs = await planner.create_agents_from_task("Plan a conference")
        
        agents = create_agents_from_specs(agent_specs)
        
        # Register agents
        for agent in agents:
            AgentRuntimeRegistry.register(agent.name, agent)
        ```
    """
    agents = []
    for agent_spec in agent_specs:
        try:
            agent = create_agent_from_spec(agent_spec, custom_instructions)
            agents.append(agent)
        except Exception as e:
            logger.error(f"Failed to create agent from spec: {e}")
            continue
    
    logger.info(f"Created {len(agents)} agents from {len(agent_specs)} specifications")
    return agents

