"""
代码执行节点
"""
import concurrent.futures
import logging
import time
from typing import Any, Dict

from ..base import BaseNode, NodeContext, NodeResult
from ..registry import NodeRegistry

logger = logging.getLogger(__name__)


@NodeRegistry.register
class CodeNode(BaseNode):
    """
    代码执行节点
    
    执行 Python 代码片段
    """
    
    node_type = 'code'
    node_name = '代码'
    node_category = 'logic'
    node_icon = 'code'
    node_description = '执行 Python 代码片段'
    
    inputs = [
        {
            'name': 'inputs',
            'type': 'object',
            'description': '输入变量',
        },
    ]
    
    outputs = [
        {
            'name': 'result',
            'type': 'any',
            'description': '执行结果',
        },
    ]
    
    # 安全的内置函数白名单
    SAFE_BUILTINS = {
        'abs', 'all', 'any', 'bool', 'dict', 'enumerate', 'filter',
        'float', 'int', 'len', 'list', 'map', 'max', 'min', 'range',
        'round', 'set', 'sorted', 'str', 'sum', 'tuple', 'zip',
        'True', 'False', 'None',
    }
    
    def execute(self, context: NodeContext) -> NodeResult:
        """执行代码（线程池隔离，防止死循环卡死主流程）"""
        timeout = self.config.get('timeout', 30)
        
        with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
            future = executor.submit(self._execute_sync, context)
            try:
                return future.result(timeout=timeout)
            except concurrent.futures.TimeoutError:
                future.cancel()
                return NodeResult(
                    success=False,
                    error=f'代码执行超时（{timeout}秒），请检查是否存在死循环',
                    elapsed_time=timeout * 1000,
                )
    
    def _execute_sync(self, context: NodeContext) -> NodeResult:
        """同步执行代码（在子线程中运行）"""
        start_time = time.time()
        
        try:
            code = self.config.get('code', '')
            input_variables = self.config.get('inputs', []) or self.config.get('input_variables', [])
            output_variable = self.config.get('output_variable', 'result')
            
            if not code:
                return NodeResult(
                    success=False,
                    error='代码不能为空',
                )
            
            safe_globals = {
                '__builtins__': {k: getattr(__builtins__, k) if hasattr(__builtins__, k) else __builtins__[k] 
                                for k in self.SAFE_BUILTINS if hasattr(__builtins__, k) or k in __builtins__},
            }
            
            import json
            import re
            import math
            safe_globals['json'] = json
            safe_globals['re'] = re
            safe_globals['math'] = math
            
            local_vars = {}
            for var_config in input_variables:
                if isinstance(var_config, dict):
                    var_name = var_config.get('variable', '')
                    default_value = var_config.get('default_value', None)
                    if var_name:
                        value = context.get_variable(var_name)
                        if value is not None:
                            local_vars[var_name] = value
                        elif default_value:
                            local_vars[var_name] = context.resolve_template(str(default_value))
                        else:
                            local_vars[var_name] = None
                elif isinstance(var_config, str):
                    local_vars[var_config] = context.get_variable(var_config)
            
            local_vars['user_input'] = context.user_input
            local_vars['variables'] = context.variables.copy()
            
            exec(code, safe_globals, local_vars)
            
            if 'main' in local_vars and callable(local_vars['main']):
                inputs_dict = {
                    'user_input': context.user_input,
                    **context.variables,
                    **local_vars,
                }
                main_result = local_vars['main'](inputs_dict)
                if isinstance(main_result, dict):
                    result = main_result.get('result', main_result)
                else:
                    result = main_result
            else:
                result = local_vars.get('result', None)
            
            elapsed_time = int((time.time() - start_time) * 1000)
            
            return NodeResult(
                success=True,
                output=result,
                output_variables={output_variable: result},
                elapsed_time=elapsed_time,
            )
            
        except Exception as e:
            logger.exception(f'代码节点执行失败: {e}')
            elapsed_time = int((time.time() - start_time) * 1000)
            return NodeResult(
                success=False,
                error=str(e),
                elapsed_time=elapsed_time,
            )
    
    @classmethod
    def get_config_schema(cls) -> Dict[str, Any]:
        """获取配置 Schema"""
        return {
            'type': 'object',
            'properties': {
                'code': {
                    'type': 'string',
                    'title': '代码',
                    'description': 'Python 代码，结果存储在 result 变量中',
                    'format': 'code',
                    'default': '# 在这里编写代码\n# 可用变量: user_input, variables\n# 将结果赋值给 result\n\nresult = user_input.upper()',
                },
                'input_variables': {
                    'type': 'array',
                    'title': '输入变量',
                    'items': {'type': 'string'},
                    'description': '需要传入代码的变量名列表',
                },
                'output_variable': {
                    'type': 'string',
                    'title': '输出变量名',
                    'default': 'code_result',
                },
                'timeout': {
                    'type': 'integer',
                    'title': '超时时间（秒）',
                    'default': 30,
                    'minimum': 1,
                    'maximum': 300,
                },
            },
            'required': ['code'],
        }
