"""WrappedProvider for immutable transform composition.

This module provides `_WrappedProvider`, an internal class that wraps a provider
with an additional transform. Created by `Provider.wrap_transform()`.
"""

from __future__ import annotations

from collections.abc import AsyncIterator, Sequence
from contextlib import asynccontextmanager
from typing import TYPE_CHECKING

from fastmcp.server.providers.base import Provider
from fastmcp.utilities.versions import VersionSpec

if TYPE_CHECKING:
    from fastmcp.prompts.base import Prompt
    from fastmcp.resources.base import Resource
    from fastmcp.resources.template import ResourceTemplate
    from fastmcp.server.transforms import Transform
    from fastmcp.tools.base import Tool
    from fastmcp.utilities.components import FastMCPComponent


class _WrappedProvider(Provider):
    """Internal provider that wraps another provider with a transform.

    Created by Provider.wrap_transform(). Delegates all component sourcing
    to the inner provider's public methods (which apply inner's transforms),
    then applies the wrapper's transform on top.

    This enables immutable transform composition - the inner provider is
    unchanged, and the wrapper adds its transform layer.
    """

    def __init__(self, inner: Provider, transform: Transform) -> None:
        """Initialize wrapped provider.

        Args:
            inner: The provider to wrap.
            transform: The transform to apply on top of inner's results.
        """
        super().__init__()
        self._inner = inner
        # Add the transform to this provider's transform list
        # It will be applied via the normal transform chain
        self._transforms.append(transform)

    def __repr__(self) -> str:
        return f"_WrappedProvider({self._inner!r}, transforms={self._transforms!r})"

    # -------------------------------------------------------------------------
    # Delegate to inner provider's public methods (which apply inner's transforms)
    # -------------------------------------------------------------------------

    async def _list_tools(self) -> Sequence[Tool]:
        """Delegate to inner's list_tools (includes inner's transforms)."""
        return await self._inner.list_tools()

    async def _get_tool(
        self, name: str, version: VersionSpec | None = None
    ) -> Tool | None:
        """Delegate to inner's get_tool (includes inner's transforms)."""
        return await self._inner.get_tool(name, version)

    async def get_app_tool(self, app_name: str, tool_name: str) -> Tool | None:
        """Delegate to inner, bypassing this wrapper's transforms."""
        return await self._inner.get_app_tool(app_name, tool_name)

    async def get_tool_by_hash(self, tool_hash: str, tool_name: str) -> Tool | None:
        """Delegate to inner, bypassing this wrapper's transforms."""
        return await self._inner.get_tool_by_hash(tool_hash, tool_name)

    async def _list_resources(self) -> Sequence[Resource]:
        """Delegate to inner's list_resources (includes inner's transforms)."""
        return await self._inner.list_resources()

    async def _get_resource(
        self, uri: str, version: VersionSpec | None = None
    ) -> Resource | None:
        """Delegate to inner's get_resource (includes inner's transforms)."""
        return await self._inner.get_resource(uri, version)

    async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
        """Delegate to inner's list_resource_templates (includes inner's transforms)."""
        return await self._inner.list_resource_templates()

    async def _get_resource_template(
        self, uri: str, version: VersionSpec | None = None
    ) -> ResourceTemplate | None:
        """Delegate to inner's get_resource_template (includes inner's transforms)."""
        return await self._inner.get_resource_template(uri, version)

    async def _list_prompts(self) -> Sequence[Prompt]:
        """Delegate to inner's list_prompts (includes inner's transforms)."""
        return await self._inner.list_prompts()

    async def _get_prompt(
        self, name: str, version: VersionSpec | None = None
    ) -> Prompt | None:
        """Delegate to inner's get_prompt (includes inner's transforms)."""
        return await self._inner.get_prompt(name, version)

    async def get_tasks(self) -> Sequence[FastMCPComponent]:
        """Delegate to inner's get_tasks and apply wrapper's transforms."""
        # Import here to avoid circular imports
        from fastmcp.prompts.base import Prompt
        from fastmcp.resources.base import Resource
        from fastmcp.resources.template import ResourceTemplate
        from fastmcp.tools.base import Tool

        # Get tasks from inner (already has inner's transforms)
        components = list(await self._inner.get_tasks())

        # Apply this wrapper's transforms to the components
        # We need to apply transforms per component type
        tools = [c for c in components if isinstance(c, Tool)]
        resources = [c for c in components if isinstance(c, Resource)]
        templates = [c for c in components if isinstance(c, ResourceTemplate)]
        prompts = [c for c in components if isinstance(c, Prompt)]

        # Apply this wrapper's transforms sequentially
        for transform in self.transforms:
            tools = await transform.list_tools(tools)
            resources = await transform.list_resources(resources)
            templates = await transform.list_resource_templates(templates)
            prompts = await transform.list_prompts(prompts)

        return [
            c
            for c in [
                *tools,
                *resources,
                *templates,
                *prompts,
            ]
            if c.task_config.supports_tasks()
        ]

    # -------------------------------------------------------------------------
    # Lifecycle - combine with inner
    # -------------------------------------------------------------------------

    @asynccontextmanager
    async def lifespan(self) -> AsyncIterator[None]:
        """Combine lifespan with inner provider."""
        async with self._inner.lifespan():
            yield
