"""Agent factory for creating different agent implementations.

This module provides a factory pattern + registry for creating agents,
making it easy to add new agent types without modifying existing code.
"""

from __future__ import annotations

from typing import Callable, Dict

from AutoGLM_GUI.config import AgentConfig, ModelConfig
from AutoGLM_GUI.logger import logger
from AutoGLM_GUI.types import AgentSpecificConfig

from .protocols import AsyncAgent, BaseAgent


# Agent registry: agent_type -> (creator_function, config_schema)
AGENT_REGISTRY: Dict[str, Callable] = {}


def register_agent(
    agent_type: str,
    creator: Callable,
) -> None:
    """
    Register a new agent type.

    Args:
        agent_type: Unique identifier for the agent type (e.g., "glm-async", "mai")
        creator: Function that creates the agent instance.
                  Signature: (model_config, agent_config, agent_specific_config, callbacks) -> BaseAgent

    Example:
        >>> def create_mai_agent(model_config, agent_config, mai_config, callbacks):
        >>>     return MAIAgentAdapter(...)
        >>>
        >>> register_agent("mai", create_mai_agent)
    """
    if agent_type in AGENT_REGISTRY:
        logger.warning(f"Agent type '{agent_type}' already registered, overwriting")

    AGENT_REGISTRY[agent_type] = creator
    logger.info(f"Registered agent type: {agent_type}")


def create_agent(
    agent_type: str,
    model_config: ModelConfig,
    agent_config: AgentConfig,
    agent_specific_config: AgentSpecificConfig,
    device,
    takeover_callback: Callable | None = None,
    confirmation_callback: Callable | None = None,
) -> AsyncAgent | BaseAgent:
    """
    Create an agent instance using the factory pattern.

    Args:
        agent_type: Type of agent to create (e.g., "glm-async", "mai")
        model_config: Model configuration
        agent_config: Agent configuration
        agent_specific_config: Agent-specific configuration (e.g., MAIConfig fields)
        device: DeviceProtocol instance (provided by PhoneAgentManager)
        takeover_callback: Takeover callback
        confirmation_callback: Confirmation callback

    Returns:
        Agent instance implementing AsyncAgent or BaseAgent interface.
        Use runtime type detection (e.g., inspect.iscoroutinefunction) to determine which.

    Raises:
        ValueError: If agent_type is not registered
    """
    if agent_type not in AGENT_REGISTRY:
        available = ", ".join(AGENT_REGISTRY.keys())
        raise ValueError(
            f"Unknown agent type: '{agent_type}'. Available types: {available}"
        )

    creator = AGENT_REGISTRY[agent_type]

    try:
        agent = creator(
            model_config=model_config,
            agent_config=agent_config,
            agent_specific_config=agent_specific_config,
            device=device,
            takeover_callback=takeover_callback,
            confirmation_callback=confirmation_callback,
        )
        logger.debug(f"Created agent of type '{agent_type}'")
        return agent
    except Exception as e:
        logger.error(f"Failed to create agent of type '{agent_type}': {e}")
        raise


def list_agent_types() -> list[str]:
    """Get list of registered agent types."""
    return list(AGENT_REGISTRY.keys())


def is_agent_type_registered(agent_type: str) -> bool:
    """Check if an agent type is registered."""
    return agent_type in AGENT_REGISTRY


# ==================== Built-in Agent Creators ====================


def _create_async_glm_agent(
    model_config: ModelConfig,
    agent_config: AgentConfig,
    agent_specific_config: AgentSpecificConfig,  # noqa: ARG001
    device,
    takeover_callback: Callable | None = None,
    confirmation_callback: Callable | None = None,
) -> AsyncAgent:
    """Create AsyncGLMAgent instance.

    This is the async implementation that supports:
    - Native streaming with AsyncIterator
    - Immediate cancellation with asyncio.CancelledError
    - No worker threads or queues needed
    """
    from .glm.async_agent import AsyncGLMAgent

    # Note: AsyncGLMAgent implements AsyncAgent Protocol, but pyright cannot verify
    # async generator function compatibility with Protocol. This is a known limitation
    # of Python's type system. The implementation is correct at runtime.
    return AsyncGLMAgent(  # type: ignore[return-value]
        model_config=model_config,
        agent_config=agent_config,
        device=device,
        confirmation_callback=confirmation_callback,
        takeover_callback=takeover_callback,
    )


def _create_internal_mai_agent(
    model_config: ModelConfig,
    agent_config: AgentConfig,
    agent_specific_config: AgentSpecificConfig,
    device,
    takeover_callback: Callable | None = None,
    confirmation_callback: Callable | None = None,
) -> BaseAgent:
    from .mai.agent import InternalMAIAgent

    history_n = agent_specific_config.get("history_n", 3)

    return InternalMAIAgent(
        model_config=model_config,
        agent_config=agent_config,
        device=device,
        history_n=history_n,
        confirmation_callback=confirmation_callback,
        takeover_callback=takeover_callback,
    )


register_agent("glm-async", _create_async_glm_agent)
register_agent("async-glm", _create_async_glm_agent)  # 别名
register_agent("mai", _create_internal_mai_agent)
