# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Workflow routes for the backend API.
Implements the endpoints for workflow management according to the state machine.
"""

import logging
import json
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, HTTPException, Depends, Body, Path, Query, Response, status, Request

# Import auth modules
from modules.auth import limiter, getCurrentUser
from modules.auth.authentication import getRequestContext, RequestContext

# Import interfaces from feature containers
import modules.interfaces.interfaceDbChat as interfaceDbChat
from modules.interfaces.interfaceDbChat import getInterface
from modules.interfaces.interfaceRbac import getRecordsetWithRBAC

# Import models from feature containers
from modules.datamodels.datamodelChat import (
    ChatWorkflow,
    ChatMessage,
    ChatLog,
    ChatStat,
    ChatDocument
)
from modules.shared.attributeUtils import getModelAttributeDefinitions, AttributeResponse
from modules.datamodels.datamodelUam import User
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata, normalize_pagination_dict


# Configure logger
logger = logging.getLogger(__name__)

# Model attributes for ChatWorkflow
workflowAttributes = getModelAttributeDefinitions(ChatWorkflow)

# Create router for workflow endpoints
router = APIRouter(
    prefix="/api/workflows",
    tags=["Workflow"],
    responses={404: {"description": "Not found"}}
)

def getServiceChat(ctx: RequestContext):
    # Workflows are not feature-instance-specific — only mandateId is needed for RBAC.
    # Passing featureInstanceId would add a SQL WHERE filter that excludes workflows
    # created by other features (e.g., automation vs chatbot).
    return interfaceDbChat.getInterface(ctx.user, mandateId=ctx.mandateId)

# Consolidated endpoint for getting all workflows
@router.get("/", response_model=PaginatedResponse[ChatWorkflow])
@limiter.limit("120/minute")
def get_workflows(
    request: Request,
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    ctx: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[ChatWorkflow]:
    """
    Get workflows with optional pagination, sorting, and filtering.
    
    Query Parameters:
    - pagination: JSON-encoded PaginationParams object, or None for no pagination
    
    Examples:
    - GET /api/workflows/ (no pagination - returns all workflows)
    - GET /api/workflows/?pagination={"page":1,"pageSize":10,"sort":[]}
    """
    try:
        # Parse pagination parameter
        paginationParams = None
        if pagination:
            try:
                paginationDict = json.loads(pagination)
                if paginationDict:
                    # Normalize pagination dict (handles top-level "search" field)
                    paginationDict = normalize_pagination_dict(paginationDict)
                    paginationParams = PaginationParams(**paginationDict)
            except (json.JSONDecodeError, ValueError) as e:
                raise HTTPException(
                    status_code=400,
                    detail=f"Invalid pagination parameter: {str(e)}"
                )
        
        appInterface = getServiceChat(ctx)
        result = appInterface.getWorkflows(pagination=paginationParams)
        
        # If pagination was requested, result is PaginatedResult with items as dicts
        # If no pagination, result is List[Dict]
        if paginationParams:
            workflows = result.items
            totalItems = result.totalItems
            totalPages = result.totalPages
        else:
            workflows = result
            totalItems = len(result)
            totalPages = 1
        
        if paginationParams:
            return PaginatedResponse(
                items=workflows,
                pagination=PaginationMetadata(
                    currentPage=paginationParams.page,
                    pageSize=paginationParams.pageSize,
                    totalItems=totalItems,
                    totalPages=totalPages,
                    sort=paginationParams.sort,
                    filters=paginationParams.filters
                )
            )
        else:
            return PaginatedResponse(
                items=workflows,
                pagination=None
            )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error getting workflows: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get workflows: {str(e)}"
        )

@router.get("/{workflowId}", response_model=ChatWorkflow)
@limiter.limit("120/minute")
def get_workflow(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow"),
    ctx: RequestContext = Depends(getRequestContext)
) -> ChatWorkflow:
    """Get workflow by ID"""
    try:
        # Get workflow interface with current user context
        workflowInterface = getServiceChat(ctx)
        
        # Get workflow
        workflow = workflowInterface.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Workflow not found"
            )
            
        return workflow
        
    except Exception as e:
        logger.error(f"Error getting workflow: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get workflow: {str(e)}"
        )

@router.put("/{workflowId}", response_model=ChatWorkflow)
@limiter.limit("120/minute")
def update_workflow(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow to update"),
    workflowData: Dict[str, Any] = Body(...),
    ctx: RequestContext = Depends(getRequestContext)
) -> ChatWorkflow:
    """Update workflow by ID"""
    try:
        # Get workflow interface with current user context
        workflowInterface = getServiceChat(ctx)
        
        # Get workflow using interface method to check permissions
        workflow = workflowInterface.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Workflow not found"
            )
        
        # Check if user has permission to update using RBAC
        if not workflowInterface.checkRbacPermission(ChatWorkflow, "update", workflowId):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="You don't have permission to update this workflow"
            )
        
        # Update workflow
        updatedWorkflow = workflowInterface.updateWorkflow(workflowId, workflowData)
        if not updatedWorkflow:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to update workflow"
            )
            
        return updatedWorkflow
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error updating workflow: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to update workflow: {str(e)}"
        )

# API Endpoint for workflow status
@router.get("/{workflowId}/status", response_model=ChatWorkflow)
@limiter.limit("120/minute")
def get_workflow_status(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow"),
    ctx: RequestContext = Depends(getRequestContext)
) -> ChatWorkflow:
    """Get the current status of a workflow."""
    try:
        # Get service center
        interfaceDbChat = getServiceChat(ctx)
        
        # Retrieve workflow
        workflow = interfaceDbChat.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
        
        return workflow
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error getting workflow status: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error getting workflow status: {str(e)}"
        )


# API Endpoint for stopping a workflow
@router.post("/{workflowId}/stop", response_model=ChatWorkflow)
@limiter.limit("120/minute")
async def stop_workflow(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow to stop"),
    ctx: RequestContext = Depends(getRequestContext)
) -> ChatWorkflow:
    """
    Stop a running workflow.
    This is a general endpoint that can be used by any feature to stop a workflow.
    """
    try:
        from modules.workflows.automation import chatStop
        
        # Get the workflow first to get mandateId
        interfaceChatDb = getServiceChat(ctx)
        workflow = interfaceChatDb.getWorkflow(workflowId)
        
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
        
        mandateId = workflow.get("mandateId") if isinstance(workflow, dict) else getattr(workflow, "mandateId", None)
        
        # Stop the workflow
        stoppedWorkflow = await chatStop(ctx.user, workflowId, mandateId=mandateId)
        
        return stoppedWorkflow
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error stopping workflow: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error stopping workflow: {str(e)}"
        )


# API Endpoint for workflow logs with selective data transfer
@router.get("/{workflowId}/logs", response_model=PaginatedResponse[ChatLog])
@limiter.limit("120/minute")
def get_workflow_logs(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow"),
    logId: Optional[str] = Query(None, description="Optional log ID to get only newer logs (legacy selective data transfer)"),
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    ctx: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[ChatLog]:
    """
    Get logs for a workflow with optional pagination, sorting, and filtering.
    Also supports legacy selective data transfer via logId parameter.
    
    Query Parameters:
    - logId: Optional log ID for selective data transfer (returns only logs after this ID)
    - pagination: JSON-encoded PaginationParams object, or None for no pagination
    """
    try:
        # Parse pagination parameter
        paginationParams = None
        if pagination:
            try:
                paginationDict = json.loads(pagination)
                if paginationDict:
                    # Normalize pagination dict (handles top-level "search" field)
                    paginationDict = normalize_pagination_dict(paginationDict)
                    paginationParams = PaginationParams(**paginationDict)
            except (json.JSONDecodeError, ValueError) as e:
                raise HTTPException(
                    status_code=400,
                    detail=f"Invalid pagination parameter: {str(e)}"
                )
        
        # Get service center
        interfaceDbChat = getServiceChat(ctx)
        
        # Verify workflow exists
        workflow = interfaceDbChat.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
        
        # Get logs with optional pagination
        result = interfaceDbChat.getLogs(workflowId, pagination=paginationParams)
        
        # Handle legacy selective data transfer if logId is provided (takes precedence over pagination)
        if logId:
            # If pagination was requested, result is PaginatedResult, otherwise List[ChatLog]
            allLogs = result.items if paginationParams else result
            
            # Find the index of the log with the given ID
            logIndex = next((i for i, log in enumerate(allLogs) if log.id == logId), -1)
            if logIndex >= 0:
                # Return only logs after the specified log
                filteredLogs = allLogs[logIndex + 1:]
                return PaginatedResponse(
                    items=filteredLogs,
                    pagination=None
                )
        
        # If pagination was requested, result is PaginatedResult
        # If no pagination, result is List[ChatLog]
        if paginationParams:
            return PaginatedResponse(
                items=result.items,
                pagination=PaginationMetadata(
                    currentPage=paginationParams.page,
                    pageSize=paginationParams.pageSize,
                    totalItems=result.totalItems,
                    totalPages=result.totalPages,
                    sort=paginationParams.sort,
                    filters=paginationParams.filters
                )
            )
        else:
            return PaginatedResponse(
                items=result,
                pagination=None
            )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error getting workflow logs: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error getting workflow logs: {str(e)}"
        )

# API Endpoint for workflow messages with selective data transfer
@router.get("/{workflowId}/messages", response_model=PaginatedResponse[ChatMessage])
@limiter.limit("120/minute")
def get_workflow_messages(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow"),
    messageId: Optional[str] = Query(None, description="Optional message ID to get only newer messages (legacy selective data transfer)"),
    pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
    ctx: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[ChatMessage]:
    """
    Get messages for a workflow with optional pagination, sorting, and filtering.
    Also supports legacy selective data transfer via messageId parameter.
    
    Query Parameters:
    - messageId: Optional message ID for selective data transfer (returns only messages after this ID)
    - pagination: JSON-encoded PaginationParams object, or None for no pagination
    """
    try:
        # Parse pagination parameter
        paginationParams = None
        if pagination:
            try:
                paginationDict = json.loads(pagination)
                if paginationDict:
                    # Normalize pagination dict (handles top-level "search" field)
                    paginationDict = normalize_pagination_dict(paginationDict)
                    paginationParams = PaginationParams(**paginationDict)
            except (json.JSONDecodeError, ValueError) as e:
                raise HTTPException(
                    status_code=400,
                    detail=f"Invalid pagination parameter: {str(e)}"
                )
        
        # Get service center
        interfaceDbChat = getServiceChat(ctx)
        
        # Verify workflow exists
        workflow = interfaceDbChat.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
        
        # Get messages with optional pagination
        result = interfaceDbChat.getMessages(workflowId, pagination=paginationParams)
        
        # Handle legacy selective data transfer if messageId is provided (takes precedence over pagination)
        if messageId:
            # If pagination was requested, result is PaginatedResult, otherwise List[ChatMessage]
            allMessages = result.items if paginationParams else result
            
            # Find the index of the message with the given ID
            messageIndex = next((i for i, msg in enumerate(allMessages) if msg.id == messageId), -1)
            if messageIndex >= 0:
                # Return only messages after the specified message
                filteredMessages = allMessages[messageIndex + 1:]
                return PaginatedResponse(
                    items=filteredMessages,
                    pagination=None
                )
        
        # If pagination was requested, result is PaginatedResult
        # If no pagination, result is List[ChatMessage]
        if paginationParams:
            return PaginatedResponse(
                items=result.items,
                pagination=PaginationMetadata(
                    currentPage=paginationParams.page,
                    pageSize=paginationParams.pageSize,
                    totalItems=result.totalItems,
                    totalPages=result.totalPages,
                    sort=paginationParams.sort,
                    filters=paginationParams.filters
                )
            )
        else:
            return PaginatedResponse(
                items=result,
                pagination=None
            )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error getting workflow messages: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error getting workflow messages: {str(e)}"
        )


# State 11: Workflow Reset/Deletion endpoint
@router.delete("/{workflowId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
def delete_workflow(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow to delete"),
    ctx: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Deletes a workflow and its associated data."""
    try:
        # Get service center
        interfaceDbChat = getServiceChat(ctx)
        
        # Check workflow access and permission using RBAC
        workflows = getRecordsetWithRBAC(
            interfaceDbChat.db,
            ChatWorkflow,
            ctx.user,
            recordFilter={"id": workflowId},
            mandateId=ctx.mandateId
        )
        if not workflows:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
        
        workflow_data = workflows[0]
        
        # Check if user has permission to delete using RBAC
        if not interfaceDbChat.checkRbacPermission(ChatWorkflow, "delete", workflowId):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="You don't have permission to delete this workflow"
            )
        
        # Delete workflow
        success = interfaceDbChat.deleteWorkflow(workflowId)
        
        if not success:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to delete workflow"
            )
        
        return {
            "id": workflowId,
            "message": "Workflow and associated data deleted successfully"
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error deleting workflow: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error deleting workflow: {str(e)}"
        )


