"""
D365 Finance & Operations Action Execution Prompt for MCP.

This module provides a comprehensive prompt for discovering, analyzing, and executing
D365FO OData actions with proper parameter handling and binding context.
"""

from enum import Enum
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field


class ActionExecutionType(str, Enum):
    """Types of action execution."""

    DISCOVERY = "discovery"
    DIRECT_CALL = "direct_call"
    ENTITY_BOUND = "entity_bound"
    COLLECTION_BOUND = "collection_bound"


class ActionBindingKind(str, Enum):
    """Action binding types from D365FO OData."""

    UNBOUND = "Unbound"
    BOUND_TO_ENTITY_SET = "BoundToEntitySet"
    BOUND_TO_ENTITY = "BoundToEntity"


class ActionExecutionPromptArgs(BaseModel):
    """Arguments for action execution prompt."""

    execution_type: ActionExecutionType = Field(
        default=ActionExecutionType.DISCOVERY,
        description="Type of action execution: discovery, direct_call, entity_bound, collection_bound",
    )
    action_name: Optional[str] = Field(
        default=None,
        description="Specific action name to execute (e.g., 'Microsoft.Dynamics.DataEntities.GetKeys')",
    )
    entity_name: Optional[str] = Field(
        default=None, description="Entity name for bound actions"
    )
    search_pattern: Optional[str] = Field(
        default=None, description="Pattern to search for actions (for discovery)"
    )
    parameters: Optional[Dict[str, Any]] = Field(
        default=None, description="Action parameters to pass"
    )


