"""Helper functions for service instantiation with dependency injection.

This module provides utilities to reduce boilerplate in MCP tool implementations
by centralizing the dependency extraction and service instantiation pattern.

STORY-023f: Added _build_service() and get_service_from_server_context() to support
both MCP tools and REST endpoints with shared service construction logic.
"""

from typing import TYPE_CHECKING, cast

from fastmcp import Context

from testio_mcp.services.base_service import BaseService

if TYPE_CHECKING:
    from testio_mcp.server import ServerContext


def _build_service[ServiceT: BaseService](
    service_class: type[ServiceT], server_ctx: "ServerContext"
) -> ServiceT:
    """Shared service construction logic (DRY principle).

    This function centralizes the service wiring logic that was previously
    duplicated. It's used by both get_service() (for MCP tools) and
    get_service_from_server_context() (for REST endpoints).

    Args:
        service_class: Service class to instantiate (must inherit BaseService)
        server_ctx: Server context with testio_client and cache

    Returns:
        Service instance with injected dependencies

    Note:
        This is an internal function. Use get_service() for MCP tools or
        get_service_from_server_context() for REST endpoints.
    """
    client = server_ctx["testio_client"]
    cache = server_ctx["cache"]

    # Create and return service instance with injected dependencies (STORY-023c)
    # Services have different dependency needs:
    # - TestService: needs repositories (client, test_repo, bug_repo)
    # - ProductService: needs cache (client, cache)
    # - Others: may need different combinations
    #
    # For now, we handle each service type explicitly
    service_name = service_class.__name__

    if service_name in ("TestService", "MultiTestReportService"):
        # Import here to avoid circular imports
        from testio_mcp.repositories.bug_repository import BugRepository

        # Create repositories from cache's database connection
        assert cache.db is not None, "Cache must be initialized"
        assert cache.repo is not None, "Cache repository must be initialized"

        # Use existing TestRepository from cache, create new BugRepository
        test_repo = cache.repo
        bug_repo = BugRepository(db=cache.db, client=client, customer_id=cache.customer_id)

        return service_class(client=client, test_repo=test_repo, bug_repo=bug_repo)  # type: ignore[call-arg]

    # Default: pass client and cache (for ProductService and others)
    return service_class(client=client, cache=cache)  # type: ignore[call-arg]


def get_service[ServiceT: BaseService](ctx: Context, service_class: type[ServiceT]) -> ServiceT:
    """Extract dependencies from FastMCP context and create service instance.

    This helper reduces tool boilerplate from 5 lines to 1 line while
    maintaining full type safety for mypy strict mode. It follows the
    dependency injection pattern established in ADR-007.

    Args:
        ctx: FastMCP context (injected automatically by framework)
        service_class: Service class to instantiate (must inherit BaseService)

    Returns:
        Service instance with injected client and cache dependencies

    Example:
        >>> @mcp.tool()
        >>> async def my_tool(param: str, ctx: Context) -> dict:
        ...     service = get_service(ctx, MyService)
        ...     return await service.my_method(param)

    Type Safety:
        >>> service = get_service(ctx, TestService)  # type: TestService
        >>> result = await service.get_test_status(123)  # type-checked!
    """
    # Extract dependencies from lifespan context (ADR-007)
    # Access via ctx.request_context.lifespan_context (FastMCP pattern)
    # Note: request_context is never None in MCP tool execution context
    assert ctx.request_context is not None, "Context must be available in tool execution"
    server_ctx = cast("ServerContext", ctx.request_context.lifespan_context)

    # Delegate to shared service construction logic
    return _build_service(service_class, server_ctx)


def get_service_from_server_context[ServiceT: BaseService](
    server_ctx: "ServerContext", service_class: type[ServiceT]
) -> ServiceT:
    """Create service instance from ServerContext (for REST endpoints).

    This is the FastAPI equivalent of get_service(). FastAPI endpoints receive
    Request objects, not FastMCP Context, so they access ServerContext directly
    from app.state.

    Args:
        server_ctx: Server context with testio_client and cache
        service_class: Service class to instantiate (must inherit BaseService)

    Returns:
        Service instance with injected dependencies

    Example:
        >>> @api.get("/api/tests/{test_id}")
        >>> async def get_test_rest(test_id: int, request: Request):
        ...     server_ctx = get_server_context_from_request(request)
        ...     service = get_service_from_server_context(server_ctx, TestService)
        ...     return await service.get_test_status(test_id)

    Type Safety:
        >>> service = get_service_from_server_context(server_ctx, TestService)
        >>> result = await service.get_test_status(123)  # type-checked!
    """
    # Delegate to shared service construction logic
    return _build_service(service_class, server_ctx)
