"""
节点基类
"""
import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


@dataclass
class NodeContext:
    """
    节点执行上下文
    
    包含节点执行所需的所有信息
    """
    # 工作流运行实例
    workflow_run_id: str = ''
    
    # 变量存储（所有节点共享）
    variables: Dict[str, Any] = field(default_factory=dict)
    
    # 用户输入
    user_input: str = ''
    
    # 当前用户
    user_id: str = ''
    
    # 对话历史（用于 LLM 节点）
    conversation_history: List[Dict[str, str]] = field(default_factory=list)
    
    # 节点配置
    node_config: Dict[str, Any] = field(default_factory=dict)
    
    # 上一个节点的输出
    previous_output: Any = None
    
    # 元数据
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    # 数据库会话（用于需要数据库访问的节点）
    db_session: Any = None
    
    def get_variable(self, name: str, default: Any = None) -> Any:
        """获取变量"""
        return self.variables.get(name, default)
    
    def set_variable(self, name: str, value: Any) -> None:
        """设置变量"""
        self.variables[name] = value
    
    def resolve_template(self, template: str) -> str:
        """
        解析模板中的变量引用
        
        支持格式：
        - {{variable_name}} - 直接变量引用
        - {{variable_name[0]}} - 数组索引访问
        - {{variable_name[-1]}} - 负数索引（最后一个）
        - {{NodeID.key}} - 节点输出引用（如果存在）
        - {{NodeID.key}}.property - 访问解析结果的嵌套属性
        - {{NodeID.key[0].property}} - 数组索引 + 属性访问
        """
        import re
        import json
        result = template
        
        # 匹配 {{...}} 格式的变量引用，以及可选的后续属性访问 .property1.property2...
        pattern = r'\{\{([^}]+)\}\}((?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)'
        
        def get_nested_value(obj, path: str):
            """从对象中获取嵌套属性值，支持数组索引"""
            if not path:
                return obj
            
            # 移除开头的点
            if path.startswith('.'):
                path = path[1:]
            
            current = obj
            # 使用正则分割路径，支持 .property 和 [index] 格式
            # 例如: "items[0].name" -> ["items", "[0]", "name"]
            parts = re.split(r'(?=\[)|\.', path)
            
            for part in parts:
                if not part:
                    continue
                    
                # 如果是字符串，尝试解析为 JSON
                if isinstance(current, str):
                    try:
                        current = json.loads(current)
                    except (json.JSONDecodeError, TypeError):
                        return None
                
                # 检查是否是数组索引 [n]
                index_match = re.match(r'\[(-?\d+)\]', part)
                if index_match:
                    index = int(index_match.group(1))
                    if isinstance(current, (list, tuple)):
                        try:
                            current = current[index]
                        except IndexError:
                            return None
                    else:
                        return None
                # 从字典中获取属性
                elif isinstance(current, dict):
                    if part in current:
                        current = current[part]
                    else:
                        return None
                else:
                    return None
            
            return current
        
        def replace_var(match):
            var_ref = match.group(1).strip()
            extra_path = match.group(2) or ''  # 额外的属性路径，如 .customer_name
            
            value = None
            index_path = ''  # 变量名后的索引/属性路径
            
            # 检查是否有数组索引 [n]，如果有则分割
            # 例如: "loop_results[0]" -> var_name="loop_results", index_path="[0]"
            # 例如: "llm-123.llm_response" -> var_name="llm-123.llm_response", index_path=""
            bracket_pos = var_ref.find('[')
            if bracket_pos > 0:
                var_name = var_ref[:bracket_pos]
                index_path = var_ref[bracket_pos:]
            else:
                var_name = var_ref
            
            # 尝试解析 NodeID.key.subkey... 格式（支持多层属性访问）
            if '.' in var_name:
                parts = var_name.split('.')
                node_id = parts[0]
                remaining_path = '.'.join(parts[1:])  # 剩余路径，如 "item.module_name"
                
                # 先尝试从节点输出命名空间获取
                node_outputs = self.variables.get(f'_node_{node_id}')
                if isinstance(node_outputs, dict):
                    # 尝试获取第一层 key
                    first_key = parts[1] if len(parts) > 1 else None
                    if first_key and first_key in node_outputs:
                        value = node_outputs[first_key]
                        # 如果还有更多层级，继续递归获取
                        if len(parts) > 2:
                            nested_path = '.'.join(parts[2:])
                            nested_value = get_nested_value(value, nested_path)
                            if nested_value is not None:
                                value = nested_value
                
                # 回退：尝试直接从变量获取（node_id 作为变量名）
                if value is None and node_id in self.variables:
                    node_data = self.variables[node_id]
                    if isinstance(node_data, dict):
                        first_key = parts[1] if len(parts) > 1 else None
                        if first_key and first_key in node_data:
                            value = node_data[first_key]
                            if len(parts) > 2:
                                nested_path = '.'.join(parts[2:])
                                nested_value = get_nested_value(value, nested_path)
                                if nested_value is not None:
                                    value = nested_value
                
                # 再回退：直接从顶层变量获取完整路径
                if value is None and remaining_path in self.variables:
                    value = self.variables[remaining_path]
            else:
                # 直接变量引用
                if var_name in self.variables:
                    value = self.variables[var_name]
            
            # 如果找到了值，处理索引路径和额外的属性路径
            if value is not None:
                # 合并索引路径和额外路径
                full_path = index_path + extra_path
                if full_path:
                    nested_value = get_nested_value(value, full_path)
                    if nested_value is not None:
                        return str(nested_value)
                    # 嵌套属性未找到，返回原始值
                    return str(value)
                return str(value)
            
            # 未找到变量，保持原样
            return match.group(0)
        
        result = re.sub(pattern, replace_var, result)
        return result


