"""
AWS Bedrock adapter for Boring-OS.

Provides AWS Bedrock Agents-compatible action group schemas and
Lambda handler utilities for integrating Boring-OS tools.

Usage:
    from boring_os import BoringOS
    from boring_os.adapters.bedrock import create_action_group_schema
    
    client = BoringOS(api_key="...")
    schema = create_action_group_schema(client, tools=["get_customer_profile"])
    
    # Or use the Lambda handler decorator
    from boring_os.adapters.bedrock import bedrock_handler
    
    @bedrock_handler(client)
    def lambda_handler(event, context):
        pass  # Handled automatically

Requires: pip install boring-os[bedrock]
"""

from __future__ import annotations

import json
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable

if TYPE_CHECKING:
    from boring_os.client import BoringOS
    from boring_os.models import Tool


def _json_schema_to_openapi(json_schema: dict[str, Any]) -> dict[str, Any]:
    """
    Convert JSON Schema to OpenAPI 3.0 schema format.
    
    AWS Bedrock uses OpenAPI 3.0 format for action group schemas.
    
    Args:
        json_schema: JSON Schema object
    
    Returns:
        OpenAPI-compatible schema
    """
    openapi_schema: dict[str, Any] = {}
    
    # Map basic type
    if "type" in json_schema:
        openapi_schema["type"] = json_schema["type"]
    
    # Map description
    if "description" in json_schema:
        openapi_schema["description"] = json_schema["description"]
    
    # Map enum
    if "enum" in json_schema:
        openapi_schema["enum"] = json_schema["enum"]
    
    # Map default
    if "default" in json_schema:
        openapi_schema["default"] = json_schema["default"]
    
    # Map format
    if "format" in json_schema:
        openapi_schema["format"] = json_schema["format"]
    
    # Map string constraints
    if "minLength" in json_schema:
        openapi_schema["minLength"] = json_schema["minLength"]
    if "maxLength" in json_schema:
        openapi_schema["maxLength"] = json_schema["maxLength"]
    if "pattern" in json_schema:
        openapi_schema["pattern"] = json_schema["pattern"]
    
    # Map number constraints
    if "minimum" in json_schema:
        openapi_schema["minimum"] = json_schema["minimum"]
    if "maximum" in json_schema:
        openapi_schema["maximum"] = json_schema["maximum"]
    
    # Map object properties
    if "properties" in json_schema:
        openapi_schema["properties"] = {
            name: _json_schema_to_openapi(prop)
            for name, prop in json_schema["properties"].items()
        }
    
    # Map required fields
    if "required" in json_schema:
        openapi_schema["required"] = json_schema["required"]
    
    # Map array items
    if "items" in json_schema:
        openapi_schema["items"] = _json_schema_to_openapi(json_schema["items"])
    
    return openapi_schema


def _create_operation_for_tool(tool: "Tool") -> dict[str, Any]:
    """
    Create an OpenAPI operation definition for a Boring-OS tool.
    
    Args:
        tool: Tool definition
    
    Returns:
        OpenAPI operation object
    """
    # Build description
    description = tool.description or f"Execute the {tool.name} tool"
    if tool.instructions:
        if tool.instructions.summary:
            description = tool.instructions.summary
    
    # Build request body schema from input schema
    request_schema: dict[str, Any] = {"type": "object", "properties": {}}
    required: list[str] = []
    
    if tool.schema_ and tool.schema_.input:
        input_schema = tool.schema_.input
        if "properties" in input_schema:
            for name, prop in input_schema["properties"].items():
                request_schema["properties"][name] = _json_schema_to_openapi(prop)
        if "required" in input_schema:
            required = input_schema["required"]
    
    if required:
        request_schema["required"] = required
    
    # Build response schema from output schema
    response_schema: dict[str, Any] = {"type": "object"}
    if tool.schema_ and tool.schema_.output:
        response_schema = _json_schema_to_openapi(tool.schema_.output)
    
    operation: dict[str, Any] = {
        "operationId": tool.name,
        "summary": description[:100],  # OpenAPI summary should be short
        "description": description,
        "requestBody": {
            "required": True,
            "content": {
                "application/json": {
                    "schema": request_schema
                }
            }
        },
        "responses": {
            "200": {
                "description": "Successful tool execution",
                "content": {
                    "application/json": {
                        "schema": response_schema
                    }
                }
            },
            "400": {
                "description": "Invalid parameters"
            },
            "500": {
                "description": "Tool execution error"
            }
        }
    }
    
    return operation


