"""
Poping SDK - OpenAI-style AI Agent Framework

[File Overview]
===============
- Purpose: Module-level API for easy configuration and agent creation
- Data Flow:
    poping.set() → configure global client → poping.agent() → AgentBuilder
- Core Functions:
    1. set() - Configure global client (no return value)
    2. get_client() - Get global client instance
    3. agent() - Create or load agent (returns AgentBuilder)
    4. list_agents() - List all agents
- Related Files:
    @/poping_sdk/poping/client.py → PopingClient and Poping class
    @/poping_sdk/poping/agent.py → AgentBuilder, Agent, Session
    @/poping_sdk/poping/tool.py → @tool decorator

Quick Start:
    import poping

    # Configure once
    poping.set(api_key="your_api_key")

    # Create agent with builder pattern
    agent = poping.agent(llm="claude-3-5-sonnet-20241022").build(name="assistant")

    # Chat with session context manager
    with agent.session(end_user_id="user_123") as conv:
        response = conv.chat("Hello!")
"""

import os
from typing import Any, Dict, List, Optional

from .client import PopingClient, Poping
from .agent import AgentBuilder, StructuredResponse
from .storage import Storage
from .context import Context
from .tool import tool
from . import sessions
from .exceptions import (
    PopingError,
    APIError,
    AuthenticationError,
    RateLimitError,
    ResourceNotFoundError,
)

__version__ = "0.0.8"

__all__ = [
    # Module-level API (recommended)
    "set",
    "get_client",
    "agent",
    "list_agents",
    # Convenience wrappers
    "projects",
    "mcps",
    "market",
    "payments",
    # Core classes
    "Poping",           # Class-based API
    "PopingClient",     # Low-level HTTP client
    "Storage",
    "Context",
    "tool",
    "StructuredResponse",  # Structured output wrapper
    # Submodules
    "sessions",
    # Exceptions
    "PopingError",
    "APIError",
    "AuthenticationError",
    "RateLimitError",
    "ResourceNotFoundError",
]

# ==============================================================================
# Global State
# ==============================================================================

_global_client: Optional[PopingClient] = None
_global_project: Optional[str] = "default"


# ==============================================================================
# Module-Level API
# ==============================================================================

def set(
    api_key: Optional[str] = None,
    base_url: Optional[str] = None,
    project: Optional[str] = None,
    timeout: int = 310,
) -> None:
    """
    Configure Poping SDK globally (module-level API)

    This configures a global client that can be used by all subsequent
    poping.agent() calls. Does NOT return a value (OpenAI-style).

    Args:
        api_key: API key for authentication (or set POPING_API_KEY env var)
        base_url: Backend URL (default: http://localhost:8000 or POPING_BASE_URL env var)
        project: Default project name or ID to use for all sessions (optional)
        timeout: Request timeout in seconds (default: 310)

    Returns:
        None

    Example:
        import poping

        # Configure once
        poping.set(
            api_key="your_api_key",
            project="my_project"
        )

        # Use directly
        agent = poping.agent(llm="claude-3-5-sonnet-20241022").build(name="assistant")

        # Sessions will use the configured project by default
        with agent.session(end_user_id="user_123") as conv:
            response = conv.chat("Hello!")
    """
    global _global_client, _global_project

    # Store configuration
    _global_project = project

    # Create global client
    _global_client = PopingClient(
        api_key=api_key,
        base_url=base_url,
        timeout=timeout,
    )


def get_client() -> PopingClient:
    """
    Get the global PopingClient instance

    If not configured via set(), will auto-initialize from environment variables.

    Returns:
        Global PopingClient instance

    Raises:
        ValueError: If no API key found in config or environment

    Example:
        import poping

        poping.set(api_key="your_key")

        # Get client (for advanced usage)
        client = poping.get_client()
    """
    global _global_client

    if _global_client is None:
        # Auto-initialize from environment
        api_key = os.getenv("POPING_API_KEY")
        base_url = os.getenv("POPING_BASE_URL", "http://localhost:8000")

        if not api_key:
            raise ValueError(
                "Poping SDK not configured. Either:\n"
                "1. Call poping.set(api_key='...') first, or\n"
                "2. Set POPING_API_KEY environment variable"
            )

        _global_client = PopingClient(api_key=api_key, base_url=base_url)

    return _global_client