@dataclass
class NodeResult:
    """
    节点执行结果
    """
    # 是否成功
    success: bool = True
    
    # 输出数据
    output: Any = None
    
    # 输出变量（会合并到上下文的 variables 中）
    output_variables: Dict[str, Any] = field(default_factory=dict)
    
    # 错误信息
    error: str = ''
    
    # 下一个节点 ID（用于条件分支）
    next_node_id: str = ''
    
    # Token 使用（LLM 节点）
    tokens_used: int = 0
    
    # 耗时（毫秒）
    elapsed_time: int = 0
    
    # 元数据
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    # ========== 对话流相关 ==========
    
    # 是否等待用户输入（对话流模式）
    waiting_for_input: bool = False
    
    # 等待配置（描述需要什么类型的输入）
    waiting_config: Dict[str, Any] = field(default_factory=dict)
    
    # 事件列表（如发送消息）
    events: List[Dict[str, Any]] = field(default_factory=list)
    
    # ========== 设计预览相关 ==========
    
    # 设计预览数据（用于工作流中显示设计结果并允许编辑）
    preview: Optional[Dict[str, Any]] = None


class BaseNode(ABC):
    """
    节点基类
    
    所有节点必须继承此类并实现 execute 方法
    """
    
    # 节点类型标识（必须唯一）
    node_type: str = ''
    
    # 节点显示名称
    node_name: str = ''
    
    # 节点分类
    node_category: str = 'basic'  # basic, llm, logic, data, tool, knowledge
    
    # 节点图标
    node_icon: str = ''
    
    # 节点描述
    node_description: str = ''
    
    # 输入参数定义
    inputs: List[Dict[str, Any]] = []
    
    # 输出参数定义
    outputs: List[Dict[str, Any]] = []
    
    # 是否支持多个输出分支
    supports_branches: bool = False
    
    def __init__(self, config: Dict[str, Any] = None):
        """
        初始化节点
        
        Args:
            config: 节点配置
        """
        self.config = config or {}
    
    @abstractmethod
    def execute(self, context: NodeContext) -> NodeResult:
        """
        执行节点
        
        Args:
            context: 执行上下文
            
        Returns:
            NodeResult
        """
        pass
    
    async def execute_async(self, context: NodeContext) -> NodeResult:
        """
        异步执行节点（默认调用同步方法）
        
        Args:
            context: 执行上下文
            
        Returns:
            NodeResult
        """
        return self.execute(context)
    
    def validate_config(self) -> tuple:
        """
        验证节点配置
        
        Returns:
            (is_valid, error_message)
        """
        return True, ''
    
    @classmethod
    def get_schema(cls) -> Dict[str, Any]:
        """
        获取节点 Schema（供前端渲染）
        
        Returns:
            节点 Schema
        """
        return {
            'type': cls.node_type,
            'name': cls.node_name,
            'category': cls.node_category,
            'icon': cls.node_icon,
            'description': cls.node_description,
            'inputs': cls.inputs,
            'outputs': cls.outputs,
            'supports_branches': cls.supports_branches,
        }
    
    @classmethod
    def get_config_schema(cls) -> Dict[str, Any]:
        """
        获取节点配置 Schema（供前端表单渲染）
        
        Returns:
            配置 Schema
        """
        return {}
    
    def resolve_require_confirmation(self, context: NodeContext, default: bool = True) -> bool:
        """
        解析 require_confirmation 配置
        
        支持三种模式：
        1. 布尔值：直接使用 True/False
        2. 字符串 'always'/'never'：始终确认/从不确认
        3. 变量引用：{{variable_name}} 格式，解析变量值作为布尔值
        
        Args:
            context: 节点执行上下文
            default: 默认值
            
        Returns:
            是否需要确认
        """
        value = self.config.get('require_confirmation', default)
        
        # 布尔值直接返回
        if isinstance(value, bool):
            return value
        
        # 字符串处理
        if isinstance(value, str):
            value_lower = value.lower().strip()
            
            # 固定模式
            if value_lower in ('always', 'true', '1', 'yes'):
                return True
            if value_lower in ('never', 'false', '0', 'no'):
                return False
            
            # 变量引用模式：{{variable_name}}
            if '{{' in value and '}}' in value:
                resolved = context.resolve_template(value)
                # 解析后的值转换为布尔值
                if isinstance(resolved, bool):
                    return resolved
                if isinstance(resolved, str):
                    resolved_lower = resolved.lower().strip()
                    if resolved_lower in ('true', '1', 'yes'):
                        return True
                    if resolved_lower in ('false', '0', 'no'):
                        return False
                # 非空字符串视为 True
                return bool(resolved and resolved != value)
        
        # 其他情况返回默认值
        return default
