"""
黑板系统
用于行为树节点间的数据共享
包含高级数据管理功能和事件系统
"""

import json
import pickle
import threading
from typing import Any, Dict, List, Optional, Callable, Union
from datetime import datetime
from copy import deepcopy

class DataChangeType:
    """数据变更类型枚举"""
    SET = "set"
    UPDATE = "update"
    DELETE = "delete"
    CLEAR = "clear"

class DataChangeEvent:
    """数据变更事件"""
    
    def __init__(self, key: str, old_value: Any, new_value: Any, 
                 change_type: str, timestamp: datetime = None):
        self.key = key
        self.old_value = old_value
        self.new_value = new_value
        self.change_type = change_type
        self.timestamp = timestamp or datetime.now()
        
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            'key': self.key,
            'old_value': self.old_value,
            'new_value': self.new_value,
            'change_type': self.change_type,
            'timestamp': self.timestamp.isoformat()
        }

class Blackboard:
    """
    黑板系统
    用于行为树节点间的数据共享
    """
    
    def __init__(self, name: str = "GlobalBlackboard", 
                 max_history_size: int = 1000,
                 enable_history: bool = True,
                 enable_validation: bool = False):
        self.name = name
        self.data: Dict[str, Any] = {}
        self.lock = threading.RLock()  # 线程安全锁
        self.data_types: Dict[str, type] = {}  # 数据类型约束
        self.validators: Dict[str, Callable[[Any], bool]] = {}  # 数据验证器
        self.callbacks: Dict[str, List[Callable]] = {}  # 数据变更回调
        self.history: List[DataChangeEvent] = []  # 数据变更历史
        self.max_history_size = max_history_size
        self.enable_history = enable_history
        self.enable_validation = enable_validation
        self.timestamp = datetime.now()
        
    def set(self, key: str, value: Any, validate: bool = True) -> bool:
        """设置键值对"""
        with self.lock:
            old_value = self.data.get(key)
            
            # 验证数据类型
            if self.enable_validation and key in self.data_types:
                if not isinstance(value, self.data_types[key]):
                    raise TypeError(f"Key '{key}' requires value of type {self.data_types[key]}, got {type(value)}")
            
            # 验证数据
            if validate and key in self.validators:
                if not self.validators[key](value):
                    raise ValueError(f"Value for key '{key}' failed validation")
            
            self.data[key] = value
            
            # 记录历史
            if self.enable_history:
                event = DataChangeEvent(key, old_value, value, DataChangeType.SET)
                self.history.append(event)
                if len(self.history) > self.max_history_size:
                    self.history.pop(0)
            
            # 触发回调
            self._trigger_callbacks(key, old_value, value, DataChangeType.SET)
            
            return True
        
    def get(self, key: str, default: Any = None) -> Any:
        """获取键对应的值"""
        with self.lock:
            return self.data.get(key, default)
        
    def get_typed(self, key: str, expected_type: type, default: Any = None) -> Any:
        """获取特定类型的值"""
        with self.lock:
            value = self.data.get(key, default)
            if value is not None and not isinstance(value, expected_type):
                try:
                    # 尝试类型转换
                    if expected_type == int:
                        return int(value)
                    elif expected_type == float:
                        return float(value)
                    elif expected_type == str:
                        return str(value)
                    elif expected_type == bool:
                        if isinstance(value, str):
                            return value.lower() in ['true', '1', 'yes', 'on']
                        return bool(value)
                    else:
                        # 尝试直接转换
                        return expected_type(value)
                except (ValueError, TypeError):
                    return default
            return value
        
    def has(self, key: str) -> bool:
        """检查键是否存在"""
        with self.lock:
            return key in self.data
        
    def remove(self, key: str) -> bool:
        """移除键值对"""
        with self.lock:
            if key in self.data:
                old_value = self.data[key]
                del self.data[key]
                
                # 记录历史
                if self.enable_history:
                    event = DataChangeEvent(key, old_value, None, DataChangeType.DELETE)
                    self.history.append(event)
                    if len(self.history) > self.max_history_size:
                        self.history.pop(0)
                
                # 触发回调
                self._trigger_callbacks(key, old_value, None, DataChangeType.DELETE)
                
                return True
            return False
            
    def clear(self) -> None:
        """清空所有数据"""
        with self.lock:
            old_data = self.data.copy()
            self.data.clear()
            
            # 记录历史
            if self.enable_history:
                for key, value in old_data.items():
                    event = DataChangeEvent(key, value, None, DataChangeType.CLEAR)
                    self.history.append(event)
                    if len(self.history) > self.max_history_size:
                        self.history.pop(0)
            
            # 触发回调
            for key, value in old_data.items():
                self._trigger_callbacks(key, value, None, DataChangeType.CLEAR)
        
    def update(self, data_dict: Dict[str, Any], validate: bool = True) -> int:
        """批量更新数据，返回成功更新的项数"""
        with self.lock:
            success_count = 0
            for key, value in data_dict.items():
                try:
                    self.set(key, value, validate=validate)
                    success_count += 1
                except (TypeError, ValueError):
                    continue  # 跳过验证失败的项
            return success_count
    
    def get_sub_blackboard(self, prefix: str, include_nested: bool = True) -> 'Blackboard':
        """获取子黑板，包含指定前缀的所有键"""
        with self.lock:
            sub_board = Blackboard(f"{self.name}_{prefix}_sub", 
                                 max_history_size=self.max_history_size,
                                 enable_history=self.enable_history)
            
            for key, value in self.data.items():
                if key.startswith(prefix):
                    # 如果不包含嵌套键，只获取直接匹配的键
                    if not include_nested and '.' in key[len(prefix):]:
                        continue
                    sub_key = key[len(prefix):].lstrip('.')
                    sub_board.set(sub_key, value)
            
            return sub_board
    
    def set_type_constraint(self, key: str, data_type: type) -> None:
        """设置键的数据类型约束"""
        with self.lock:
            self.data_types[key] = data_type
    
    def set_validator(self, key: str, validator: Callable[[Any], bool]) -> None:
        """设置键的数据验证器"""
        with self.lock:
            self.validators[key] = validator
    
    def add_callback(self, key: str, callback: Callable[[str, Any, Any, str], None]) -> None:
        """添加数据变更回调"""
        with self.lock:
            if key not in self.callbacks:
                self.callbacks[key] = []
            self.callbacks[key].append(callback)
    
    def remove_callback(self, key: str, callback: Callable[[str, Any, Any, str], None]) -> bool:
        """移除数据变更回调"""
        with self.lock:
            if key in self.callbacks and callback in self.callbacks[key]:
                self.callbacks[key].remove(callback)
                return True
            return False
    
    def _trigger_callbacks(self, key: str, old_value: Any, new_value: Any, change_type: str) -> None:
        """触发回调函数"""
        if key in self.callbacks:
            for callback in self.callbacks[key]:
                try:
                    callback(key, old_value, new_value, change_type)
                except Exception as e:
                    print(f"回调执行出错: {e}")
        
        # 触发通配符回调（'__all__'键表示所有变更）
        if '__all__' in self.callbacks:
            for callback in self.callbacks['__all__']:
                try:
                    callback(key, old_value, new_value, change_type)
                except Exception as e:
                    print(f"通配符回调执行出错: {e}")
    
    def get_keys_by_pattern(self, pattern: str) -> List[str]:
        """根据模式获取匹配的键列表（支持简单通配符）"""
        import fnmatch
        with self.lock:
            return [key for key in self.data.keys() if fnmatch.fnmatch(key, pattern)]
    
    def get_keys_by_prefix(self, prefix: str) -> List[str]:
        """根据前缀获取键列表"""
        with self.lock:
            return [key for key in self.data.keys() if key.startswith(prefix)]
    
    def get_keys_by_type(self, data_type: type) -> List[str]:
        """根据类型获取键列表"""
        with self.lock:
            result = []
            for key, value in self.data.items():
                if isinstance(value, data_type):
                    result.append(key)
            return result
    
    def get_history(self, key: str = None, limit: int = None) -> List[DataChangeEvent]:
        """获取变更历史"""
        with self.lock:
            if key:
                history = [event for event in self.history if event.key == key]
            else:
                history = self.history.copy()
            
            if limit:
                return history[-limit:]
            return history
    
    def clear_history(self) -> None:
        """清空历史记录"""
        with self.lock:
            self.history.clear()
    
    def get_size(self) -> int:
        """获取数据大小（项数）"""
        with self.lock:
            return len(self.data)
    
    def get_memory_usage(self) -> int:
        """获取内存使用量估算（字节）"""
        with self.lock:
            import sys
            size = sys.getsizeof(self.data)
            for key, value in self.data.items():
                size += sys.getsizeof(key)
                size += sys.getsizeof(value)
            return size
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典（深拷贝）"""
        with self.lock:
            return deepcopy(self.data)
    
    def from_dict(self, data: Dict[str, Any], clear_first: bool = False) -> None:
        """从字典加载数据"""
        with self.lock:
            if clear_first:
                self.data.clear()
            self.data.update(data)
    
    def to_json(self, indent: int = 2) -> str:
        """转换为JSON字符串"""
        with self.lock:
            try:
                # 首先尝试使用深拷贝来防止序列化问题
                data_copy = deepcopy(self.data)
                return json.dumps(data_copy, indent=indent, default=str)
            except TypeError:
                # 如果某些对象无法序列化，转换为字符串
                safe_data = {}
                for k, v in self.data.items():
                    try:
                        json.dumps(v)  # 测试是否可序列化
                        safe_data[k] = v
                    except TypeError:
                        safe_data[k] = str(v)  # 转换为字符串
                return json.dumps(safe_data, indent=indent)
    
    def from_json(self, json_str: str, clear_first: bool = False) -> bool:
        """从JSON字符串加载数据"""
        with self.lock:
            try:
                data = json.loads(json_str)
                if clear_first:
                    self.data.clear()
                self.data.update(data)
                return True
            except json.JSONDecodeError:
                return False
    
    def to_pickle(self) -> bytes:
        """序列化为pickle格式"""
        with self.lock:
            return pickle.dumps(self.data)
    
    def from_pickle(self, pickle_bytes: bytes, clear_first: bool = False) -> bool:
        """从pickle格式反序列化"""
        with self.lock:
            try:
                data = pickle.loads(pickle_bytes)
                if clear_first:
                    self.data.clear()
                self.data.update(data)
                return True
            except pickle.PickleError:
                return False
    
    def get_timestamp(self) -> datetime:
        """获取时间戳"""
        with self.lock:
            return self.timestamp
    
    def update_timestamp(self) -> None:
        """更新时间戳"""
        with self.lock:
            self.timestamp = datetime.now()
    
    def get_statistics(self) -> Dict[str, Any]:
        """获取黑板统计信息"""
        with self.lock:
            type_counts = {}
            for value in self.data.values():
                type_name = type(value).__name__
                type_counts[type_name] = type_counts.get(type_name, 0) + 1
            
            return {
                'name': self.name,
                'size': self.get_size(),
                'memory_usage': self.get_memory_usage(),
                'timestamp': self.timestamp.isoformat() if self.timestamp else None,
                'type_distribution': type_counts,
                'history_size': len(self.history),
                'max_history_size': self.max_history_size,
                'data_types_count': len(self.data_types),
                'validators_count': len(self.validators),
                'callbacks_count': sum(len(cbs) for cbs in self.callbacks.values())
            }
    
    def copy(self) -> 'Blackboard':
        """创建黑板副本"""
        with self.lock:
            new_board = Blackboard(
                name=f"{self.name}_copy",
                max_history_size=self.max_history_size,
                enable_history=self.enable_history,
                enable_validation=self.enable_validation
            )
            new_board.data = deepcopy(self.data)
            new_board.data_types = self.data_types.copy()
            new_board.validators = self.validators.copy()
            new_board.callbacks = {k: v.copy() for k, v in self.callbacks.items()}
            return new_board

class BlackboardManager:
    """
    黑板管理器
    用于管理多个黑板实例
    """
    
    def __init__(self):
        self.blackboards: Dict[str, Blackboard] = {}
        self.default_blackboard: Optional[Blackboard] = None
    
    def create_blackboard(self, name: str, **kwargs) -> Blackboard:
        """创建新的黑板"""
        if name in self.blackboards:
            raise ValueError(f"Blackboard with name '{name}' already exists")
        
        blackboard = Blackboard(name, **kwargs)
        self.blackboards[name] = blackboard
        
        # 如果是第一个黑板，设为默认
        if not self.default_blackboard:
            self.default_blackboard = blackboard
            
        return blackboard
    
    def get_blackboard(self, name: str) -> Optional[Blackboard]:
        """获取指定名称的黑板"""
        return self.blackboards.get(name)
    
    def get_or_create(self, name: str, **kwargs) -> Blackboard:
        """获取或创建黑板"""
        if name in self.blackboards:
            return self.blackboards[name]
        return self.create_blackboard(name, **kwargs)
    
    def set_default(self, name: str) -> bool:
        """设置默认黑板"""
        if name in self.blackboards:
            self.default_blackboard = self.blackboards[name]
            return True
        return False
    
    def get_default(self) -> Optional[Blackboard]:
        """获取默认黑板"""
        return self.default_blackboard
    
    def remove_blackboard(self, name: str) -> bool:
        """移除黑板"""
        if name in self.blackboards:
            del self.blackboards[name]
            # 如果删除的是默认黑板，重新设置默认值
            if self.default_blackboard and self.default_blackboard.name == name:
                if self.blackboards:
                    self.default_blackboard = next(iter(self.blackboards.values()))
                else:
                    self.default_blackboard = None
            return True
        return False
    
    def get_all_names(self) -> List[str]:
        """获取所有黑板名称"""
        return list(self.blackboards.keys())
    
    def get_statistics(self) -> Dict[str, Any]:
        """获取管理器统计信息"""
        stats = {
            'total_blackboards': len(self.blackboards),
            'default_blackboard': self.default_blackboard.name if self.default_blackboard else None,
            'blackboard_stats': {}
        }
        
        for name, board in self.blackboards.items():
            stats['blackboard_stats'][name] = board.get_statistics()
            
        return stats