"""
SMF Core - High-level abstractions for building MCP servers.

This module provides:
- ServerFactory: Creates configured FastMCP servers
- AppBuilder: Fluent interface for registering components
- ComponentRegistry: Tracks registered components
"""

from typing import Any, Callable, Dict, List, Optional, Type, Union

from fastmcp import FastMCP

from smf.registry import ComponentRegistry
from smf.settings import Settings, get_settings
from smf.settings import Settings, get_settings


class ServerFactory:
    """
    Factory for creating configured FastMCP servers.

    Applies default settings, attaches middleware, and configures auth.
    Uses Facade + Builder pattern to simplify server creation.
    """

    def __init__(self, settings: Optional[Settings] = None):
        """
        Initialize ServerFactory.

        Args:
            settings: Optional settings instance. If None, uses global settings.
        """
        self.settings = settings or get_settings()
        self._registry = ComponentRegistry()

    def create(
        self,
        name: Optional[str] = None,
        instructions: Optional[str] = None,
        version: Optional[str] = None,
        **kwargs: Any,
    ) -> FastMCP:
        """
        Create a configured FastMCP server.

        Args:
            name: Server name (overrides settings)
            instructions: Server instructions (overrides settings)
            version: Server version (overrides settings)
            **kwargs: Additional FastMCP parameters

        Returns:
            Configured FastMCP instance
        """
        # Merge settings with overrides
        fastmcp_params = self.settings.to_fastmcp_params()

        if name:
            fastmcp_params["name"] = name
        if instructions:
            fastmcp_params["instructions"] = instructions
        if version:
            fastmcp_params["version"] = version

        # Apply user overrides
        fastmcp_params.update(kwargs)

        # Create FastMCP instance
        mcp = FastMCP(**fastmcp_params)

        # Attach middleware (will be implemented in middleware module)
        self._attach_middleware(mcp)

        return mcp

    def _attach_middleware(self, mcp: FastMCP) -> None:
        """Attach middleware to FastMCP instance."""
        # Import here to avoid circular dependencies
        from smf.middleware import (
            attach_error_handling,
            attach_logging,
            attach_rate_limiting,
            attach_tracing,
        )
        from smf.observability import attach_metrics

        # Attach middleware in order
        if self.settings.structured_logging:
            attach_logging(mcp, self.settings)

        if self.settings.tracing_enabled:
            attach_tracing(mcp, self.settings)

        if self.settings.rate_limit_enabled:
            attach_rate_limiting(mcp, self.settings)

        if self.settings.metrics_enabled:
            attach_metrics(mcp, self.settings)

        # Error handling should be last
        attach_error_handling(mcp, self.settings)

    @property
    def registry(self) -> ComponentRegistry:
        """Get component registry."""
        return self._registry


