import json
import logging
from typing import Any, Dict, List, Tuple

import mcp.types as types

from .utils import (
    clean_schema_for_display,
    generate_example_from_schema,
    resolve_schema_references,
    get_single_param_type_from_schema,
)

logger = logging.getLogger(__name__)


def convert_openapi_to_mcp_tools(
    openapi_schema: Dict[str, Any],
    describe_all_responses: bool = False,
    describe_full_response_schema: bool = False,
) -> Tuple[List[types.Tool], Dict[str, Dict[str, Any]]]:
    """
    Convert OpenAPI operations to MCP tools.

    Args:
        openapi_schema: The OpenAPI schema
        describe_all_responses: Whether to include all possible response schemas in tool descriptions
        describe_full_response_schema: Whether to include full response schema in tool descriptions

    Returns:
        A tuple containing:
        - A list of MCP tools
        - A mapping of operation IDs to operation details for HTTP execution
    """
    # Resolve all references in the schema at once
    resolved_openapi_schema = resolve_schema_references(openapi_schema, openapi_schema)

    tools = []
    operation_map = {}

    # Process each path in the OpenAPI schema
    for path, path_item in resolved_openapi_schema.get("paths", {}).items():
        for method, operation in path_item.items():
            # Skip non-HTTP methods
            if method not in ["get", "post", "put", "delete", "patch"]:
                logger.warning(f"Skipping non-HTTP method: {method}")
                continue

            # Get operation metadata
            operation_id = operation.get("operationId")
            if not operation_id:
                logger.warning(f"Skipping operation with no operationId: {operation}")
                continue

            # Save operation details for later HTTP calls
            operation_map[operation_id] = {
                "path": path,
                "method": method,
                "parameters": operation.get("parameters", []),
                "request_body": operation.get("requestBody", {}),
            }

            summary = operation.get("summary", "")
            description = operation.get("description", "")

            # Build tool description
            #tool_description = f"{summary}" if summary else f"{method.upper()} {path}"
            tool_description=""
            if description:
                tool_description += f"\n\n{description}"

            # Add response information to the description
            responses = operation.get("responses", {})
            if responses:
                response_info = "\n\n### Responses:\n"

                # Find the success response
                success_codes = range(200, 300)
                success_response = None
                for status_code in success_codes:
                    if str(status_code) in responses:
                        success_response = responses[str(status_code)]
                        break

                # Get the list of responses to include
                responses_to_include = responses
                if not describe_all_responses and success_response:
                    # If we're not describing all responses, only include the success response
                    success_code = next((code for code in success_codes if str(code) in responses), None)
                    if success_code:
                        responses_to_include = {str(success_code): success_response}

                # Process all selected responses
                for status_code, response_data in responses_to_include.items():
                    response_desc = response_data.get("description", "")
                    response_info += f"\n**{status_code}**: {response_desc}"

                    # Highlight if this is the main success response
                    if response_data == success_response:
                        response_info += " (Success Response)"

                    # Add schema information if available
                    if "content" in response_data:
                        for content_type, content_data in response_data["content"].items():
                            if "schema" in content_data:
                                schema = content_data["schema"]
                                response_info += f"\nContent-Type: {content_type}"

                                # Clean the schema for display
                                display_schema = clean_schema_for_display(schema)

                                # Try to get example response
                                example_response = None

                                # Check if content has examples
                                if "examples" in content_data:
                                    for example_key, example_data in content_data["examples"].items():
                                        if "value" in example_data:
                                            example_response = example_data["value"]
                                            break
                                # If content has example
                                elif "example" in content_data:
                                    example_response = content_data["example"]

                                # If we have an example response, add it to the docs
                                if example_response:
                                    response_info += "\n\n**Example Response:**\n```json\n"
                                    response_info += json.dumps(example_response, indent=2)
                                    response_info += "\n```"
                                # Otherwise generate an example from the schema
                                else:
                                    generated_example = generate_example_from_schema(display_schema)
                                    if generated_example:
                                        response_info += "\n\n**Example Response:**\n```json\n"
                                        response_info += json.dumps(generated_example, indent=2)
                                        response_info += "\n```"

                                # Only include full schema information if requested
                                if describe_full_response_schema:
                                    # Format schema information based on its type
                                    if display_schema.get("type") == "array" and "items" in display_schema:
                                        items_schema = display_schema["items"]

                                        response_info += "\n\n**Output Schema:** Array of items with the following structure:\n```json\n"
                                        response_info += json.dumps(items_schema, indent=2)
                                        response_info += "\n```"
                                    elif "properties" in display_schema:
                                        response_info += "\n\n**Output Schema:**\n```json\n"
                                        response_info += json.dumps(display_schema, indent=2)
                                        response_info += "\n```"
                                    else:
                                        response_info += "\n\n**Output Schema:**\n```json\n"
                                        response_info += json.dumps(display_schema, indent=2)
                                        response_info += "\n```"

                #tool_description += response_info

            # Organize parameters by type
            path_params = []
            query_params = []
            header_params = []
            body_params = []

            for param in operation.get("parameters", []):
                param_name = param.get("name")
                param_in = param.get("in")
                required = param.get("required", False)

                if param_in == "path":
                    path_params.append((param_name, param))
                elif param_in == "query":
                    query_params.append((param_name, param))
                elif param_in == "header":
                    header_params.append((param_name, param))

            # Process request body if present
            request_body = operation.get("requestBody", {})
            if request_body and "content" in request_body:
                content_type = next(iter(request_body["content"]), None)
                if content_type and "schema" in request_body["content"][content_type]:
                    schema = request_body["content"][content_type]["schema"]
                    if "properties" in schema:
                        for prop_name, prop_schema in schema["properties"].items():
                            required = prop_name in schema.get("required", [])
                            body_params.append(
                                (
                                    prop_name,
                                    {
                                        "name": prop_name,
                                        "schema": prop_schema,
                                        "required": required,
                                    },
                                )
                            )

            # Create input schema properties for all parameters
            properties = {}
            required_props = []

            # Add path parameters to properties
            for param_name, param in path_params:
                param_schema = param.get("schema", {})
                param_desc = param.get("description", "")
                param_required = param.get("required", True)  # Path params are usually required

                properties[param_name] = param_schema.copy()
                properties[param_name]["title"] = param_name
                if param_desc:
                    properties[param_name]["description"] = param_desc

                if "type" not in properties[param_name]:
                    properties[param_name]["type"] = param_schema.get("type", "string")

                if param_required:
                    required_props.append(param_name)

            # Add query parameters to properties
            for param_name, param in query_params:
                param_schema = param.get("schema", {})
                param_desc = param.get("description", "")
                param_required = param.get("required", False)

                properties[param_name] = param_schema.copy()
                properties[param_name]["title"] = param_name
                if param_desc:
                    properties[param_name]["description"] = param_desc

                if "type" not in properties[param_name]:
                    properties[param_name]["type"] = get_single_param_type_from_schema(param_schema)

                if "default" in param_schema:
                    properties[param_name]["default"] = param_schema["default"]

                if param_required:
                    required_props.append(param_name)

            # Add body parameters to properties
            for param_name, param in body_params:
                param_schema = param.get("schema", {})
                param_desc = param.get("description", "")
                param_required = param.get("required", False)

                properties[param_name] = param_schema.copy()
                properties[param_name]["title"] = param_name
                if param_desc:
                    properties[param_name]["description"] = param_desc

                if "type" not in properties[param_name]:
                    properties[param_name]["type"] = get_single_param_type_from_schema(param_schema)

                if "default" in param_schema:
                    properties[param_name]["default"] = param_schema["default"]

                if param_required:
                    required_props.append(param_name)

            # Create a proper input schema for the tool
            input_schema = {"type": "object", "properties": properties, "title": f"{operation_id}Arguments"}

            if required_props:
                input_schema["required"] = required_props

            # Create the MCP tool definition
            tool = types.Tool(name=operation_id, description=tool_description, inputSchema=input_schema)

            tools.append(tool)

    return tools, operation_map
