"""Route mapping logic for OpenAPI operations."""

import enum
import re
from collections.abc import Callable
from dataclasses import dataclass, field
from re import Pattern
from typing import TYPE_CHECKING, Literal

if TYPE_CHECKING:
    from .components import (
        OpenAPIResource,
        OpenAPIResourceTemplate,
        OpenAPITool,
    )
# Import from our new utilities
from fastmcp.experimental.utilities.openapi import HttpMethod, HTTPRoute
from fastmcp.utilities.logging import get_logger

logger = get_logger(__name__)

# Type definitions for the mapping functions
RouteMapFn = Callable[[HTTPRoute, "MCPType"], "MCPType | None"]
ComponentFn = Callable[
    [
        HTTPRoute,
        "OpenAPITool | OpenAPIResource | OpenAPIResourceTemplate",
    ],
    None,
]


class MCPType(enum.Enum):
    """Type of FastMCP component to create from a route.

    Enum values:
        TOOL: Convert the route to a callable Tool
        RESOURCE: Convert the route to a Resource (typically GET endpoints)
        RESOURCE_TEMPLATE: Convert the route to a ResourceTemplate (typically GET with path params)
        EXCLUDE: Exclude the route from being converted to any MCP component
    """

    TOOL = "TOOL"
    RESOURCE = "RESOURCE"
    RESOURCE_TEMPLATE = "RESOURCE_TEMPLATE"
    # PROMPT = "PROMPT"
    EXCLUDE = "EXCLUDE"


@dataclass(kw_only=True)
class RouteMap:
    """Mapping configuration for HTTP routes to FastMCP component types."""

    methods: list[HttpMethod] | Literal["*"] = field(default="*")
    pattern: Pattern[str] | str = field(default=r".*")

    tags: set[str] = field(
        default_factory=set,
        metadata={"description": "A set of tags to match. All tags must match."},
    )
    mcp_type: MCPType = field(
        metadata={"description": "The type of FastMCP component to create."},
    )
    mcp_tags: set[str] = field(
        default_factory=set,
        metadata={
            "description": "A set of tags to apply to the generated FastMCP component."
        },
    )


# Default route mapping: all routes become tools.
# Users can provide custom route_maps to override this behavior.
DEFAULT_ROUTE_MAPPINGS = [
    RouteMap(mcp_type=MCPType.TOOL),
]


def _determine_route_type(
    route: HTTPRoute,
    mappings: list[RouteMap],
) -> RouteMap:
    """
    Determines the FastMCP component type based on the route and mappings.

    Args:
        route: HTTPRoute object
        mappings: List of RouteMap objects in priority order

    Returns:
        The RouteMap that matches the route, or a catchall "Tool" RouteMap if no match is found.
    """
    # Check mappings in priority order (first match wins)
    for route_map in mappings:
        # Check if the HTTP method matches
        if route_map.methods == "*" or route.method in route_map.methods:
            # Handle both string patterns and compiled Pattern objects
            if isinstance(route_map.pattern, Pattern):
                pattern_matches = route_map.pattern.search(route.path)
            else:
                pattern_matches = re.search(route_map.pattern, route.path)

            if pattern_matches:
                # Check if tags match (if specified)
                # If route_map.tags is empty, tags are not matched
                # If route_map.tags is non-empty, all tags must be present in route.tags (AND condition)
                if route_map.tags:
                    route_tags_set = set(route.tags or [])
                    if not route_map.tags.issubset(route_tags_set):
                        # Tags don't match, continue to next mapping
                        continue

                # We know mcp_type is not None here due to post_init validation
                assert route_map.mcp_type is not None
                logger.debug(
                    f"Route {route.method} {route.path} matched mapping to {route_map.mcp_type.name}"
                )
                return route_map

    # Default fallback
    return RouteMap(mcp_type=MCPType.TOOL)


# Export public symbols
__all__ = [
    "MCPType",
    "RouteMap",
    "RouteMapFn",
    "ComponentFn",
    "DEFAULT_ROUTE_MAPPINGS",
    "_determine_route_type",
]