def agent(
    agent_id_or_llm: Optional[str] = None,
    *,
    llm: Optional[str] = None,
    name: Optional[str] = None,
    project: Optional[str] = None,
    **kwargs: Any,
) -> AgentBuilder:
    """
    Create new agent or load existing agent (module-level API)

    This function uses the globally configured client (via poping.set()).
    If not configured, it will auto-initialize from environment variables.

    Usage:
        1. Create new agent:
           poping.agent(llm="claude-3-5-sonnet-20241022")
           poping.agent("claude-3-5-sonnet-20241022")  # shorthand

        2. Load by ID:
           poping.agent("agt_abc123")

    Args:
        agent_id_or_llm: Agent ID (if starts with "agt_"), or LLM model name
        llm: LLM model (for creating new agent)
        name: Agent name (for creating new agent)
        project: Project ID scope (overrides global project)
        **kwargs: Additional configuration

    Returns:
        AgentBuilder instance

    Example:
        import poping

        poping.set(api_key="your_key")

        # Create new agent
        agent = poping.agent(llm="claude-3-5-sonnet-20241022").build(name="assistant")

        # Load existing agent
        agent = poping.agent("agt_abc123")
    """
    client = get_client()
    project_id = project or _global_project

    # Case 1: Load by ID (starts with "agt_")
    if agent_id_or_llm and agent_id_or_llm.startswith("agt_"):
        return AgentBuilder(
            client=client,
            agent_id=agent_id_or_llm,
            project_id=project_id,
        )

    # Case 2: Create new agent
    llm_final = llm or agent_id_or_llm
    if not llm_final:
        raise ValueError("Either agent_id_or_llm or llm parameter required")

    return AgentBuilder(
        client=client,
        llm=llm_final,
        project_id=project_id,
    )


def list_agents(project: Optional[str] = None) -> List[Dict[str, Any]]:
    """
    List all agents for current user (module-level API)

    Args:
        project: Project ID filter (optional)

    Returns:
        List of agent summaries with metadata

    Example:
        import poping

        poping.set(api_key="your_key")
        agents = poping.list_agents()

        for agent in agents:
            print(f"{agent['name']}: {agent['agent_id']}")
    """
    client = get_client()
    project_id = project or _global_project
    return client.list_agents(project_id=project_id)


# ==============================================================================
# Convenience Wrappers
# ==============================================================================

class projects:
    """Convenience wrapper for project operations"""

    @staticmethod
    def list() -> List[Dict[str, Any]]:
        """List all projects"""
        return get_client().list_projects()

    @staticmethod
    def create(**payload: Any) -> Dict[str, Any]:
        """Create new project"""
        return get_client().create_project(**payload)

    @staticmethod
    def update(project_id: str, **payload: Any) -> Dict[str, Any]:
        """Update project"""
        return get_client().update_project(project_id, **payload)

    @staticmethod
    def delete(project_id: str) -> Dict[str, Any]:
        """Delete project"""
        return get_client().delete_project(project_id)


class mcps:
    """Convenience wrapper for MCP operations"""

    @staticmethod
    def list(**params: Any) -> List[Dict[str, Any]]:
        """List MCPs"""
        return get_client().list_mcps(**params)

    @staticmethod
    def create(**payload: Any) -> Dict[str, Any]:
        """Create MCP"""
        return get_client().create_mcp(**payload)

    @staticmethod
    def get(mcp_id: str, **params: Any) -> Dict[str, Any]:
        """Get MCP details"""
        return get_client().get_mcp(mcp_id, **params)

    @staticmethod
    def update(mcp_id: str, **payload: Any) -> Dict[str, Any]:
        """Update MCP"""
        return get_client().update_mcp(mcp_id, **payload)

    @staticmethod
    def delete(mcp_id: str, **params: Any) -> Dict[str, Any]:
        """Delete MCP"""
        return get_client().delete_mcp(mcp_id, **params)


class market:
    """Convenience wrapper for market operations"""

    @staticmethod
    def list(category: Optional[str] = None, max_cost: Optional[int] = None) -> Dict[str, Any]:
        """List market tools"""
        params = {}
        if category is not None:
            params["category"] = category
        if max_cost is not None:
            params["max_cost"] = max_cost
        return get_client().list_market(**params)


class payments:
    """Convenience wrapper for payment operations"""

    @staticmethod
    def create(
        amount: float,
        subject: str = "Top-up",
        return_url: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Create payment"""
        return get_client().create_payment(
            amount=amount,
            subject=subject,
            return_url=return_url,
        )

    @staticmethod
    def status(order_id: str) -> Dict[str, Any]:
        """Get payment status"""
        return get_client().payment_status(order_id)