class AppBuilder:
    """
    Fluent builder for registering tools, resources, and prompts.

    Uses Decorator + Fluent Builder pattern to register components
    before applying them to the server.
    """

    def __init__(
        self,
        server: Optional[FastMCP] = None,
        registry: Optional[ComponentRegistry] = None,
        settings: Optional[Settings] = None,
    ):
        """
        Initialize AppBuilder.

        Args:
            server: FastMCP server instance (created if None)
            registry: Component registry (created if None)
            settings: Settings instance (uses global if None)
        """
        self.settings = settings or get_settings()
        self._registry = registry or ComponentRegistry()
        self._server = server

    @property
    def server(self) -> FastMCP:
        """Get or create FastMCP server."""
        if self._server is None:
            factory = ServerFactory(self.settings)
            self._server = factory.create()
        return self._server

    def tool(
        self,
        func: Optional[Callable] = None,
        *,
        name: Optional[str] = None,
        description: Optional[str] = None,
        tags: Optional[List[str]] = None,
    ) -> Union[Callable, "AppBuilder"]:
        """
        Register a tool.

        Can be used as decorator or method:
        - @builder.tool
        - builder.tool(my_function)
        - builder.tool(name="my_tool")(my_function)

        Args:
            func: Function to register
            name: Tool name (defaults to function name)
            description: Tool description (defaults to docstring)
            tags: Tags for the tool

        Returns:
            Decorated function or builder instance
        """
        if func is None:
            # Used as decorator with arguments: @builder.tool(name="...")
            return lambda f: self.tool(f, name=name, description=description, tags=tags)

        # Register in registry
        self._registry.register_tool(
            func, name=name, description=description, tags=tags
        )

        # Apply to FastMCP server
        decorated = self.server.tool(func)
        return decorated

    def resource(
        self,
        func: Optional[Callable] = None,
        *,
        name: Optional[str] = None,
        description: Optional[str] = None,
        tags: Optional[List[str]] = None,
    ) -> Union[Callable, "AppBuilder"]:
        """
        Register a resource.

        Args:
            func: Function to register
            name: Resource name (defaults to function name)
            description: Resource description (defaults to docstring)
            tags: Tags for the resource

        Returns:
            Decorated function or builder instance
        """
        if func is None:
            return lambda f: self.resource(
                f, name=name, description=description, tags=tags
            )

        self._registry.register_resource(
            func, name=name, description=description, tags=tags
        )

        decorated = self.server.resource(func)
        return decorated

    def prompt(
        self,
        func: Optional[Callable] = None,
        *,
        name: Optional[str] = None,
        description: Optional[str] = None,
        tags: Optional[List[str]] = None,
    ) -> Union[Callable, "AppBuilder"]:
        """
        Register a prompt.

        Args:
            func: Function to register
            name: Prompt name (defaults to function name)
            description: Prompt description (defaults to docstring)
            tags: Tags for the prompt

        Returns:
            Decorated function or builder instance
        """
        if func is None:
            return lambda f: self.prompt(
                f, name=name, description=description, tags=tags
            )

        self._registry.register_prompt(
            func, name=name, description=description, tags=tags
        )

        decorated = self.server.prompt(func)
        return decorated

    def register_module(self, module: Any) -> "AppBuilder":
        """
        Register all components from a module.

        Scans module for decorated functions and registers them.

        Args:
            module: Python module to scan

        Returns:
            Self for method chaining
        """
        from smf.utils.import_tools import discover_components

        components = discover_components(module)
        for component in components:
            if component["type"] == "tool":
                self.tool(component["func"], **component.get("metadata", {}))
            elif component["type"] == "resource":
                self.resource(component["func"], **component.get("metadata", {}))
            elif component["type"] == "prompt":
                self.prompt(component["func"], **component.get("metadata", {}))

        return self

    def register_from_path(self, path: str) -> "AppBuilder":
        """
        Register components from a filesystem path.

        Args:
            path: Path to scan for components

        Returns:
            Self for method chaining
        """
        from smf.utils.import_tools import import_from_path

        module = import_from_path(path)
        return self.register_module(module)

    def build(self) -> FastMCP:
        """
        Build and return the configured server.

        Returns:
            Configured FastMCP instance
        """
        return self.server

    def __enter__(self) -> "AppBuilder":
        """Context manager entry."""
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        """Context manager exit."""
        pass


# Convenience function for quick server creation
def create_server(
    name: Optional[str] = None,
    settings: Optional[Settings] = None,
    **kwargs: Any,
) -> FastMCP:
    """
    Create a configured SMF server quickly.

    Args:
        name: Server name
        settings: Optional settings instance
        **kwargs: Additional FastMCP parameters

    Returns:
        Configured FastMCP instance

    Example:
        >>> mcp = create_server("My Server")
        >>> @mcp.tool
        >>> def greet(name: str) -> str:
        ...     return f"Hello, {name}!"
    """
    factory = ServerFactory(settings)
    return factory.create(name=name, **kwargs)

