"""
Factory module for creating Graph MCP clients.

This module provides factory functions for creating GraphMCPClient instances
with proper configuration and environment discovery.
"""

from contextlib import asynccontextmanager

from robosystems.config import env
from robosystems.config.constants import MAX_QUERY_LENGTH
from robosystems.config.tuning import TuningConfig
from robosystems.logger import logger

from .client import GraphMCPClient
from .pool import get_connection_pool


async def create_graph_mcp_client(
  graph_id: str = "sec", api_base_url: str | None = None
) -> GraphMCPClient:
  """
  Create a Graph MCP client with environment-based configuration and timeout controls.

  Args:
      graph_id: Graph database identifier
      api_base_url: Override API URL (uses env var if None)

  Returns:
      Configured GraphMCPClient instance with appropriate timeouts
  """
  # If URL not provided, use GraphClientFactory to discover the proper endpoint
  if not api_base_url:
    from robosystems.config.shared_repositories import is_shared_repository_or_subgraph
    from robosystems.graph_api.client.factory import GraphClientFactory

    # Determine operation type based on graph
    # Shared repositories and their subgraphs (e.g. sec_historical) are read-only
    operation_type = "read" if is_shared_repository_or_subgraph(graph_id) else "write"

    # Get a client from the factory which will discover the proper endpoint
    # The factory handles routing appropriately:
    # - Shared repos: Routes to shared_master/shared_replica infrastructure
    # - User graphs: Looks up the tier from the database
    graph_client = await GraphClientFactory.create_client(
      graph_id=graph_id, operation_type=operation_type
    )

    # Extract the base URL from the client
    if hasattr(graph_client, "config") and hasattr(graph_client.config, "base_url"):
      api_base_url = graph_client.config.base_url
    elif hasattr(graph_client, "_base_url"):
      api_base_url = graph_client._base_url
    elif hasattr(graph_client, "base_url"):
      api_base_url = graph_client.base_url
    else:
      # Fallback to environment variable
      api_base_url = env.GRAPH_API_URL or "http://localhost:8001"

    # Ensure we have a valid URL
    if not api_base_url:
      api_base_url = "http://localhost:8001"

    logger.info(
      f"GraphClientFactory discovered endpoint: {api_base_url} for graph {graph_id}"
    )

  # Configure timeouts - runtime tunable via SSM
  timeout = TuningConfig.get_graph_http_timeout()
  query_timeout = TuningConfig.get_graph_query_timeout()
  max_query_length = MAX_QUERY_LENGTH

  return GraphMCPClient(
    api_base_url=api_base_url,
    graph_id=graph_id,
    timeout=timeout,
    query_timeout=query_timeout,
    max_query_length=max_query_length,
  )


@asynccontextmanager
async def acquire_graph_mcp_client(
  graph_id: str = "sec", api_base_url: str | None = None, use_pool: bool = True
):
  """
  Acquire a Graph MCP client from the connection pool.

  This is the preferred method for getting MCP clients as it reuses
  connections to reduce initialization overhead.

  Args:
      graph_id: Graph database identifier
      api_base_url: Override API URL (uses env var if None)
      use_pool: Whether to use connection pooling (default: True)

  Yields:
      Configured GraphMCPClient instance

  Example:
      async with acquire_graph_mcp_client("sec") as client:
          result = await client.execute_query("MATCH (n) RETURN n LIMIT 1")
  """
  if use_pool:
    # Use connection pool for better performance
    pool = get_connection_pool()
    async with pool.acquire(graph_id, api_base_url) as client:
      yield client
  else:
    # Create a new client without pooling (for testing or special cases)
    client = await create_graph_mcp_client(graph_id, api_base_url)
    try:
      yield client
    finally:
      # Clean up if client has a close method
      if hasattr(client, "close"):
        try:
          await client.close()
        except Exception as e:
          logger.error(f"Error closing MCP client: {e}")