# Document Management Endpoints

@router.delete("/{workflowId}/messages/{messageId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
def delete_workflow_message(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow"),
    messageId: str = Path(..., description="ID of the message to delete"),
    ctx: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a message from a workflow."""
    try:
        # Get service center
        interfaceDbChat = getServiceChat(ctx)
        
        # Verify workflow exists
        workflow = interfaceDbChat.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
                
        # Delete the message
        success = interfaceDbChat.deleteMessage(workflowId, messageId)
        
        if not success:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Message with ID {messageId} not found in workflow {workflowId}"
            )
        
        # Messages are stored in ChatMessage table linked by workflowId --
        # no messageIds list on ChatWorkflow to update.
        
        return {
            "workflowId": workflowId,
            "messageId": messageId,
            "message": "Message deleted successfully"
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error deleting message: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error deleting message: {str(e)}"
        )

@router.delete("/{workflowId}/messages/{messageId}/files/{fileId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
def delete_file_from_message(
    request: Request,
    workflowId: str = Path(..., description="ID of the workflow"),
    messageId: str = Path(..., description="ID of the message"),
    fileId: str = Path(..., description="ID of the file to delete"),
    ctx: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
    """Delete a file reference from a message in a workflow."""
    try:
        # Get service center
        interfaceDbChat = getServiceChat(ctx)
        
        # Verify workflow exists
        workflow = interfaceDbChat.getWorkflow(workflowId)
        if not workflow:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Workflow with ID {workflowId} not found"
            )
                
        # Delete file reference from message
        success = interfaceDbChat.deleteFileFromMessage(workflowId, messageId, fileId)
        
        if not success:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"File with ID {fileId} not found in message {messageId}"
            )
        
        return {
            "workflowId": workflowId,
            "messageId": messageId,
            "fileId": fileId,
            "message": "File reference deleted successfully"
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error deleting file reference: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error deleting file reference: {str(e)}"
        )


# Action Discovery Endpoints

@router.get("/actions", response_model=Dict[str, Any])
@limiter.limit("120/minute")
def get_all_actions(
    request: Request,
    currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
    """
    Get all available workflow actions for the current user (filtered by RBAC).
    
    Returns:
    - Dictionary with actions grouped by module, filtered by RBAC permissions
    
    Example response:
    {
        "actions": [
            {
                "module": "outlook",
                "actionId": "outlook.readEmails",
                "name": "readEmails",
                "description": "Read emails and metadata from a mailbox folder",
                "parameters": {...}
            },
            ...
        ]
    }
    """
    try:
        from modules.services import getInterface as getServices
        from modules.workflows.processing.shared.methodDiscovery import discoverMethods
        
        # Get services and discover methods
        services = getServices(currentUser, None)
        discoverMethods(services)
        
        # Import methods catalog
        from modules.workflows.processing.shared.methodDiscovery import methods
        
        # Collect all actions from all methods
        allActions = []
        for methodName, methodInfo in methods.items():
            # Skip duplicate entries (same method stored with full and short name)
            if methodName.startswith('Method'):
                continue
                
            methodInstance = methodInfo['instance']
            methodActions = methodInstance.actions
            
            for actionName, actionInfo in methodActions.items():
                # Build action response
                actionResponse = {
                    "module": methodInstance.name,
                    "actionId": f"{methodInstance.name}.{actionName}",
                    "name": actionName,
                    "description": actionInfo.get('description', ''),
                    "parameters": actionInfo.get('parameters', {})
                }
                allActions.append(actionResponse)
        
        return {
            "actions": allActions
        }
        
    except Exception as e:
        logger.error(f"Error getting all actions: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get actions: {str(e)}"
        )


@router.get("/actions/{method}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
def get_method_actions(
    request: Request,
    method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
    currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
    """
    Get all available actions for a specific method (filtered by RBAC).
    
    Path Parameters:
    - method: Method name (e.g., 'outlook', 'sharepoint', 'ai')
    
    Returns:
    - Dictionary with actions for the specified method
    
    Example response:
    {
        "module": "outlook",
        "actions": [
            {
                "actionId": "outlook.readEmails",
                "name": "readEmails",
                "description": "Read emails and metadata from a mailbox folder",
                "parameters": {...}
            },
            ...
        ]
    }
    """
    try:
        from modules.services import getInterface as getServices
        from modules.workflows.processing.shared.methodDiscovery import discoverMethods
        
        # Get services and discover methods
        services = getServices(currentUser, None)
        discoverMethods(services)
        
        # Import methods catalog
        from modules.workflows.processing.shared.methodDiscovery import methods
        
        # Find method instance
        methodInstance = None
        for methodName, methodInfo in methods.items():
            if methodInfo['instance'].name == method:
                methodInstance = methodInfo['instance']
                break
        
        if not methodInstance:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Method '{method}' not found"
            )
        
        # Collect actions for this method
        actions = []
        methodActions = methodInstance.actions
        
        for actionName, actionInfo in methodActions.items():
            actionResponse = {
                "actionId": f"{methodInstance.name}.{actionName}",
                "name": actionName,
                "description": actionInfo.get('description', ''),
                "parameters": actionInfo.get('parameters', {})
            }
            actions.append(actionResponse)
        
        return {
            "module": methodInstance.name,
            "description": methodInstance.description,
            "actions": actions
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error getting actions for method {method}: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get actions for method {method}: {str(e)}"
        )


@router.get("/actions/{method}/{action}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
def get_action_schema(
    request: Request,
    method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
    action: str = Path(..., description="Action name (e.g., 'readEmails', 'uploadDocument')"),
    currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
    """
    Get action schema with parameter definitions for a specific action.
    
    Path Parameters:
    - method: Method name (e.g., 'outlook', 'sharepoint', 'ai')
    - action: Action name (e.g., 'readEmails', 'uploadDocument')
    
    Returns:
    - Action schema with full parameter definitions
    
    Example response:
    {
        "method": "outlook",
        "action": "readEmails",
        "actionId": "outlook.readEmails",
        "description": "Read emails and metadata from a mailbox folder",
        "parameters": {
            "connectionReference": {
                "name": "connectionReference",
                "type": "str",
                "frontendType": "userConnection",
                "frontendOptions": "user.connection",
                "required": true,
                "description": "Microsoft connection label"
            },
            ...
        }
    }
    """
    try:
        from modules.services import getInterface as getServices
        from modules.workflows.processing.shared.methodDiscovery import discoverMethods
        
        # Get services and discover methods
        services = getServices(currentUser, None)
        discoverMethods(services)
        
        # Import methods catalog
        from modules.workflows.processing.shared.methodDiscovery import methods
        
        # Find method instance
        methodInstance = None
        for methodName, methodInfo in methods.items():
            if methodInfo['instance'].name == method:
                methodInstance = methodInfo['instance']
                break
        
        if not methodInstance:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Method '{method}' not found"
            )
        
        # Get action
        methodActions = methodInstance.actions
        if action not in methodActions:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Action '{action}' not found in method '{method}'"
            )
        
        actionInfo = methodActions[action]
        
        return {
            "method": methodInstance.name,
            "action": action,
            "actionId": f"{methodInstance.name}.{action}",
            "description": actionInfo.get('description', ''),
            "parameters": actionInfo.get('parameters', {})
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error getting action schema for {method}.{action}: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to get action schema: {str(e)}"
        )