# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
# methodDiscovery.py
# Method discovery and management for workflow execution

import logging
import importlib
import pkgutil
import inspect
from typing import Any, Dict, List
from modules.workflows.methods.methodBase import MethodBase

# Set up logger
logger = logging.getLogger(__name__)

# Global methods catalog - moved from serviceCenter
methods = {}

def discoverMethods(serviceCenter):
    """Dynamically discover all method classes and their actions in modules methods package.
    
    CRITICAL: If methods are already discovered, updates their Services reference to ensure
    they use the current workflow (self.services.workflow). This prevents stale workflow IDs
    from being used when a new workflow starts.
    """
    try:
        # Import the methods package
        methodsPackage = importlib.import_module('modules.workflows.methods')
        
        # Discover all modules and packages in the methods package
        for _, name, isPkg in pkgutil.iter_modules(methodsPackage.__path__):
            if name.startswith('method'):
                try:
                    if isPkg:
                        # Package (folder) - import __init__.py which exports the Method class
                        module = importlib.import_module(f'modules.workflows.methods.{name}')
                    else:
                        # Module (file) - import directly
                        module = importlib.import_module(f'modules.workflows.methods.{name}')
                    
                    # Find all classes in the module that inherit from MethodBase
                    for itemName, item in inspect.getmembers(module):
                        if (inspect.isclass(item) and 
                            issubclass(item, MethodBase) and 
                            item != MethodBase):
                            
                            # Check if method already exists in cache
                            shortName = itemName.replace('Method', '').lower()
                            if itemName in methods or shortName in methods:
                                # Method already discovered - update Services reference to use current workflow
                                existingMethodInfo = methods.get(itemName) or methods.get(shortName)
                                if existingMethodInfo and existingMethodInfo.get('instance'):
                                    existingMethodInfo['instance'].services = serviceCenter
                                    logger.debug(f"Updated Services reference for cached method {itemName} to use current workflow")
                                else:
                                    # Method exists but instance is missing - recreate it
                                    methodInstance = item(serviceCenter)
                                    actions = methodInstance.actions
                                    methodInfo = {
                                        'instance': methodInstance,
                                        'actions': actions,
                                        'description': item.__doc__ or f"Method {itemName}"
                                    }
                                    methods[itemName] = methodInfo
                                    methods[shortName] = methodInfo
                                    logger.info(f"Recreated method {itemName} (short: {shortName}) with {len(actions)} actions")
                            else:
                                # Method not discovered yet - create new instance
                                methodInstance = item(serviceCenter)
                                
                                # Use the actions property from MethodBase which handles WorkflowActionDefinition
                                actions = methodInstance.actions
                                
                                # Create method info
                                methodInfo = {
                                    'instance': methodInstance,
                                    'actions': actions,
                                    'description': item.__doc__ or f"Method {itemName}"
                                }
                                
                                # Store the method with full class name
                                methods[itemName] = methodInfo
                                
                                # Also store with short name for action executor access
                                methods[shortName] = methodInfo
                                
                                logger.info(f"Discovered method {itemName} (short: {shortName}) with {len(actions)} actions")
                            
                except Exception as e:
                    logger.error(f"Error discovering method {name}: {str(e)}")
                    continue
                    
        logger.info(f"Discovered/updated {len(methods)} method entries total")
        
    except Exception as e:
        logger.error(f"Error discovering methods: {str(e)}")

def getMethodsList(serviceCenter):
    """Get a list of available methods with their signatures"""
    if not methods:
        discoverMethods(serviceCenter)
    
    methodsList = []
    for methodName, methodInfo in methods.items():
        methodDescription = methodInfo['description']
        actionsList = []
        
        for actionName, actionInfo in methodInfo['actions'].items():
            actionDescription = actionInfo['description']
            parameters = actionInfo['parameters']
            
            # Build parameter signature
            paramSig = []
            for paramName, paramInfo in parameters.items():
                paramType = paramInfo['type']
                paramRequired = paramInfo['required']
                paramDefault = paramInfo['default']
                
                if paramRequired:
                    paramSig.append(f"{paramName}: {paramType}")
                else:
                    defaultStr = f" = {paramDefault}" if paramDefault is not None else " = None"
                    paramSig.append(f"{paramName}: {paramType}{defaultStr}")
            
            paramSignature = f"({', '.join(paramSig)})" if paramSig else "()"
            actionsList.append(f"- {actionName}{paramSignature}: {actionDescription}")
        
        actionsStr = "\n".join(actionsList)
        methodsList.append(f"**{methodName}**: {methodDescription}\n{actionsStr}")
    
    return "\n\n".join(methodsList)

def getActionParameterList(methodName: str, actionName: str, methods: Dict[str, Any]) -> str:
    """Get action parameter list from WorkflowActionParameter structure for AI parameter generation (list only)."""
    try:
        if not methods or methodName not in methods:
            return ""
        
        methodInstance = methods[methodName]['instance']
        if actionName not in methodInstance.actions:
            return ""
        
        action_info = methodInstance.actions[actionName]
        # Use structured WorkflowActionParameter objects from new system
        parameters = action_info.get('parameters', {})
        
        param_list = []
        for paramName, paramInfo in parameters.items():
            paramType = paramInfo.get('type', 'Any')
            paramDesc = paramInfo.get('description', '')
            paramRequired = paramInfo.get('required', False)
            
            # Format: paramName (type, required/optional): description
            reqText = "required" if paramRequired else "optional"
            if paramDesc:
                param_list.append(f"- {paramName} ({paramType}, {reqText}): {paramDesc}")
            else:
                param_list.append(f"- {paramName} ({paramType}, {reqText})")
        
        # Return list only, without leading headings or trailing text
        return "\n".join(param_list)
    except Exception as e:
        logger.error(f"Error getting action parameter signature for {methodName}.{actionName}: {str(e)}")
        return ""

