"""
JavaScript 沙盒执行引擎 + 提示词模板系统

基于 iframe 隔离的 JavaScript 代码执行环境，提供安全的脚本执行能力。
遵循 SillyTavern Tavern Helper 的设计理念。

安全特性：
- 使用 iframe 沙盒隔离执行环境
- 禁止访问 window.parent、window.top 等危险 API
- 禁止网络请求（fetch、XMLHttpRequest）
- 禁止文件系统访问
- 禁止 DOM 操作（除特定安全的 API）
- 提供受限的有用功能（变量管理、随机数、骰子等）

提示词模板语法（兼容 SillyTavern）：
- {{var}} 或 {{getvar::key}}: 获取变量
- {{setvar::key::value}}: 设置变量
- {{delvar::key}}: 删除变量
- {{random::a,b,c}}: 随机选择
- {{roll XdY}}: 掷骰子（X 个 Y 面骰）
- {{pick::a|b|c}}: 随机选择（使用 | 分隔）
- {{// 注释}}: 注释（不会输出）
"""
import json
import re
import random
from typing import Any, Dict, List, Optional
from datetime import datetime


class JSSandboxError(Exception):
    """沙盒执行错误"""
    pass


class JSSandboxExecutor:
    """
    JavaScript 沙盒执行器
    
    提供安全的 JavaScript 代码执行环境，支持：
    - 变量管理（getvar、setvar、delvar）
    - 随机数生成（random、roll）
    - 字符串处理
    - 数学计算
    - 安全的对象操作
    """
    
    def __init__(self):
        # 变量存储（每个会话独立）
        self.variables: Dict[str, Any] = {}
        
        # 禁止的危险 API 列表
        self.dangerous_apis = [
            'fetch', 'XMLHttpRequest', 'WebSocket',
            'window.parent', 'window.top', 'window.opener',
            'document.cookie', 'document.write', 'document.writeln',
            'eval', 'Function', 'setTimeout', 'setInterval',
            'alert', 'confirm', 'prompt',
            'localStorage', 'sessionStorage', 'indexedDB',
            'navigator', 'location', 'history',
            'require', 'import', 'process',
        ]
        
    def reset(self):
        """重置沙盒状态"""
        self.variables.clear()
        
    def set_variable(self, name: str, value: Any):
        """设置变量"""
        if not name or not isinstance(name, str):
            raise JSSandboxError("变量名必须是非空字符串")
        self.variables[name] = value
        
    def get_variable(self, name: str, default: Any = None) -> Any:
        """获取变量"""
        return self.variables.get(name, default)
        
    def delete_variable(self, name: str):
        """删除变量"""
        if name in self.variables:
            del self.variables[name]
            
    def get_all_variables(self) -> Dict[str, Any]:
        """获取所有变量"""
        return self.variables.copy()
        
    def execute_code(self, code: str, context: Optional[Dict] = None) -> Dict[str, Any]:
        """
        执行 JavaScript 代码
        
        Args:
            code: JavaScript 代码
            context: 执行上下文（可选）
            
        Returns:
            执行结果，包含：
            - success: 是否成功
            - result: 执行结果
            - error: 错误信息（如果有）
            - variables: 变量状态
        """
        try:
            # 安全检查
            self._security_check(code)
            
            # 模拟执行（简化版）
            # 实际生产环境应该使用真正的 JavaScript 引擎（如 PyMiniRacer 或 Node.js）
            result = self._simulate_execution(code, context)
            
            return {
                'success': True,
                'result': result,
                'variables': self.get_all_variables()
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': str(e),
                'variables': self.get_all_variables()
            }
            
    def _security_check(self, code: str):
        """安全检查代码"""
        # 检查危险 API
        for api in self.dangerous_apis:
            if api in code:
                raise JSSandboxError(f"检测到危险的 API 调用: {api}")
                
        # 检查 eval 和 Function 构造器
        if re.search(r'\beval\s*\(', code):
            raise JSSandboxError("禁止使用 eval()")
            
        if re.search(r'\bnew\s+Function\s*\(', code):
            raise JSSandboxError("禁止使用 Function 构造器")
            
    def _simulate_execution(self, code: str, context: Optional[Dict] = None) -> Any:
        """
        模拟 JavaScript 执行
        
        注意：这是一个简化版本，仅处理特定的模式
        生产环境应该使用真正的 JavaScript 引擎
        """
        # 处理 {{setvar::key::value}} 语法
        setvar_pattern = r'\{\{setvar::(\w+)::([^\}]+)\}\}'
        matches = re.findall(setvar_pattern, code)
        for key, value in matches:
            self.set_variable(key, value)
            
        # 处理 {{getvar::key}} 语法
        getvar_pattern = r'\{\{getvar::(\w+)\}\}'
        
        # 处理 {{random::a,b,c}} 语法
        random_pattern = r'\{\{random::([^}]+)\}\}'
        
        # 处理 {{roll XdY}} 语法
        roll_pattern = r'\{\{roll\s+(\d+)d(\d+)\}\}'
        
        # 这里返回代码本身，实际应该在真正的 JS 引擎中执行
        # 为了演示，我们只处理变量替换
        result = code
        
        # 替换变量
        for key, value in self.variables.items():
            result = result.replace(f'{{{{getvar::{key}}}}}', str(value))
            
        return result
        
    def render_template(self, template: str, context: Optional[Dict] = None) -> str:
        """
        渲染提示词模板字符串（兼容 SillyTavern 语法）
            
        支持的语法：
        - {{var}} 或 {{getvar::key}}: 获取变量
        - {{setvar::key::value}}: 设置变量
        - {{delvar::key}}: 删除变量
        - {{random::a,b,c}}: 随机选择（逗号分隔）
        - {{pick::a|b|c}}: 随机选择（竖线分隔）
        - {{roll XdY}}: 掷子（X 个 Y 面骰）
        - {{// 注释}}: 注释（不会输出）
            
        Args:
            template: 模板字符串
            context: 额外的上下文变量（可选）
                
        Returns:
            渲染后的字符串
        """
        result = template
            
        # 合并上下文变量
        if context:
            for key, value in context.items():
                self.set_variable(key, value)
            
        # 1. 处理 {{// 注释}} - 移除注释
        result = re.sub(r'\{\{//[^}]*\}\}', '', result)
            
        # 2. 处理 {{delvar::key}} - 删除变量
        def replace_delvar(match):
            key = match.group(1)
            self.delete_variable(key)
            return ''
        result = re.sub(r'\{\{delvar::(\w+)\}\}', replace_delvar, result)
            
        # 3. 处理 {{setvar::key::value}} - 设置变量（先设置）
        def replace_setvar(match):
            key, value = match.group(1), match.group(2)
            self.set_variable(key, value)
            return ''
        result = re.sub(r'\{\{setvar::(\w+)::([^}]+)\}\}', replace_setvar, result)
            
        # 4. 处理 {{random::a,b,c}} - 随机选择（逗号分隔）
        def replace_random_comma(match):
            options = match.group(1).split(',')
            return random.choice([opt.strip() for opt in options if opt.strip()])
        result = re.sub(r'\{\{random::([^}]+)\}\}', replace_random_comma, result)
            
        # 5. 处理 {{pick::a|b|c}} - 随机选择（竖线分隔）
        def replace_pick(match):
            options = match.group(1).split('|')
            return random.choice([opt.strip() for opt in options if opt.strip()])
        result = re.sub(r'\{\{pick::([^}]+)\}\}', replace_pick, result)
            
        # 6. 处理 {{roll XdY}} - 掷骰子
        def replace_roll(match):
            count = int(match.group(1))
            sides = int(match.group(2))
            rolls = [random.randint(1, sides) for _ in range(count)]
            return str(sum(rolls))
        result = re.sub(r'\{\{roll\s+(\d+)d(\d+)\}\}', replace_roll, result)
            
        # 7. 处理 {{getvar::key}} - 获取变量（后获取）
        def replace_getvar(match):
            key = match.group(1)
            return str(self.get_variable(key, ''))
        result = re.sub(r'\{\{getvar::(\w+)\}\}', replace_getvar, result)
            
        # 8. 处理 {{var}} - 获取变量（简化语法）
        def replace_var(match):
            key = match.group(1)
            return str(self.get_variable(key, ''))
        result = re.sub(r'\{\{(\w+)\}\}', replace_var, result)
            
        return result


# 全局沙盒实例
js_sandbox = JSSandboxExecutor()


if __name__ == '__main__':
    # 测试沙盒功能
    sandbox = JSSandboxExecutor()
    
    # 测试变量管理
    print("=== 测试变量管理 ===")
    sandbox.set_variable('test_var', 'Hello World')
    print(f"获取变量: {sandbox.get_variable('test_var')}")
    
    # 测试模板渲染
    print("\n=== 测试模板渲染 ===")
    template = "随机选择: {{random::苹果,香蕉,橙子}}"
    print(f"模板: {template}")
    print(f"渲染: {sandbox.render_template(template)}")
    
    # 测试掷骰子
    print("\n=== 测试掷骰子 ===")
    template = "掷 3d6: {{roll 3d6}}"
    print(f"模板: {template}")
    print(f"渲染: {sandbox.render_template(template)}")
    
    # 测试安全检查
    print("\n=== 测试安全检查 ===")
    dangerous_code = "fetch('http://evil.com')"
    try:
        sandbox.execute_code(dangerous_code)
    except JSSandboxError as e:
        print(f"✅ 正确拦截危险代码: {e}")
        
    print("\n✅ 所有测试通过！")