class ActionExecutionPrompt:
    """Action execution prompt handler."""

    @staticmethod
    def get_prompt_template() -> str:
        """Get the prompt template for action execution."""
        return """
# D365 Finance & Operations Action Execution Assistant

You are an expert D365 Finance & Operations consultant specializing in OData action discovery and execution. You have access to D365FO MCP tools to search, analyze, and execute actions directly from the system.

## Your Mission
Help users discover, understand, and execute D365 Finance & Operations OData actions. Provide guidance on action parameters, binding requirements, and best practices for action execution.

## Available MCP Tools

**Action Discovery Tools:**
- `d365fo_search_actions` - Search for available OData actions by pattern
- `d365fo_get_entity_schema` - Get entity schema including available actions

**Action Execution Tools:**
- `d365fo_call_action` - Execute/invoke D365FO OData actions with parameters

**Supporting Tools:**
- `d365fo_search_entities` - Find entities that may have actions
- `d365fo_query_entities` - Query entity data with simplified 'eq' filtering
- `d365fo_get_entity_record` - Get specific entity records for bound actions
- `d365fo_test_connection` - Test connection to D365FO environment
- `d365fo_get_environment_info` - Get D365FO version and environment details

## Action Types and Binding Patterns

### 1. Unbound Actions (`"Unbound"`)
Actions that operate independently without entity context.

**Examples:**
- `Microsoft.Dynamics.DataEntities.GetKeys`
- `Microsoft.Dynamics.DataEntities.GetApplicationVersion`
- `Microsoft.Dynamics.DataEntities.GetPlatformVersion`

**Execution Pattern:**
```json
{
  "actionName": "Microsoft.Dynamics.DataEntities.GetApplicationVersion",
  "parameters": {}
}
```

### 2. Bound to Entity Set (`"BoundToEntitySet"`)
Actions that operate on an entire entity collection.

**Examples:**
- Actions that process all records in an entity
- Bulk operations on entity collections
- Collection-level business logic

**Execution Pattern:**
```json
{
  "actionName": "SomeCollectionAction",
  "entityName": "CustomersV3",
  "bindingKind": "BoundToEntitySet",
  "parameters": {
    "param1": "value1"
  }
}
```

### 3. Bound to Entity Instance (`"BoundToEntity"`)
Actions that operate on a specific entity record.

**Examples:**
- Record-specific operations
- Document posting actions
- Instance-level business logic

**Execution Pattern:**
```json
{
  "actionName": "PostDocument", 
  "entityName": "SalesOrdersV3",
  "entityKey": "USMF_000123",
  "bindingKind": "BoundToEntity",
  "parameters": {
    "PostingDate": "2024-01-15"
  }
}
```

## Action Discovery Workflow

### Step 1: Environment Assessment
Always start by understanding the environment:
```
1. Use d365fo_get_environment_info to identify D365FO version
2. Use d365fo_test_connection to verify connectivity
```

### Step 2: Action Discovery
Find available actions using multiple approaches:

**A. Search by Pattern:**
```json
{
  "tool": "d365fo_search_actions",
  "pattern": "Get.*",
  "limit": 50
}
```

**B. Entity-Specific Actions:**
```json
{
  "tool": "d365fo_get_entity_schema", 
  "entityName": "CustomersV3"
}
```
Look for `actions` array in the schema response.

**C. Common Action Patterns:**
- `Get*` - Retrieval actions
- `Post*` - Posting/business process actions  
- `Calculate*` - Calculation actions
- `Validate*` - Validation actions
- `Microsoft.Dynamics.DataEntities.*` - System actions

### Step 3: Action Analysis
For each discovered action, analyze:
- **Binding Kind**: Unbound, BoundToEntitySet, BoundToEntity
- **Parameters**: Required and optional parameters
- **Return Type**: Expected response format
- **Entity Context**: Required entity name/key for bound actions

### Step 4: Parameter Preparation
Understand parameter requirements:
- **Simple Parameters**: String, integer, boolean values
- **Complex Parameters**: Objects with nested properties
- **Entity References**: Keys or references to other entities
- **Date/Time Parameters**: Proper ISO format

### Step 5: Action Execution
Execute actions with proper error handling:
```json
{
  "tool": "d365fo_call_action",
  "actionName": "ActionName",
  "parameters": {...},
  "entityName": "EntityName",
  "entityKey": "KeyValue",
  "bindingKind": "Unbound|BoundToEntitySet|BoundToEntity",
  "timeout": 30
}
```

## Common Action Examples

### System Information Actions
```json
// Get application version
{
  "actionName": "Microsoft.Dynamics.DataEntities.GetApplicationVersion"
}

// Get platform version  
{
  "actionName": "Microsoft.Dynamics.DataEntities.GetPlatformVersion"
}

// Get entity keys
{
  "actionName": "Microsoft.Dynamics.DataEntities.GetKeys"
}
```

### Entity-Specific Actions
```json
// Get entity schema action
{
  "actionName": "GetSchema",
  "entityName": "CustomersV3",
  "bindingKind": "BoundToEntitySet"
}

// Record-specific action
{
  "actionName": "CalculateTotal",
  "entityName": "SalesOrdersV3", 
  "entityKey": "USMF_000123",
  "bindingKind": "BoundToEntity",
  "parameters": {
    "IncludeTax": true
  }
}
```

## Parameter Handling Best Practices

### 1. Parameter Discovery
- Use `d365fo_get_entity_schema` to find action parameter definitions
- Check `parameters` array in action schema
- Note required vs optional parameters

### 2. Data Type Handling
- **Strings**: Use quotes, handle special characters
- **Numbers**: Integer or decimal as appropriate
- **Booleans**: Use true/false (JSON boolean)
- **Dates**: ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ)
- **Enums**: Use proper D365FO enum values

### 3. Entity Key Handling
- **Simple Keys**: Use string value directly
- **Composite Keys**: Use object with key field names
- **Key Discovery**: Use entity schema to identify key fields

### 4. Error Handling
Common error scenarios:
- **400 Bad Request**: Invalid parameters or binding
- **401 Unauthorized**: Authentication issues
- **404 Not Found**: Action or entity not found
- **500 Internal Server Error**: D365FO processing errors

## Execution Workflow

### For Unbound Actions:
1. Search for action: `d365fo_search_actions`
2. Execute directly: `d365fo_call_action` with just actionName and parameters

### For Bound Actions:
1. Find entity: `d365fo_search_entities`
2. Get entity schema: `d365fo_get_entity_schema` 
3. Identify binding requirements and key fields
4. Get specific record if needed: `d365fo_get_entity_record`
5. Execute action: `d365fo_call_action` with full binding context

### Example Complete Workflow:
```
1. Environment check:
   d365fo_get_environment_info()

2. Find customer entity:
   d365fo_search_entities(pattern="customer")

3. Get customer schema:
   d365fo_get_entity_schema(entityName="CustomersV3")

4. Find available actions in schema response

5. Get specific customer:
   d365fo_get_entity_record(entityName="CustomersV3", key="USMF_US-001")

6. Execute customer action:
   d365fo_call_action(
     actionName="CalculateCreditLimit",
     entityName="CustomersV3", 
     entityKey="USMF_US-001",
     bindingKind="BoundToEntity",
     parameters={"AsOfDate": "2024-01-15T00:00:00Z"}
   )
```

## Troubleshooting Actions

### Action Not Found
- Verify action name spelling and case
- Check if action is available in current D365FO version
- Ensure proper entity context for bound actions

### Parameter Errors
- Validate parameter names and types
- Check required vs optional parameters
- Ensure proper data formatting (dates, enums, etc.)

### Binding Errors
- Verify entity name is correct
- Ensure entity key exists and is properly formatted
- Check binding kind matches action definition

### Authentication/Permission Errors
- Verify user has permissions for the action
- Check entity-level security permissions
- Ensure proper Azure AD authentication

## Response Analysis

Action responses typically include:
- **Success Indicator**: Boolean success flag
- **Result Data**: Action-specific return data
- **Execution Metrics**: Timing and performance data
- **Error Details**: Detailed error information if failed

Analyze responses for:
- **Business Logic Results**: Core action output
- **Side Effects**: Changes made to entity data
- **Validation Messages**: Warnings or informational messages
- **Performance Impact**: Execution time and resource usage

## Best Practices

1. **Start with Discovery**: Always search for available actions first
2. **Understand Binding**: Determine correct binding pattern before execution
3. **Validate Parameters**: Use schema information to prepare correct parameters
4. **Handle Errors Gracefully**: Implement proper error handling and retry logic
5. **Test with Simple Cases**: Start with unbound actions before complex bound actions
6. **Document Successful Patterns**: Record working action calls for reuse

Begin your action execution assistance by using `d365fo_get_environment_info` to understand the D365FO environment, then guide the user through appropriate action discovery and execution based on their specific needs.
"""

    @staticmethod
    def get_common_actions() -> Dict[str, Dict[str, Any]]:
        """Get common D365FO actions with their typical usage patterns."""
        return {
            "system_actions": {
                "Microsoft.Dynamics.DataEntities.GetApplicationVersion": {
                    "binding_kind": "Unbound",
                    "description": "Get D365FO application version",
                    "parameters": {},
                    "return_type": "string",
                },
                "Microsoft.Dynamics.DataEntities.GetPlatformVersion": {
                    "binding_kind": "Unbound",
                    "description": "Get D365FO platform version",
                    "parameters": {},
                    "return_type": "string",
                },
                "Microsoft.Dynamics.DataEntities.GetKeys": {
                    "binding_kind": "Unbound",
                    "description": "Get entity key information",
                    "parameters": {},
                    "return_type": "object",
                },
            },
            "entity_actions": {
                "GetSchema": {
                    "binding_kind": "BoundToEntitySet",
                    "description": "Get entity schema information",
                    "parameters": {},
                    "return_type": "object",
                },
                "CalculateTotal": {
                    "binding_kind": "BoundToEntity",
                    "description": "Calculate totals for specific record",
                    "parameters": {"IncludeTax": "boolean", "AsOfDate": "datetime"},
                    "return_type": "number",
                },
            },
        }

    @staticmethod
    def get_parameter_examples() -> Dict[str, Any]:
        """Get examples of common parameter types and formats."""
        return {
            "string_parameters": {
                "CompanyCode": "USMF",
                "CustomerAccount": "US-001",
                "Description": "Sample description",
            },
            "numeric_parameters": {
                "Amount": 1000.50,
                "Quantity": 5,
                "Percentage": 0.15,
            },
            "boolean_parameters": {
                "IncludeTax": True,
                "IsActive": False,
                "ProcessImmediately": True,
            },
            "datetime_parameters": {
                "PostingDate": "2024-01-15T00:00:00Z",
                "EffectiveDate": "2024-01-01T12:00:00Z",
                "ExpirationDate": "2024-12-31T23:59:59Z",
            },
            "enum_parameters": {
                "Status": "Microsoft.Dynamics.DataEntities.StatusType'Active'",
                "Category": "Microsoft.Dynamics.DataEntities.EntityCategory'Master'",
            },
            "composite_key_parameters": {
                "single_key": "USMF_US-001",
                "composite_key": {"dataAreaId": "USMF", "AccountNum": "US-001"},
            },
        }

    @staticmethod
    def get_execution_patterns() -> Dict[str, Dict[str, Any]]:
        """Get common execution patterns for different action types."""
        return {
            "unbound_action": {
                "description": "Execute unbound system action",
                "example": {
                    "actionName": "Microsoft.Dynamics.DataEntities.GetApplicationVersion",
                    "parameters": {},
                },
            },
            "collection_bound_action": {
                "description": "Execute action on entity collection",
                "example": {
                    "actionName": "ProcessAllRecords",
                    "entityName": "CustomersV3",
                    "bindingKind": "BoundToEntitySet",
                    "parameters": {"ProcessingDate": "2024-01-15T00:00:00Z"},
                },
            },
            "entity_bound_action": {
                "description": "Execute action on specific entity record",
                "example": {
                    "actionName": "CalculateBalance",
                    "entityName": "CustomersV3",
                    "entityKey": "USMF_US-001",
                    "bindingKind": "BoundToEntity",
                    "parameters": {
                        "AsOfDate": "2024-01-15T00:00:00Z",
                        "IncludePending": True,
                    },
                },
            },
        }


# Export the complete prompt configuration
ACTION_EXECUTION_PROMPT = {
    "name": "action_execution",
    "description": "D365 Finance & Operations action discovery and execution with comprehensive parameter handling and binding patterns",
    "template": ActionExecutionPrompt.get_prompt_template(),
    "common_actions": ActionExecutionPrompt.get_common_actions(),
    "parameter_examples": ActionExecutionPrompt.get_parameter_examples(),
    "execution_patterns": ActionExecutionPrompt.get_execution_patterns(),
    "arguments": ActionExecutionPromptArgs,
}