def create_action_group_schema(
    client: "BoringOS",
    tools: list[str] | None = None,
    title: str = "Boring-OS Tools",
    description: str = "Tools provided by Boring-OS",
    version: str = "1.0.0",
) -> dict[str, Any]:
    """
    Create an OpenAPI schema for AWS Bedrock action group.
    
    Generates an OpenAPI 3.0 specification that can be used to create
    a Bedrock Agents action group.
    
    Args:
        client: Boring-OS client instance
        tools: List of tool names to include (None = all active tools)
        title: API title for the schema
        description: API description
        version: API version
    
    Returns:
        OpenAPI 3.0 specification dictionary
    
    Example:
        from boring_os import BoringOS
        from boring_os.adapters.bedrock import create_action_group_schema
        
        client = BoringOS(api_key="...")
        
        # Create schema for specific tools
        schema = create_action_group_schema(
            client,
            tools=["get_customer_profile", "get_billing_details"]
        )
        
        # Save to file for Bedrock
        import json
        with open("action_group_schema.json", "w") as f:
            json.dump(schema, f, indent=2)
    """
    from boring_os.models import ToolStatus
    
    # Fetch tool definitions
    tool_defs: list["Tool"] = []
    
    if tools:
        for name in tools:
            try:
                tool_def = client.tools.get(name)
                tool_defs.append(tool_def)
            except Exception:
                pass
    else:
        response = client.tools.list(status=ToolStatus.ACTIVE, limit=1000)
        for tool_info in response.tools:
            try:
                tool_def = client.tools.get(tool_info.name)
                tool_defs.append(tool_def)
            except Exception:
                pass
    
    # Build paths from tools
    paths: dict[str, Any] = {}
    for tool in tool_defs:
        # Each tool becomes a POST endpoint
        path = f"/{tool.name}"
        paths[path] = {
            "post": _create_operation_for_tool(tool)
        }
    
    # Build OpenAPI spec
    openapi_schema: dict[str, Any] = {
        "openapi": "3.0.0",
        "info": {
            "title": title,
            "description": description,
            "version": version,
        },
        "paths": paths,
    }
    
    return openapi_schema


def bedrock_handler(
    client: "BoringOS",
    return_format: str = "json",
) -> Callable[[Callable[..., Any]], Callable[..., dict[str, Any]]]:
    """
    Decorator to create a Bedrock Agents Lambda handler.
    
    Wraps a Lambda function to automatically handle Bedrock agent
    requests and execute the appropriate Boring-OS tool.
    
    Args:
        client: Boring-OS client instance
        return_format: Response format ("json" or "text")
    
    Returns:
        Decorator function
    
    Example:
        from boring_os import BoringOS
        from boring_os.adapters.bedrock import bedrock_handler
        
        client = BoringOS(api_key=os.environ["BORING_API_KEY"])
        
        @bedrock_handler(client)
        def lambda_handler(event, context):
            # The decorator handles everything
            pass
    """
    def decorator(func: Callable[..., Any]) -> Callable[..., dict[str, Any]]:
        @wraps(func)
        def wrapper(event: dict[str, Any], context: Any) -> dict[str, Any]:
            """Handle Bedrock agent request."""
            # Extract request details from Bedrock event
            action_group = event.get("actionGroup", "")
            api_path = event.get("apiPath", "")
            http_method = event.get("httpMethod", "POST")
            
            # Get tool name from path (remove leading slash)
            tool_name = api_path.lstrip("/")
            
            # Extract parameters from request body
            request_body = event.get("requestBody", {})
            content = request_body.get("content", {})
            json_content = content.get("application/json", {})
            properties = json_content.get("properties", [])
            
            # Convert properties array to dict
            params: dict[str, Any] = {}
            for prop in properties:
                name = prop.get("name")
                value = prop.get("value")
                if name and value is not None:
                    params[name] = value
            
            # Execute the tool
            try:
                result = client.tools.call(tool_name, **params)
                
                response_body = {
                    "application/json": {
                        "body": json.dumps(result) if return_format == "json" else str(result)
                    }
                }
                
                return {
                    "messageVersion": "1.0",
                    "response": {
                        "actionGroup": action_group,
                        "apiPath": api_path,
                        "httpMethod": http_method,
                        "httpStatusCode": 200,
                        "responseBody": response_body,
                    }
                }
                
            except Exception as e:
                error_body = {
                    "application/json": {
                        "body": json.dumps({"error": str(e)})
                    }
                }
                
                return {
                    "messageVersion": "1.0",
                    "response": {
                        "actionGroup": action_group,
                        "apiPath": api_path,
                        "httpMethod": http_method,
                        "httpStatusCode": 500,
                        "responseBody": error_body,
                    }
                }
        
        return wrapper
    return decorator


class BedrockFunctionResponse:
    """
    Helper class for building Bedrock agent function responses.
    
    Provides a cleaner API for returning results from Lambda handlers.
    """
    
    @staticmethod
    def success(
        action_group: str,
        api_path: str,
        result: Any,
        http_method: str = "POST",
    ) -> dict[str, Any]:
        """
        Build a success response for Bedrock.
        
        Args:
            action_group: Action group name
            api_path: API path
            result: Tool result data
            http_method: HTTP method
        
        Returns:
            Bedrock-formatted response
        """
        return {
            "messageVersion": "1.0",
            "response": {
                "actionGroup": action_group,
                "apiPath": api_path,
                "httpMethod": http_method,
                "httpStatusCode": 200,
                "responseBody": {
                    "application/json": {
                        "body": json.dumps(result)
                    }
                },
            }
        }
    
    @staticmethod
    def error(
        action_group: str,
        api_path: str,
        error_message: str,
        http_method: str = "POST",
        status_code: int = 500,
    ) -> dict[str, Any]:
        """
        Build an error response for Bedrock.
        
        Args:
            action_group: Action group name
            api_path: API path
            error_message: Error message
            http_method: HTTP method
            status_code: HTTP status code
        
        Returns:
            Bedrock-formatted error response
        """
        return {
            "messageVersion": "1.0",
            "response": {
                "actionGroup": action_group,
                "apiPath": api_path,
                "httpMethod": http_method,
                "httpStatusCode": status_code,
                "responseBody": {
                    "application/json": {
                        "body": json.dumps({"error": error_message})
                    }
                },
            }
        }
