"""
CSSL Runtime Environment
Executes CSSL scripts by interpreting the AST
"""

import os
import time
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Callable, Union

from .cssl_parser import ASTNode, parse_cssl, parse_cssl_program, CSSLSyntaxError
from .cssl_events import CSSLEventManager, EventType, EventData, get_event_manager
from .cssl_builtins import CSSLBuiltins
from .cssl_modules import get_module_registry, get_standard_module
from .cssl_types import (
    Parameter, DataStruct, Shuffled, Iterator, Combo,
    Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary, Map,
    CSSLClass, CSSLInstance
)


# Global custom filter registry
# Structure: { "type::helper": callback_function }
# Callback signature: (source, filter_value, runtime) -> Any
_custom_filters: Dict[str, Callable[[Any, Any, Any], Any]] = {}


def register_filter(filter_type: str, helper: str, callback: Callable[[Any, Any, Any], Any]) -> None:
    """Register a custom filter.

    Args:
        filter_type: The filter type (e.g., "mytype")
        helper: The helper name (e.g., "where", "index", or "*" for catch-all)
        callback: Function(source, filter_value, runtime) -> filtered_result

    Usage in CSSL:
        result <==[mytype::where="value"] source;

    Usage from Python:
        from includecpp.core.cssl.cssl_runtime import register_filter
        register_filter("mytype", "where", lambda src, val, rt: ...)
    """
    key = f"{filter_type}::{helper}"
    _custom_filters[key] = callback


def unregister_filter(filter_type: str, helper: str) -> bool:
    """Unregister a custom filter."""
    key = f"{filter_type}::{helper}"
    if key in _custom_filters:
        del _custom_filters[key]
        return True
    return False


def get_custom_filters() -> Dict[str, Callable]:
    """Get all registered custom filters."""
    return _custom_filters.copy()


class CSSLRuntimeError(Exception):
    """Runtime error during CSSL execution with detailed context"""
    def __init__(self, message: str, line: int = 0, context: str = None, hint: str = None):
        self.line = line
        self.context = context
        self.hint = hint

        # Build detailed error message
        error_parts = []

        # Main error message
        if line:
            error_parts.append(f"Error at line {line}: {message}")
        else:
            error_parts.append(f"Error: {message}")

        # Add context if available
        if context:
            error_parts.append(f"  Context: {context}")

        # Add hint if available
        if hint:
            error_parts.append(f"  Hint: {hint}")

        super().__init__("\n".join(error_parts))


# Common error hints for better user experience
ERROR_HINTS = {
    'undefined_variable': "Did you forget to declare the variable? Use 'string x = ...' or 'int x = ...'",
    'undefined_function': "Check function name spelling. Functions are case-sensitive.",
    'type_mismatch': "Try using explicit type conversion: toInt(), toFloat(), toString()",
    'null_reference': "Variable is null. Check if it was properly initialized.",
    'index_out_of_bounds': "Array index must be >= 0 and < array.length()",
    'division_by_zero': "Cannot divide by zero. Add a check: if (divisor != 0) { ... }",
    'invalid_operation': "This operation is not supported for this type.",
    'missing_semicolon': "Statement might be missing a semicolon (;)",
    'missing_brace': "Check for matching opening and closing braces { }",
}


class CSSLBreak(Exception):
    """Break statement"""
    pass


class CSSLContinue(Exception):
    """Continue statement"""
    pass


class CSSLReturn(Exception):
    """Return statement"""
    def __init__(self, value: Any = None):
        self.value = value
        super().__init__()


@dataclass
class Scope:
    """Variable scope"""
    variables: Dict[str, Any] = field(default_factory=dict)
    parent: Optional['Scope'] = None

    def get(self, name: str) -> Any:
        if name in self.variables:
            return self.variables[name]
        if self.parent:
            return self.parent.get(name)
        return None

    def set(self, name: str, value: Any):
        self.variables[name] = value

    def update(self, name: str, value: Any) -> bool:
        if name in self.variables:
            self.variables[name] = value
            return True
        if self.parent:
            return self.parent.update(name, value)
        return False

    def has(self, name: str) -> bool:
        if name in self.variables:
            return True
        if self.parent:
            return self.parent.has(name)
        return False


@dataclass
class ServiceDefinition:
    """Parsed service definition"""
    name: str = ""
    version: str = "1.0"
    author: str = ""
    description: str = ""
    dependencies: List[str] = field(default_factory=list)
    autostart: bool = False
    priority: int = 0
    structs: Dict[str, ASTNode] = field(default_factory=dict)
    functions: Dict[str, ASTNode] = field(default_factory=dict)
    event_handlers: Dict[str, List[ASTNode]] = field(default_factory=dict)


class CSSLRuntime:
    """
    CSSL Script Runtime
    Interprets and executes CSSL Abstract Syntax Trees
    """

    def __init__(self, service_engine=None, output_callback: Callable[[str, str], None] = None):
        self.service_engine = service_engine
        self.scope = Scope()
        self.global_scope = self.scope
        self.builtins = CSSLBuiltins(self)
        self.event_manager = get_event_manager()
        self.output_buffer: List[str] = []
        self.services: Dict[str, ServiceDefinition] = {}
        self._modules: Dict[str, Any] = {}
        self._global_structs: Dict[str, Any] = {}  # Global structs for s@<name> references
        self._function_injections: Dict[str, List[tuple]] = {}  # List of (code_block, captured_values_dict)
        self._function_replaced: Dict[str, bool] = {}  # NEW: Track replaced functions (<<==)
        self._original_functions: Dict[str, Any] = {}  # Store originals before replacement
        self._injection_captures: Dict[str, Dict[str, Any]] = {}  # Captured %vars per injection
        self._current_captured_values: Dict[str, Any] = {}  # Current captured values during injection execution
        self._promoted_globals: Dict[str, Any] = {}  # NEW: Variables promoted via global()
        self._current_instance: Optional[CSSLInstance] = None  # Current class instance for this-> access
        self._running = False
        self._exit_code = 0
        self._output_callback = output_callback  # Callback for console output (text, level)
        self._source_lines: List[str] = []  # Store source code lines for error reporting
        self._current_file: str = "<code>"  # Current file being executed

        self._setup_modules()
        self._setup_builtins()

    def _setup_modules(self):
        """Setup module references for @KernelClient, @VSRAM, etc."""
        if self.service_engine:
            self._modules['KernelClient'] = self.service_engine.KernelClient
            self._modules['Kernel'] = self.service_engine.KernelClient

            if hasattr(self.service_engine.KernelClient, 'VSRam'):
                self._modules['VSRAM'] = self.service_engine.KernelClient.VSRam
                self._modules['VSRam'] = self.service_engine.KernelClient.VSRam

            if hasattr(self.service_engine.KernelClient, 'WheelKernel'):
                self._modules['Wheel'] = self.service_engine.KernelClient.WheelKernel
                self._modules['WheelKernel'] = self.service_engine.KernelClient.WheelKernel

            if hasattr(self.service_engine.KernelClient, 'CSnI'):
                self._modules['Network'] = self.service_engine.KernelClient.CSnI
                self._modules['CSnI'] = self.service_engine.KernelClient.CSnI

            if hasattr(self.service_engine, 'ServiceOperation'):
                self._modules['Service'] = self.service_engine.ServiceOperation
                self._modules['ServiceOperation'] = self.service_engine.ServiceOperation

            self._modules['ServiceEngine'] = self.service_engine
            self._modules['Boot'] = self.service_engine

        self._modules['event'] = self.event_manager
        self._modules['Events'] = self.event_manager

        # Register CSSL Standard Modules
        module_registry = get_module_registry()
        for module_name in module_registry.list_modules():
            module = module_registry.get_module(module_name)
            if module:
                module.runtime = self
                self._modules[module_name] = module

    def _setup_builtins(self):
        """Register built-in functions in global scope"""
        for name in self.builtins.list_functions():
            self.global_scope.set(name, self.builtins.get_function(name))

    def get_module(self, path: str) -> Any:
        """Get a module by path like 'KernelClient.VSRam'"""
        parts = path.split('.')
        obj = self._modules.get(parts[0])

        if obj is None:
            return None

        for part in parts[1:]:
            if hasattr(obj, part):
                obj = getattr(obj, part)
            elif isinstance(obj, dict) and part in obj:
                obj = obj[part]
            else:
                return None

        return obj

    def register_global_struct(self, name: str, struct_data: Any):
        """Register a struct as globally accessible via s@<name>"""
        self._global_structs[name] = struct_data

    def get_global_struct(self, path: str) -> Any:
        """Get a global struct by path like 'Backend.Loop.timer'"""
        parts = path.split('.')
        obj = self._global_structs.get(parts[0])

        if obj is None:
            return None

        for part in parts[1:]:
            if hasattr(obj, part):
                obj = getattr(obj, part)
            elif isinstance(obj, dict) and part in obj:
                obj = obj[part]
            else:
                return None

        return obj

    def _format_error(self, line: int, message: str, hint: str = None) -> CSSLRuntimeError:
        """Format a detailed error with source context"""
        error_parts = []

        # Main error header
        if line and line > 0:
            error_parts.append(f"Error at line {line} in {self._current_file}:")
        else:
            error_parts.append(f"Error in {self._current_file}:")

        # Extract message without existing line info
        clean_msg = message
        if "at line" in clean_msg.lower():
            # Remove redundant line info from message
            clean_msg = clean_msg.split(":", 1)[-1].strip() if ":" in clean_msg else clean_msg

        error_parts.append(f"  {clean_msg}")

        # Show source context (3 lines before and after)
        if self._source_lines and line and line > 0:
            error_parts.append("")
            start = max(0, line - 3)
            end = min(len(self._source_lines), line + 2)

            for i in range(start, end):
                line_num = i + 1
                source_line = self._source_lines[i] if i < len(self._source_lines) else ""
                marker = ">>>" if line_num == line else "   "
                error_parts.append(f"  {marker} {line_num:4d} | {source_line}")

        # Add hint
        if hint:
            error_parts.append("")
            error_parts.append(f"  Hint: {hint}")

        return CSSLRuntimeError("\n".join(error_parts), line)

    def _get_source_line(self, line: int) -> str:
        """Get source line by number (1-indexed)"""
        if self._source_lines and 0 < line <= len(self._source_lines):
            return self._source_lines[line - 1]
        return ""

    def execute(self, source: str) -> Any:
        """Execute CSSL service source code"""
        self._source_lines = source.splitlines()
        try:
            ast = parse_cssl(source)
            return self._execute_node(ast)
        except CSSLSyntaxError as e:
            raise self._format_error(e.line, str(e))
        except SyntaxError as e:
            raise CSSLRuntimeError(f"Syntax error: {e}")

    def execute_program(self, source: str) -> Any:
        """Execute standalone CSSL program (no service wrapper)"""
        self._source_lines = source.splitlines()
        try:
            ast = parse_cssl_program(source)
            return self._exec_program(ast)
        except CSSLSyntaxError as e:
            raise self._format_error(e.line, str(e))
        except SyntaxError as e:
            raise CSSLRuntimeError(f"Syntax error: {e}")

    def execute_ast(self, ast: ASTNode) -> Any:
        """Execute a pre-parsed AST"""
        return self._execute_node(ast)

    def execute_file(self, filepath: str) -> Any:
        """Execute a CSSL service file"""
        import os
        self._current_file = os.path.basename(filepath)
        with open(filepath, 'r', encoding='utf-8') as f:
            source = f.read()
        return self.execute(source)

    def execute_program_file(self, filepath: str) -> Any:
        """Execute a standalone CSSL program file"""
        import os
        self._current_file = os.path.basename(filepath)
        with open(filepath, 'r', encoding='utf-8') as f:
            source = f.read()
        return self.execute_program(source)

    def _execute_node(self, node: ASTNode) -> Any:
        """Execute an AST node"""
        if node is None:
            return None

        method_name = f'_exec_{node.type.replace("-", "_")}'
        method = getattr(self, method_name, None)

        if method:
            return method(node)
        else:
            raise CSSLRuntimeError(f"Unknown node type: {node.type}", node.line)

    def _exec_service(self, node: ASTNode) -> ServiceDefinition:
        """Execute service root node"""
        service = ServiceDefinition()

        for child in node.children:
            if child.type == 'service-init':
                self._exec_service_init(child, service)
            elif child.type == 'service-include':
                self._exec_service_include(child, service)
            elif child.type == 'service-run':
                self._exec_service_run(child, service)
            # NEW: package block support
            elif child.type == 'package':
                self._exec_package(child, service)
            # NEW: package-includes block support
            elif child.type == 'package-includes':
                self._exec_package_includes(child, service)
            # NEW: struct at top level
            elif child.type == 'struct':
                struct_info = child.value
                if isinstance(struct_info, dict):
                    struct_name = struct_info.get('name', '')
                else:
                    struct_name = struct_info
                service.structs[struct_name] = child
                self._exec_struct(child)
            # NEW: define at top level
            elif child.type == 'function':
                func_info = child.value
                func_name = func_info.get('name')
                service.functions[func_name] = child
                self.scope.set(func_name, child)

        return service

    def _exec_program(self, node: ASTNode) -> Any:
        """Execute standalone program (no service wrapper)

        A program can contain:
        - struct definitions
        - function definitions (define)
        - top-level statements (assignments, function calls, control flow)
        """
        result = None
        self._running = True  # Start running

        for child in node.children:
            # Check if exit() was called
            if not self._running:
                break

            if child.type == 'struct':
                self._exec_struct(child)
            elif child.type == 'class':
                self._exec_class(child)
            elif child.type == 'function':
                self._exec_function(child)
            elif child.type == 'global_assignment':
                # Handle global variable declaration: global Name = value
                result = self._exec_global_assignment(child)
            elif child.type == 'typed_declaration':
                # Handle typed variable declaration: type<T> varName = value;
                result = self._exec_typed_declaration(child)
            elif child.type == 'instance_declaration':
                # Handle instance declaration: instance<"name"> varName;
                result = self._exec_instance_declaration(child)
            elif child.type == 'super_func':
                # Super-function for .cssl-pl payload files (#$run, #$exec, #$printl)
                result = self._exec_super_func(child)
            elif child.type in ('assignment', 'expression', 'inject', 'receive', 'flow',
                               'if', 'while', 'for', 'c_for', 'foreach', 'switch', 'try'):
                result = self._execute_node(child)
            elif child.type == 'call':
                result = self._eval_call(child)
            else:
                # Try to execute as statement
                try:
                    result = self._execute_node(child)
                except CSSLRuntimeError:
                    pass  # Ignore unknown nodes in program mode

        # Look for and execute main() if defined (only if still running)
        if self._running:
            main_func = self.scope.get('main')
            if main_func and isinstance(main_func, ASTNode) and main_func.type == 'function':
                try:
                    result = self._call_function(main_func, [])
                except CSSLReturn as ret:
                    result = ret.value

        return result

    def _exec_service_init(self, node: ASTNode, service: ServiceDefinition):
        """Execute service-init block"""
        # Property aliases mapping
        key_aliases = {
            'service-name': 'name',
            'service-version': 'version',
            'service-author': 'author',
            'service-description': 'description',
            'executation': 'execution',
            'execution': 'execution',
        }

        for child in node.children:
            if child.type == 'property' and child.value:
                key = child.value.get('key')
                value = child.value.get('value')

                # Apply alias mapping
                key = key_aliases.get(key, key)

                if key == 'name':
                    service.name = value
                elif key == 'version':
                    service.version = value
                elif key == 'author':
                    service.author = value
                elif key == 'description':
                    service.description = value
                elif key == 'dependencies':
                    if isinstance(value, list):
                        service.dependencies = value
                    elif isinstance(value, str):
                        service.dependencies = [value]
                elif key == 'autostart':
                    service.autostart = bool(value)
                elif key == 'priority':
                    service.priority = int(value)
                elif key == 'execution':
                    # Handle execution type
                    if value == 'Persistent':
                        service.autostart = True
                    elif value == 'Only-Once':
                        service.autostart = False

    def _exec_service_include(self, node: ASTNode, service: ServiceDefinition):
        """Execute service-include block for importing modules and files"""
        for child in node.children:
            if child.type == 'expression':
                # Evaluate the expression which may be:
                # - @ModuleName (standard module reference)
                # - include(cso_root('/path/to/file.cssl'))
                # - Variable assignment
                result = self._evaluate(child.value)

                # If result is a module, register it
                if result is not None:
                    # Check if it's a call result (like include())
                    if hasattr(result, 'name') and result.name:
                        # It's a ServiceDefinition from include()
                        # Merge its functions and structs into current service
                        if hasattr(result, 'functions'):
                            for fname, fnode in result.functions.items():
                                service.functions[fname] = fnode
                                self.scope.set(fname, fnode)
                        if hasattr(result, 'structs'):
                            for sname, snode in result.structs.items():
                                service.structs[sname] = snode

            elif child.type == 'assignment' or child.type == 'injection':
                # Handle: utils <== include(cso_root('/services/utils.cssl'));
                info = child.value
                name = info.get('name') or info.get('target')
                if isinstance(name, ASTNode):
                    name = name.value if name.type == 'identifier' else str(name)
                source = info.get('source') or info.get('value')
                value = self._evaluate(source)
                self.scope.set(name, value)

            elif child.type == 'call':
                # Direct function call like include(...)
                self._eval_call(child)

            elif child.type == 'module_ref':
                # @ModuleName reference - just evaluate to register
                self._evaluate(child)

    def _exec_service_run(self, node: ASTNode, service: ServiceDefinition):
        """Execute service-run block"""
        for child in node.children:
            if child.type == 'struct':
                # Handle new dict format: {'name': name, 'global': is_global}
                struct_info = child.value
                if isinstance(struct_info, dict):
                    struct_name = struct_info.get('name', '')
                else:
                    struct_name = struct_info
                service.structs[struct_name] = child
                self._exec_struct(child)
            elif child.type == 'function':
                func_info = child.value
                func_name = func_info.get('name')
                service.functions[func_name] = child
                self.scope.set(func_name, child)

    def _exec_package(self, node: ASTNode, service: ServiceDefinition):
        """Execute package {} block for service metadata - NEW

        Syntax:
        package {
            service = "ServiceName";
            exec = @Start();
            version = "1.0.0";
            description = "Beschreibung";
        }
        """
        exec_func = None

        for child in node.children:
            if child.type == 'package_property' and child.value:
                key = child.value.get('key')
                value_node = child.value.get('value')

                # Evaluate the value
                value = self._evaluate(value_node)

                if key == 'service':
                    service.name = value
                elif key == 'version':
                    service.version = value
                elif key == 'description':
                    service.description = value
                elif key == 'author':
                    service.author = value
                elif key == 'exec':
                    # Store exec function for later execution
                    exec_func = value_node

        # Store exec function reference for later
        if exec_func:
            service.functions['__exec__'] = exec_func

    def _exec_package_includes(self, node: ASTNode, service: ServiceDefinition):
        """Execute package-includes {} block for imports - NEW

        Syntax:
        package-includes {
            @Lists = get('list');
            @OS = get('os');
            @Time = get('time');
            @VSRam = get('vsramsdk');
        }
        """
        for child in node.children:
            if child.type == 'assignment':
                info = child.value
                target = info.get('target')
                value_node = info.get('value')

                # Evaluate the value (e.g., get('list'))
                value = self._evaluate(value_node)

                # Get target name
                if isinstance(target, ASTNode):
                    if target.type == 'module_ref':
                        # @ModuleName = get(...)
                        module_name = target.value
                        self._modules[module_name] = value
                    elif target.type == 'identifier':
                        # varName = get(...)
                        self.scope.set(target.value, value)
                elif isinstance(target, str):
                    self.scope.set(target, value)

            elif child.type == 'expression':
                # Evaluate expression statements
                self._evaluate(child.value)

            elif child.type == 'inject':
                # Handle: @Module <== get(...);
                target = child.value.get('target')
                source = self._evaluate(child.value.get('source'))

                if isinstance(target, ASTNode) and target.type == 'module_ref':
                    self._modules[target.value] = source
                elif isinstance(target, ASTNode) and target.type == 'identifier':
                    self.scope.set(target.value, source)

    def _exec_struct(self, node: ASTNode) -> Dict[str, Any]:
        """Execute struct block"""
        struct_data = {}

        # Get struct name and global flag
        struct_info = node.value
        if isinstance(struct_info, dict):
            struct_name = struct_info.get('name', '')
            is_global = struct_info.get('global', False)
        else:
            # Backwards compatibility: value is just the name
            struct_name = struct_info
            is_global = False

        for child in node.children:
            if child.type == 'injection':
                info = child.value
                name = info.get('name')
                source = info.get('source')
                value = self._evaluate(source)
                struct_data[name] = value
                self.scope.set(name, value)

            elif child.type == 'assignment':
                info = child.value
                name = info.get('name')
                value = self._evaluate(info.get('value'))
                struct_data[name] = value
                self.scope.set(name, value)

            elif child.type == 'function':
                func_info = child.value
                func_name = func_info.get('name')
                struct_data[func_name] = child
                self.scope.set(func_name, child)

            elif child.type == 'expression':
                # Execute expression statements like print()
                self._evaluate(child.value)

            elif child.type == 'call':
                # Direct function calls
                self._eval_call(child)

            else:
                # Try to execute other statement types
                try:
                    self._execute_node(child)
                except Exception:
                    pass

        # Register as global struct if decorated with (@)
        if is_global and struct_name:
            self.register_global_struct(struct_name, struct_data)

        return struct_data

    def _exec_class(self, node: ASTNode) -> CSSLClass:
        """Execute class definition - registers class in scope.

        Parses class members and methods, creating a CSSLClass object
        that can be instantiated with 'new'.
        """
        class_info = node.value
        class_name = class_info.get('name')

        members = {}  # Member variable defaults/types
        methods = {}  # Method AST nodes
        constructor = None

        for child in node.children:
            if child.type == 'function':
                # This is a method
                func_info = child.value
                method_name = func_info.get('name')

                if func_info.get('is_constructor') or method_name == class_name or method_name == '__init__':
                    constructor = child
                else:
                    methods[method_name] = child

            elif child.type == 'typed_declaration':
                # This is a member variable
                decl = child.value
                member_name = decl.get('name')
                member_type = decl.get('type')
                member_value = decl.get('value')

                # Store member info with type and optional default
                members[member_name] = {
                    'type': member_type,
                    'default': self._evaluate(member_value) if member_value else None
                }

        # Create class definition object
        class_def = CSSLClass(
            name=class_name,
            members=members,
            methods=methods,
            constructor=constructor
        )

        # Register class in scope
        self.scope.set(class_name, class_def)
        self.global_scope.set(class_name, class_def)

        return class_def

    def _exec_function(self, node: ASTNode) -> Any:
        """Execute function definition - just registers it"""
        func_info = node.value
        func_name = func_info.get('name')
        self.scope.set(func_name, node)
        return None

    def _exec_typed_declaration(self, node: ASTNode) -> Any:
        """Execute typed variable declaration: type<T> varName = value;

        Creates appropriate type instances for stack, vector, datastruct, etc.
        """
        decl = node.value
        type_name = decl.get('type')
        element_type = decl.get('element_type', 'dynamic')
        var_name = decl.get('name')
        value_node = decl.get('value')

        # Create the appropriate type instance
        if type_name == 'stack':
            instance = Stack(element_type)
        elif type_name == 'vector':
            instance = Vector(element_type)
        elif type_name == 'datastruct':
            instance = DataStruct(element_type)
        elif type_name == 'shuffled':
            instance = Shuffled(element_type)
        elif type_name == 'iterator':
            instance = Iterator(element_type)
        elif type_name == 'combo':
            instance = Combo(element_type)
        elif type_name == 'dataspace':
            instance = DataSpace(element_type)
        elif type_name == 'openquote':
            instance = OpenQuote()
        elif type_name in ('int', 'integer'):
            instance = 0 if value_node is None else self._evaluate(value_node)
        elif type_name in ('string', 'str'):
            instance = "" if value_node is None else self._evaluate(value_node)
        elif type_name in ('float', 'double'):
            instance = 0.0 if value_node is None else self._evaluate(value_node)
        elif type_name == 'bool':
            instance = False if value_node is None else self._evaluate(value_node)
        elif type_name == 'dynamic':
            instance = None if value_node is None else self._evaluate(value_node)
        elif type_name == 'json':
            instance = {} if value_node is None else self._evaluate(value_node)
        elif type_name == 'array':
            instance = Array(element_type)
        elif type_name == 'list':
            instance = List(element_type)
        elif type_name in ('dictionary', 'dict'):
            instance = Dictionary(element_type)
        elif type_name == 'map':
            instance = Map(element_type)
        else:
            # Default: evaluate the value or set to None
            instance = self._evaluate(value_node) if value_node else None

        # If there's an explicit value, use it instead
        if value_node and type_name not in ('int', 'integer', 'string', 'str', 'float', 'double', 'bool', 'dynamic', 'json', 'array'):
            # For container types, the value might be initialization data
            init_value = self._evaluate(value_node)
            if isinstance(init_value, (list, tuple)):
                instance.extend(init_value)
            elif init_value is not None:
                if hasattr(instance, 'append'):
                    instance.append(init_value)

        # Check for global modifier
        modifiers = decl.get('modifiers', [])
        is_global = 'global' in modifiers

        # Store in scope
        self.scope.set(var_name, instance)

        # If global, also store in promoted_globals and global_scope
        if is_global:
            self._promoted_globals[var_name] = instance
            self.global_scope.set(var_name, instance)

        return instance

    def _exec_instance_declaration(self, node: ASTNode) -> Any:
        """Execute instance declaration: instance<"name"> varName;

        Gets or creates a shared instance by name.
        """
        from ..cssl_bridge import _live_objects, SharedObjectProxy
        decl = node.value
        instance_name = decl.get('instance_name')
        var_name = decl.get('name')
        value_node = decl.get('value')

        # Get existing shared instance
        instance = None
        if instance_name in _live_objects:
            instance = SharedObjectProxy(instance_name, _live_objects[instance_name])
        elif self.global_scope.has(f'${instance_name}'):
            instance = self.global_scope.get(f'${instance_name}')

        # If value is provided, use that and register as shared
        if value_node:
            instance = self._evaluate(value_node)
            # Register in global scope for future access
            self.global_scope.set(f'${instance_name}', instance)

        # Store in scope
        self.scope.set(var_name, instance)
        return instance

    def _exec_super_func(self, node: ASTNode) -> Any:
        """Execute super-function for .cssl-pl payload files.

        Super-functions are pre-execution hooks that run when payload() loads a file.

        Supported super-functions:
            #$run(funcName)        - Call a function defined in the payload
            #$exec(expression)     - Execute an expression immediately
            #$printl(message)      - Print a message during load

        Example .cssl-pl file:
            void initDatabase() {
                printl("DB initialized");
            }

            #$run(initDatabase);       // Calls initDatabase when payload loads
            #$printl("Payload loaded"); // Prints during load
        """
        super_info = node.value
        super_name = super_info.get('name', '')  # e.g., "#$run", "#$exec", "#$printl"
        args = super_info.get('args', [])

        # Extract the function name part (after #$)
        if super_name.startswith('#$'):
            func_type = super_name[2:]  # "run", "exec", "printl"
        else:
            func_type = super_name

        if func_type == 'run':
            # #$run(funcName) - Call a function by name
            if args:
                func_ref = args[0]
                if isinstance(func_ref, ASTNode):
                    if func_ref.type == 'identifier':
                        func_name = func_ref.value
                    elif func_ref.type == 'call':
                        # Direct call like #$run(setup())
                        return self._eval_call(func_ref)
                    else:
                        func_name = self._evaluate(func_ref)
                else:
                    func_name = str(func_ref)

                # Look up and call the function
                func_node = self.scope.get(func_name)
                if func_node and isinstance(func_node, ASTNode) and func_node.type == 'function':
                    return self._call_function(func_node, [])
                else:
                    raise CSSLRuntimeError(f"#$run: Function '{func_name}' not found", node.line)

        elif func_type == 'exec':
            # #$exec(expression) - Execute an expression
            if args:
                return self._evaluate(args[0])

        elif func_type == 'printl':
            # #$printl(message) - Print a message
            if args:
                msg = self._evaluate(args[0])
                print(str(msg))
                self.output_buffer.append(str(msg))
                return None

        elif func_type == 'print':
            # #$print(message) - Print without newline
            if args:
                msg = self._evaluate(args[0])
                print(str(msg), end='')
                self.output_buffer.append(str(msg))
                return None

        else:
            raise CSSLRuntimeError(f"Unknown super-function: {super_name}", node.line)

        return None

    def _exec_global_assignment(self, node: ASTNode) -> Any:
        """Execute global variable assignment: global Name = value

        Stores the value in _promoted_globals so it can be accessed via @Name
        """
        inner = node.value  # The wrapped assignment/expression node

        if inner is None:
            return None

        # Handle assignment node: global Name = value
        if inner.type == 'assignment':
            target = inner.value.get('target')
            value = self._evaluate(inner.value.get('value'))

            # Get variable name from target
            if isinstance(target, ASTNode):
                if target.type == 'identifier':
                    var_name = target.value
                elif target.type == 'global_ref':
                    # r@Name = value
                    var_name = target.value
                else:
                    var_name = str(target.value) if hasattr(target, 'value') else str(target)
            elif isinstance(target, str):
                var_name = target
            else:
                var_name = str(target)

            # Store in promoted globals for @Name access
            self._promoted_globals[var_name] = value
            # Also store in global scope for regular access
            self.global_scope.set(var_name, value)
            return value

        # Handle expression that results in assignment
        elif inner.type == 'expression':
            result = self._evaluate(inner.value)
            return result

        # Handle typed declaration: global datastruct<int> data;
        elif inner.type == 'typed_declaration':
            # Add global modifier to the declaration
            if isinstance(inner.value, dict):
                inner.value['modifiers'] = inner.value.get('modifiers', []) + ['global']
            result = self._exec_typed_declaration(inner)
            return result

        # Fallback: execute normally
        return self._execute_node(inner)

    def _call_function(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None) -> Any:
        """Call a function node with arguments (positional and named)

        Args:
            func_node: The function AST node
            args: List of positional arguments
            kwargs: Dict of named arguments (param_name -> value)
        """
        func_info = func_node.value
        params = func_info.get('params', [])
        modifiers = func_info.get('modifiers', [])
        kwargs = kwargs or {}

        # Check for undefined modifier - suppress errors if present
        is_undefined = 'undefined' in modifiers

        # Create new scope
        new_scope = Scope(parent=self.scope)

        # Bind parameters - handle both positional and named arguments
        for i, param in enumerate(params):
            # Extract param name and type from dict format: {'name': 'a', 'type': 'int'}
            if isinstance(param, dict):
                param_name = param['name']
                param_type = param.get('type', '')
            else:
                param_name = param
                param_type = ''

            # Check if this is an 'open' parameter - receives all args as a list
            if param_type == 'open' or param_name == 'Params':
                # 'open Params' receives all arguments as a list
                new_scope.set(param_name, list(args))
                new_scope.set('Params', list(args))  # Also set 'Params' for OpenFind
            elif param_name in kwargs:
                # Named argument takes priority
                new_scope.set(param_name, kwargs[param_name])
            elif i < len(args):
                # Positional argument
                new_scope.set(param_name, args[i])
            else:
                new_scope.set(param_name, None)

        # Execute body
        old_scope = self.scope
        self.scope = new_scope

        try:
            for child in func_node.children:
                # Check if exit() was called
                if not self._running:
                    break
                self._execute_node(child)
        except CSSLReturn as ret:
            return ret.value
        except Exception as e:
            # If undefined modifier, suppress all errors
            if is_undefined:
                return None
            raise
        finally:
            self.scope = old_scope

        return None

    def _exec_if(self, node: ASTNode) -> Any:
        """Execute if statement"""
        condition = self._evaluate(node.value.get('condition'))

        if condition:
            for child in node.children:
                if child.type == 'then':
                    for stmt in child.children:
                        self._execute_node(stmt)
                    return None

        # Execute else block
        for child in node.children:
            if child.type == 'else':
                for stmt in child.children:
                    self._execute_node(stmt)
                return None

        return None

    def _exec_while(self, node: ASTNode) -> Any:
        """Execute while loop"""
        while self._running and self._evaluate(node.value.get('condition')):
            try:
                for child in node.children:
                    if not self._running:
                        break
                    self._execute_node(child)
            except CSSLBreak:
                break
            except CSSLContinue:
                continue

        return None

    def _exec_for(self, node: ASTNode) -> Any:
        """Execute Python-style for loop: for (i in range(start, end, step)) { }"""
        var_name = node.value.get('var')
        start = int(self._evaluate(node.value.get('start')))
        end = int(self._evaluate(node.value.get('end')))

        # Optional step parameter (default is 1)
        step_node = node.value.get('step')
        step = int(self._evaluate(step_node)) if step_node else 1

        for i in range(start, end, step):
            if not self._running:
                break
            self.scope.set(var_name, i)
            try:
                for child in node.children:
                    if not self._running:
                        break
                    self._execute_node(child)
            except CSSLBreak:
                break
            except CSSLContinue:
                continue

        return None

    def _exec_c_for(self, node: ASTNode) -> Any:
        """Execute C-style for loop: for (init; condition; update) { }

        Supports:
        - for (int i = 0; i < n; i++) { }
        - for (int i = 0; i < n; i = i + 1) { }
        - for (i = 0; i < n; i += 1) { }
        - for (; condition; ) { }  (infinite loop with condition)
        """
        init = node.value.get('init')
        condition = node.value.get('condition')
        update = node.value.get('update')

        # Execute init statement
        if init:
            var_name = init.value.get('var')
            init_value = self._evaluate(init.value.get('value'))
            self.scope.set(var_name, init_value)
        else:
            var_name = None

        # Main loop
        while self._running:
            # Check condition
            if condition:
                cond_result = self._evaluate(condition)
                if not cond_result:
                    break
            # If no condition, this would be infinite - we still need a way to break

            # Execute body
            try:
                for child in node.children:
                    if not self._running:
                        break
                    self._execute_node(child)
            except CSSLBreak:
                break
            except CSSLContinue:
                pass  # Continue to update, then next iteration

            # Execute update
            if update:
                self._exec_c_for_update(update)

        return None

    def _exec_c_for_update(self, update: 'ASTNode') -> None:
        """Execute the update part of a C-style for loop."""
        var_name = update.value.get('var')
        op = update.value.get('op')
        value_node = update.value.get('value')

        current = self.scope.get(var_name) or 0

        if op == 'increment':
            self.scope.set(var_name, current + 1)
        elif op == 'decrement':
            self.scope.set(var_name, current - 1)
        elif op == 'add':
            add_value = self._evaluate(value_node)
            self.scope.set(var_name, current + add_value)
        elif op == 'subtract':
            sub_value = self._evaluate(value_node)
            self.scope.set(var_name, current - sub_value)
        elif op == 'assign':
            new_value = self._evaluate(value_node)
            self.scope.set(var_name, new_value)

    def _exec_foreach(self, node: ASTNode) -> Any:
        """Execute foreach loop"""
        var_name = node.value.get('var')
        iterable = self._evaluate(node.value.get('iterable'))

        if iterable is None:
            return None

        for item in iterable:
            if not self._running:
                break
            self.scope.set(var_name, item)
            try:
                for child in node.children:
                    if not self._running:
                        break
                    self._execute_node(child)
            except CSSLBreak:
                break
            except CSSLContinue:
                continue

        return None

    def _exec_switch(self, node: ASTNode) -> Any:
        """Execute switch statement"""
        value = self._evaluate(node.value.get('value'))
        matched = False

        for child in node.children:
            if child.type == 'case':
                case_value = self._evaluate(child.value.get('value'))
                if value == case_value:
                    matched = True
                    try:
                        for stmt in child.children:
                            self._execute_node(stmt)
                    except CSSLBreak:
                        return None
            elif child.type == 'default' and not matched:
                try:
                    for stmt in child.children:
                        self._execute_node(stmt)
                except CSSLBreak:
                    return None

        return None

    def _exec_return(self, node: ASTNode) -> Any:
        """Execute return statement.

        Supports multiple return values for shuffled functions:
            return a, b, c;  // Returns tuple (a, b, c)
        """
        if node.value is None:
            raise CSSLReturn(None)

        # Check if this is a multiple return value
        if isinstance(node.value, dict) and node.value.get('multiple'):
            values = [self._evaluate(v) for v in node.value.get('values', [])]
            raise CSSLReturn(tuple(values))

        value = self._evaluate(node.value)
        raise CSSLReturn(value)

    def _exec_break(self, node: ASTNode) -> Any:
        """Execute break statement"""
        raise CSSLBreak()

    def _exec_continue(self, node: ASTNode) -> Any:
        """Execute continue statement"""
        raise CSSLContinue()

    def _exec_try(self, node: ASTNode) -> Any:
        """Execute try/catch block"""
        try:
            for child in node.children:
                if child.type == 'try-block':
                    for stmt in child.children:
                        self._execute_node(stmt)
        except CSSLRuntimeError as e:
            for child in node.children:
                if child.type == 'catch-block':
                    error_var = child.value.get('error_var') if child.value else None
                    if error_var:
                        self.scope.set(error_var, str(e))
                    for stmt in child.children:
                        self._execute_node(stmt)

        return None

    def _exec_createcmd_inject(self, node: ASTNode) -> Any:
        """Execute createcmd injection: createcmd('cmd') <== { action }"""
        command_call = node.value.get('command_call')
        action_block = node.value.get('action')

        # Get command name from the createcmd call arguments
        args = command_call.value.get('args', [])
        if args:
            command_name = self._evaluate(args[0])
        else:
            raise CSSLRuntimeError("createcmd requires a command name argument")

        # Create the command handler function
        def command_handler(*cmd_args):
            # Create a scope for the command execution
            cmd_scope = Scope(parent=self.scope)

            # Set cmd_args in scope
            cmd_scope.set('args', list(cmd_args))
            cmd_scope.set('argc', len(cmd_args))

            old_scope = self.scope
            self.scope = cmd_scope

            try:
                # Execute action block statements
                for child in action_block.children:
                    if child.type == 'function':
                        # If there's a define action { } inside, call it
                        func_name = child.value.get('name')
                        if func_name == 'action':
                            return self._call_function(child, list(cmd_args))
                    else:
                        self._execute_node(child)
            except CSSLReturn as ret:
                return ret.value
            finally:
                self.scope = old_scope

            return None

        # Register the command using the builtin
        self.builtins.builtin_createcmd(command_name, command_handler)

        return command_name

    def _apply_injection_filter(self, source: Any, filter_info) -> Any:
        """Apply injection filter(s) to extract specific data from source.

        Supports both single filter dict and list of filter dicts for chained filters.
        Example: [dynamic::content=10][dynamic::content=100] applies both filters.

        All BruteInjector Helpers:
        - string::where=VALUE - Filter strings containing VALUE
        - string::length=LENGTH - Filter strings of specific length
        - integer::where=VALUE - Filter integers matching VALUE
        - json::key=KEY - Extract values with specific key from JSON/dict
        - json::value=VALUE - Filter by value in JSON/dict
        - array::index=INDEX - Get specific index from array
        - array::length=LENGTH - Filter arrays of specific length
        - vector::where=VALUE - Filter vectors containing VALUE
        - vector::index=INDEX - Get specific index from vector
        - vector::length=LENGTH - Filter vectors of specific length
        - combo::filterdb - Get filter database from combo
        - combo::blocked - Get blocked items from combo
        - dynamic::VarName=VALUE - Filter by dynamic variable value
        - sql::data - Return only SQL-compatible data
        - instance::class - Get classes from object
        - instance::method - Get methods from object
        - instance::var - Get variables from object
        - instance::all - Get all categorized (methods, classes, vars)
        - instance::"ClassName" - Get specific class by name
        - name::"Name" - Filter by name (class, dict key, attribute)
        """
        if not filter_info:
            return source

        # Handle list of filters (chained filters)
        if isinstance(filter_info, list):
            result = source
            for single_filter in filter_info:
                result = self._apply_single_filter(result, single_filter)
            return result

        # Single filter (dict)
        return self._apply_single_filter(source, filter_info)

    def _apply_single_filter(self, source: Any, filter_info: dict) -> Any:
        """Apply a single injection filter to extract specific data from source."""
        if not filter_info:
            return source

        result = source

        for filter_key, filter_value in filter_info.items():
            if '::' in filter_key:
                filter_type, helper = filter_key.split('::', 1)
                filter_val = self._evaluate(filter_value) if isinstance(filter_value, ASTNode) else filter_value

                # === CHECK CUSTOM FILTERS FIRST ===
                custom_key = f"{filter_type}::{helper}"
                if custom_key in _custom_filters:
                    result = _custom_filters[custom_key](result, filter_val, self)
                    continue

                # Check for catch-all custom filter (type::*)
                catchall_key = f"{filter_type}::*"
                if catchall_key in _custom_filters:
                    result = _custom_filters[catchall_key](result, filter_val, self)
                    continue

                # === STRING HELPERS ===
                if filter_type == 'string':
                    if helper == 'where':
                        # Exact match
                        if isinstance(result, str):
                            result = result if result == filter_val else None
                        elif isinstance(result, list):
                            result = [item for item in result if isinstance(item, str) and item == filter_val]
                    elif helper == 'contains':
                        # Contains substring
                        if isinstance(result, str):
                            result = result if filter_val in result else None
                        elif isinstance(result, list):
                            result = [item for item in result if isinstance(item, str) and filter_val in item]
                    elif helper == 'not':
                        # Exclude matching
                        if isinstance(result, str):
                            result = result if result != filter_val else None
                        elif isinstance(result, list):
                            result = [item for item in result if not (isinstance(item, str) and item == filter_val)]
                    elif helper == 'startsWith':
                        if isinstance(result, str):
                            result = result if result.startswith(filter_val) else None
                        elif isinstance(result, list):
                            result = [item for item in result if isinstance(item, str) and item.startswith(filter_val)]
                    elif helper == 'endsWith':
                        if isinstance(result, str):
                            result = result if result.endswith(filter_val) else None
                        elif isinstance(result, list):
                            result = [item for item in result if isinstance(item, str) and item.endswith(filter_val)]
                    elif helper in ('length', 'lenght'):  # Support common typo
                        if isinstance(result, str):
                            result = result if len(result) == filter_val else None
                        elif isinstance(result, list):
                            result = [item for item in result if isinstance(item, str) and len(item) == filter_val]
                    elif helper == 'cut':
                        # Cut string - returns the part BEFORE the index/substring
                        # x = <==[string::cut=2] "20:200-1"  -->  x = "20"
                        # x = <==[string::cut="1.0"] "1.0.0"  -->  x = "" (before "1.0")
                        if isinstance(result, str):
                            if isinstance(filter_val, str):
                                # Cut at substring position
                                idx = result.find(filter_val)
                                result = result[:idx] if idx >= 0 else result
                            else:
                                # Cut at integer index
                                idx = int(filter_val)
                                result = result[:idx] if 0 <= idx <= len(result) else result
                        elif isinstance(result, list):
                            def cut_item(item):
                                if not isinstance(item, str):
                                    return item
                                if isinstance(filter_val, str):
                                    idx = item.find(filter_val)
                                    return item[:idx] if idx >= 0 else item
                                return item[:int(filter_val)]
                            result = [cut_item(item) for item in result]
                    elif helper == 'cutAfter':
                        # Get the part AFTER the index/substring
                        # x = <==[string::cutAfter=2] "20:200-1"  -->  x = ":200-1"
                        # x = <==[string::cutAfter="1.0"] "1.0.0"  -->  x = ".0" (after "1.0")
                        if isinstance(result, str):
                            if isinstance(filter_val, str):
                                # Cut after substring
                                idx = result.find(filter_val)
                                result = result[idx + len(filter_val):] if idx >= 0 else result
                            else:
                                # Cut after integer index
                                idx = int(filter_val)
                                result = result[idx:] if 0 <= idx <= len(result) else result
                        elif isinstance(result, list):
                            def cut_after_item(item):
                                if not isinstance(item, str):
                                    return item
                                if isinstance(filter_val, str):
                                    idx = item.find(filter_val)
                                    return item[idx + len(filter_val):] if idx >= 0 else item
                                return item[int(filter_val):]
                            result = [cut_after_item(item) for item in result]
                    elif helper == 'slice':
                        # Slice string with start:end format (e.g., "2:5")
                        if isinstance(result, str) and isinstance(filter_val, str) and ':' in filter_val:
                            parts = filter_val.split(':')
                            start = int(parts[0]) if parts[0] else 0
                            end = int(parts[1]) if parts[1] else len(result)
                            result = result[start:end]
                    elif helper == 'split':
                        # Split string by delimiter
                        if isinstance(result, str):
                            result = result.split(str(filter_val))
                    elif helper == 'replace':
                        # Replace in string (format: "old:new")
                        if isinstance(result, str) and isinstance(filter_val, str) and ':' in filter_val:
                            parts = filter_val.split(':', 1)
                            if len(parts) == 2:
                                result = result.replace(parts[0], parts[1])
                    elif helper == 'upper':
                        if isinstance(result, str):
                            result = result.upper()
                    elif helper == 'lower':
                        if isinstance(result, str):
                            result = result.lower()
                    elif helper == 'trim':
                        if isinstance(result, str):
                            result = result.strip()

                # === INTEGER HELPERS ===
                elif filter_type == 'integer':
                    if helper == 'where':
                        if isinstance(result, int):
                            result = result if result == filter_val else None
                        elif isinstance(result, list):
                            result = [item for item in result if isinstance(item, int) and item == filter_val]

                # === JSON HELPERS ===
                elif filter_type == 'json':
                    if helper == 'key':
                        if isinstance(result, dict):
                            result = result.get(filter_val)
                        elif isinstance(result, list):
                            result = [item.get(filter_val) for item in result if isinstance(item, dict) and filter_val in item]
                    elif helper == 'value':
                        if isinstance(result, dict):
                            # Find key(s) with matching value
                            matches = [k for k, v in result.items() if v == filter_val]
                            result = matches[0] if len(matches) == 1 else matches
                        elif isinstance(result, list):
                            result = [item for item in result if (isinstance(item, dict) and filter_val in item.values())]

                # === ARRAY HELPERS ===
                elif filter_type == 'array':
                    if helper == 'index':
                        if isinstance(result, (list, tuple)):
                            idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
                            if 0 <= idx < len(result):
                                result = result[idx]
                            else:
                                result = None
                    elif helper in ('length', 'lenght'):  # Support common typo
                        if isinstance(result, (list, tuple)):
                            result = result if len(result) == filter_val else []
                    elif helper == 'where':
                        if isinstance(result, list):
                            result = [item for item in result if item == filter_val]

                # === VECTOR HELPERS ===
                elif filter_type == 'vector':
                    if helper == 'index':
                        if isinstance(result, (list, tuple)):
                            idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
                            if 0 <= idx < len(result):
                                result = result[idx]
                            else:
                                result = None
                    elif helper in ('length', 'lenght'):  # Support common typo
                        if isinstance(result, (list, tuple)):
                            result = result if len(result) == filter_val else []
                    elif helper == 'where':
                        if isinstance(result, list):
                            result = [item for item in result if item == filter_val]

                # === COMBO HELPERS ===
                elif filter_type == 'combo':
                    if helper == 'filterdb':
                        if hasattr(result, '_filterdb'):
                            result = result._filterdb
                        elif hasattr(result, 'filterdb'):
                            result = result.filterdb
                    elif helper == 'blocked':
                        if hasattr(result, '_blocked'):
                            result = result._blocked
                        elif hasattr(result, 'blocked'):
                            result = result.blocked

                # === DYNAMIC HELPERS ===
                elif filter_type == 'dynamic':
                    if helper == 'content':
                        # dynamic::content=VALUE - General content filter for any type
                        # Filters elements where content equals VALUE
                        if isinstance(result, (list, tuple)):
                            # Filter list/tuple elements by content value
                            result = [item for item in result if item == filter_val]
                        elif isinstance(result, dict):
                            # Filter dict by values matching filter_val
                            result = {k: v for k, v in result.items() if v == filter_val}
                        elif result == filter_val:
                            pass  # Keep result if it matches
                        else:
                            result = None
                    elif helper == 'not':
                        # dynamic::not=VALUE - Exclude elements equal to VALUE
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if item != filter_val]
                        elif isinstance(result, dict):
                            result = {k: v for k, v in result.items() if v != filter_val}
                        elif result != filter_val:
                            pass  # Keep result if it doesn't match
                        else:
                            result = None
                    elif helper == 'gt':
                        # dynamic::gt=VALUE - Greater than
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if item > filter_val]
                        elif result > filter_val:
                            pass
                        else:
                            result = None
                    elif helper == 'lt':
                        # dynamic::lt=VALUE - Less than
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if item < filter_val]
                        elif result < filter_val:
                            pass
                        else:
                            result = None
                    elif helper == 'gte':
                        # dynamic::gte=VALUE - Greater than or equal
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if item >= filter_val]
                        elif result >= filter_val:
                            pass
                        else:
                            result = None
                    elif helper == 'lte':
                        # dynamic::lte=VALUE - Less than or equal
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if item <= filter_val]
                        elif result <= filter_val:
                            pass
                        else:
                            result = None
                    elif helper == 'mod':
                        # dynamic::mod=VALUE - Modulo filter (item % VALUE == 0)
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if isinstance(item, (int, float)) and item % filter_val == 0]
                        elif isinstance(result, (int, float)) and result % filter_val == 0:
                            pass
                        else:
                            result = None
                    elif helper == 'range':
                        # dynamic::range="min:max" - Filter values in range
                        if isinstance(filter_val, str) and ':' in filter_val:
                            parts = filter_val.split(':')
                            min_val = int(parts[0]) if parts[0] else None
                            max_val = int(parts[1]) if parts[1] else None
                            if isinstance(result, (list, tuple)):
                                def in_range(x):
                                    if min_val is not None and x < min_val:
                                        return False
                                    if max_val is not None and x > max_val:
                                        return False
                                    return True
                                result = [item for item in result if in_range(item)]
                            elif isinstance(result, (int, float)):
                                if min_val is not None and result < min_val:
                                    result = None
                                elif max_val is not None and result > max_val:
                                    result = None
                    elif helper == 'even':
                        # dynamic::even - Filter even numbers
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if isinstance(item, int) and item % 2 == 0]
                        elif isinstance(result, int) and result % 2 == 0:
                            pass
                        else:
                            result = None
                    elif helper == 'odd':
                        # dynamic::odd - Filter odd numbers
                        if isinstance(result, (list, tuple)):
                            result = [item for item in result if isinstance(item, int) and item % 2 != 0]
                        elif isinstance(result, int) and result % 2 != 0:
                            pass
                        else:
                            result = None
                    else:
                        # dynamic::VarName=VALUE - Match if variable equals value
                        var_name = helper
                        var_value = self.scope.get(var_name)
                        if var_value == filter_val:
                            pass  # Keep result
                        else:
                            result = None

                # === SQL HELPERS ===
                elif filter_type == 'sql':
                    if helper == 'data':
                        # Return only SQL-compatible data types
                        if isinstance(result, (int, str, bool, float, list, dict)):
                            pass  # Keep result
                        else:
                            result = str(result)  # Convert to string

                # === INSTANCE HELPERS ===
                # Works on CSSLInstance, Python objects, dicts, modules
                elif filter_type == 'instance':
                    from .cssl_types import CSSLInstance
                    import inspect

                    if isinstance(result, CSSLInstance):
                        # Filter CSSL instances
                        if helper in ('class', 'classes'):
                            classes = {}
                            for name, member in result._members.items():
                                if isinstance(member, CSSLInstance):
                                    classes[name] = member
                            result = classes if classes else None
                        elif helper in ('method', 'methods'):
                            result = dict(result._class.methods)
                        elif helper in ('var', 'vars', 'variable', 'variables'):
                            vars_dict = {}
                            for name, member in result._members.items():
                                if not isinstance(member, CSSLInstance):
                                    vars_dict[name] = member
                            result = vars_dict
                        elif helper in ('all',):
                            result = {
                                'methods': list(result._class.methods.keys()),
                                'classes': [result._class.name] + [m._class.name for m in result._members.values() if isinstance(m, CSSLInstance)],
                                'vars': [n for n, m in result._members.items() if not isinstance(m, CSSLInstance)]
                            }
                        else:
                            # Filter by class name
                            class_name = filter_val if filter_val else helper
                            found = None
                            if result._class.name == class_name:
                                found = result
                            else:
                                for name, member in result._members.items():
                                    if isinstance(member, CSSLInstance) and member._class.name == class_name:
                                        found = member
                                        break
                            result = found
                    elif isinstance(result, dict):
                        # Filter dicts
                        if helper in ('class', 'classes'):
                            result = {k: v for k, v in result.items() if inspect.isclass(v)}
                        elif helper in ('method', 'methods', 'func', 'function', 'functions'):
                            result = {k: v for k, v in result.items() if callable(v)}
                        elif helper in ('var', 'vars', 'variable', 'variables'):
                            result = {k: v for k, v in result.items() if not callable(v) and not inspect.isclass(v)}
                        else:
                            # Get by key
                            key_name = filter_val if filter_val else helper
                            result = result.get(key_name)
                    elif hasattr(result, '__dict__') or hasattr(result, '__class__'):
                        # Filter Python objects/modules
                        if helper in ('class', 'classes'):
                            classes = {}
                            for name in dir(result):
                                if not name.startswith('_'):
                                    attr = getattr(result, name, None)
                                    if inspect.isclass(attr):
                                        classes[name] = attr
                            result = classes if classes else None
                        elif helper in ('method', 'methods', 'func', 'function', 'functions'):
                            methods = {}
                            for name in dir(result):
                                if not name.startswith('_'):
                                    attr = getattr(result, name, None)
                                    if callable(attr):
                                        methods[name] = attr
                            result = methods if methods else None
                        elif helper in ('var', 'vars', 'variable', 'variables'):
                            vars_dict = {}
                            for name in dir(result):
                                if not name.startswith('_'):
                                    attr = getattr(result, name, None)
                                    if not callable(attr) and not inspect.isclass(attr):
                                        vars_dict[name] = attr
                            result = vars_dict if vars_dict else None
                        elif helper in ('all',):
                            all_info = {'methods': [], 'classes': [], 'vars': []}
                            for name in dir(result):
                                if not name.startswith('_'):
                                    attr = getattr(result, name, None)
                                    if inspect.isclass(attr):
                                        all_info['classes'].append(name)
                                    elif callable(attr):
                                        all_info['methods'].append(name)
                                    else:
                                        all_info['vars'].append(name)
                            result = all_info
                        else:
                            # Get attribute by name
                            attr_name = filter_val if filter_val else helper
                            result = getattr(result, attr_name, None)

                # === NAME HELPERS ===
                # General name filter for any object type
                elif filter_type == 'name':
                    from .cssl_types import CSSLInstance
                    target_name = filter_val if filter_val else helper

                    if isinstance(result, CSSLInstance):
                        if result._class.name == target_name:
                            pass  # Keep result
                        else:
                            found = None
                            for name, member in result._members.items():
                                if isinstance(member, CSSLInstance) and member._class.name == target_name:
                                    found = member
                                    break
                            result = found
                    elif isinstance(result, dict):
                        result = result.get(target_name)
                    elif isinstance(result, list):
                        result = [item for item in result if str(item) == target_name or (hasattr(item, 'name') and item.name == target_name)]
                    elif hasattr(result, target_name):
                        result = getattr(result, target_name)
                    elif hasattr(result, '__class__') and result.__class__.__name__ == target_name:
                        pass  # Keep result if class name matches
                    else:
                        result = None

        return result

    def _exec_inject(self, node: ASTNode) -> Any:
        """Execute inject operation (<==, +<==, -<==)

        Modes:
        - replace: target <== source (replace target with source)
        - add: target +<== source (copy & add to target)
        - move: target -<== source (move from source, remove from source)
        """
        target = node.value.get('target')
        source_node = node.value.get('source')
        mode = node.value.get('mode', 'replace')
        filter_info = node.value.get('filter')

        # Check if target is a function call (for permanent injection)
        if isinstance(target, ASTNode) and target.type == 'call':
            callee = target.value.get('callee')
            if isinstance(callee, ASTNode) and callee.type == 'identifier':
                func_name = callee.value
                self.register_function_injection(func_name, source_node)
                return None

        # Check if source is an action_block with %<name> captures
        # If so, capture values NOW and evaluate the block with those captures
        if isinstance(source_node, ASTNode) and source_node.type == 'action_block':
            # Scan for %<name> captured references and capture their current values
            captured_values = self._scan_and_capture_refs(source_node)
            old_captured = self._current_captured_values.copy()
            self._current_captured_values = captured_values
            try:
                # Execute the action block and get the last expression's value
                source = self._evaluate_action_block(source_node)
            finally:
                self._current_captured_values = old_captured
        else:
            # Evaluate source normally
            source = self._evaluate(source_node)

        # Apply filter if present
        if filter_info:
            source = self._apply_injection_filter(source, filter_info)

        # Get current target value for add/move modes
        current_value = None
        if mode in ('add', 'move'):
            try:
                current_value = self._evaluate(target)
            except Exception:
                # Target might not exist yet, that's okay for add mode
                current_value = None

        # Determine final value based on mode
        if mode == 'replace':
            final_value = source
        elif mode == 'add':
            # Copy & add - preserve target and add source
            from .cssl_types import CSSLInstance

            # Special handling for CSSLInstance - merge classes
            if isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
                # Add the new class instance as a member with class name as key
                class_name = source._class.name
                current_value._members[class_name] = source
                final_value = current_value
            elif isinstance(current_value, list):
                if isinstance(source, list):
                    final_value = current_value + source
                else:
                    final_value = current_value + [source]
            elif isinstance(current_value, dict) and isinstance(source, dict):
                final_value = {**current_value, **source}
            elif isinstance(current_value, str) and isinstance(source, str):
                final_value = current_value + source
            elif current_value is None:
                final_value = [source] if not isinstance(source, list) else source
            else:
                final_value = [current_value, source]
        elif mode == 'move':
            # Move & remove from source
            final_value = source
            # Clear the source - handle all node types
            if isinstance(source_node, ASTNode):
                if source_node.type == 'identifier':
                    self.scope.set(source_node.value, None)
                elif source_node.type == 'module_ref':
                    self._set_module_value(source_node.value, None)
                elif source_node.type == 'member_access':
                    self._set_member(source_node, None)
        else:
            final_value = source

        # Set the target
        if target.type == 'identifier':
            self.scope.set(target.value, final_value)
        elif target.type == 'module_ref':
            self._set_module_value(target.value, final_value)
        elif target.type == 'shared_ref':
            # $Name <== value - create/update shared object
            from ..cssl_bridge import _live_objects, SharedObjectProxy
            name = target.value
            _live_objects[name] = final_value
            self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
        elif target.type == 'member_access':
            self._set_member(target, final_value)
        elif target.type == 'call':
            callee = target.value.get('callee')
            if isinstance(callee, ASTNode) and callee.type == 'member_access':
                obj = self._evaluate(callee.value.get('object'))
                method_name = callee.value.get('member')
                if method_name == 'add' and isinstance(obj, list):
                    obj.append(final_value)
                    return final_value

        return final_value

    def _exec_receive(self, node: ASTNode) -> Any:
        """Execute receive operation (==>, ==>+, -==>)

        Modes:
        - replace: source ==> target (move source to target, replace)
        - add: source ==>+ target (copy source to target, add)
        - move: source -==> target (move from source, remove)
        """
        source_node = node.value.get('source')
        target = node.value.get('target')
        mode = node.value.get('mode', 'replace')
        filter_info = node.value.get('filter')

        # Evaluate source
        source = self._evaluate(source_node)

        # Apply filter if present
        if filter_info:
            source = self._apply_injection_filter(source, filter_info)

        # Get current target value for add mode
        current_value = None
        if mode == 'add':
            current_value = self._evaluate(target)

        # Determine final value based on mode
        if mode == 'replace':
            final_value = source
        elif mode == 'add':
            if isinstance(current_value, list):
                if isinstance(source, list):
                    final_value = current_value + source
                else:
                    final_value = current_value + [source]
            elif isinstance(current_value, dict) and isinstance(source, dict):
                final_value = {**current_value, **source}
            elif isinstance(current_value, str) and isinstance(source, str):
                final_value = current_value + source
            elif current_value is None:
                final_value = [source] if not isinstance(source, list) else source
            else:
                final_value = [current_value, source]
        elif mode == 'move':
            final_value = source
            # Remove filtered elements from source (not clear entirely)
            if isinstance(source_node, ASTNode):
                if filter_info:
                    # Get original source value
                    original_source = self._evaluate(source_node)
                    if isinstance(original_source, list) and isinstance(final_value, list):
                        # Remove filtered items from original list
                        remaining = [item for item in original_source if item not in final_value]
                        if source_node.type == 'identifier':
                            self.scope.set(source_node.value, remaining)
                        elif source_node.type == 'module_ref':
                            self._set_module_value(source_node.value, remaining)
                        elif source_node.type == 'member_access':
                            self._set_member(source_node, remaining)
                    else:
                        # Single value filter - set source to None
                        if source_node.type == 'identifier':
                            self.scope.set(source_node.value, None)
                        elif source_node.type == 'module_ref':
                            self._set_module_value(source_node.value, None)
                        elif source_node.type == 'member_access':
                            self._set_member(source_node, None)
                else:
                    # No filter - clear entire source
                    if source_node.type == 'identifier':
                        self.scope.set(source_node.value, None)
                    elif source_node.type == 'module_ref':
                        self._set_module_value(source_node.value, None)
                    elif source_node.type == 'member_access':
                        self._set_member(source_node, None)
        else:
            final_value = source

        # Set the target
        if target.type == 'identifier':
            self.scope.set(target.value, final_value)
        elif target.type == 'module_ref':
            self._set_module_value(target.value, final_value)
        elif target.type == 'shared_ref':
            # value ==> $Name - create/update shared object
            from ..cssl_bridge import _live_objects, SharedObjectProxy
            name = target.value
            _live_objects[name] = final_value
            self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
        elif target.type == 'instance_ref':
            # value ==> instance<"name"> - create/update shared object
            from ..cssl_bridge import _live_objects, SharedObjectProxy
            name = target.value
            _live_objects[name] = final_value
            self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
        elif target.type == 'member_access':
            self._set_member(target, final_value)
        elif target.type == 'typed_declaration':
            # Handle typed target: source ==> datastruct<dynamic> Output
            var_name = target.value.get('name')
            type_name = target.value.get('type_name')
            element_type = target.value.get('element_type', 'dynamic')

            # Create appropriate container with final_value
            from .cssl_types import (create_datastruct, create_vector, create_array,
                                      create_stack, create_list, create_dictionary, create_map)

            container = None
            if type_name == 'datastruct':
                container = create_datastruct(element_type)
            elif type_name == 'vector':
                container = create_vector(element_type)
            elif type_name == 'array':
                container = create_array(element_type)
            elif type_name == 'stack':
                container = create_stack(element_type)
            elif type_name == 'list':
                container = create_list(element_type)
            elif type_name == 'dictionary':
                container = create_dictionary()
            elif type_name == 'map':
                container = create_map()

            if container is not None:
                # Add final_value to container
                if isinstance(final_value, (list, tuple)):
                    container.extend(final_value)
                elif final_value is not None:
                    container.append(final_value)
                self.scope.set(var_name, container)
            else:
                # Unknown type, just set the value directly
                self.scope.set(var_name, final_value)

        return final_value

    def _exec_infuse(self, node: ASTNode) -> Any:
        """Execute code infusion (<<==, +<<==, -<<==)

        Modes:
        - replace: func <<== { code } - REPLACES function body (original won't execute)
        - add: func +<<== { code } - ADDS code to function (both execute)
        - remove: func -<<== { code } - REMOVES matching code from function

        Also supports expression form: func <<== %exit() (wraps in action_block)
        """
        target = node.value.get('target')
        code_block = node.value.get('code')
        source_expr = node.value.get('source')  # For expression form: func <<== expr
        mode = node.value.get('mode', 'replace')  # Default is REPLACE for <<==

        # If source expression is provided instead of code block, wrap it
        if code_block is None and source_expr is not None:
            # Wrap in expression node so _execute_node can handle it
            expr_node = ASTNode('expression', value=source_expr)
            code_block = ASTNode('action_block', children=[expr_node])

        # Get function name from target
        func_name = None
        if isinstance(target, ASTNode):
            if target.type == 'identifier':
                func_name = target.value
            elif target.type == 'call':
                callee = target.value.get('callee')
                if isinstance(callee, ASTNode) and callee.type == 'identifier':
                    func_name = callee.value

        if not func_name or code_block is None:
            return None

        if mode == 'add':
            # +<<== : Add code to function (both injection + original execute)
            self.register_function_injection(func_name, code_block)
            self._function_replaced[func_name] = False  # Don't replace, just add
        elif mode == 'replace':
            # <<== : Replace function body (only injection executes, original skipped)
            # Save original function BEFORE replacing (for original() access)
            if func_name not in self._original_functions:
                # Try to find original in scope or builtins
                original = self.scope.get(func_name)
                if original is None:
                    original = getattr(self.builtins, f'builtin_{func_name}', None)
                if original is not None:
                    self._original_functions[func_name] = original
            # Capture %<name> references at registration time
            captured_values = self._scan_and_capture_refs(code_block)
            self._function_injections[func_name] = [(code_block, captured_values)]
            self._function_replaced[func_name] = True  # Mark as replaced
        elif mode == 'remove':
            # -<<== or -<<==[n] : Remove matching code from function body
            remove_index = node.value.get('index')

            if func_name in self._function_injections:
                if remove_index is not None:
                    # Indexed removal: -<<==[n] removes only the nth injection
                    if 0 <= remove_index < len(self._function_injections[func_name]):
                        self._function_injections[func_name].pop(remove_index)
                else:
                    # No index: -<<== removes all injections
                    self._function_injections[func_name] = []
            self._function_replaced[func_name] = False

        return None

    def _exec_infuse_right(self, node: ASTNode) -> Any:
        """Execute right-side code infusion (==>>)"""
        source = node.value.get('source')
        target = node.value.get('target')
        mode = node.value.get('mode', 'replace')

        # Similar to infuse but direction is reversed
        func_name = None
        if isinstance(target, ASTNode):
            if target.type == 'identifier':
                func_name = target.value
            elif target.type == 'call':
                callee = target.value.get('callee')
                if isinstance(callee, ASTNode) and callee.type == 'identifier':
                    func_name = callee.value

        if func_name and isinstance(source, ASTNode):
            self.register_function_injection(func_name, source)

        return None

    def _exec_flow(self, node: ASTNode) -> Any:
        """Execute flow operation (-> or <-)"""
        source = self._evaluate(node.value.get('source'))
        target = node.value.get('target')

        # Flow sends data to a target (could be function call or variable)
        if target.type == 'call':
            callee = self._evaluate(target.value.get('callee'))
            args = [source] + [self._evaluate(a) for a in target.value.get('args', [])]

            if callable(callee):
                return callee(*args)
            elif isinstance(callee, ASTNode) and callee.type == 'function':
                return self._call_function(callee, args)
        elif target.type == 'identifier':
            self.scope.set(target.value, source)
        elif target.type == 'module_ref':
            self._set_module_value(target.value, source)
        elif target.type == 'shared_ref':
            # $Name <== value - create/update shared object
            from ..cssl_bridge import _live_objects, SharedObjectProxy
            name = target.value
            _live_objects[name] = source
            self.global_scope.set(f'${name}', SharedObjectProxy(name, source))
        elif target.type == 'member_access':
            self._set_member(target, source)

        return source

    def _exec_assignment(self, node: ASTNode) -> Any:
        """Execute assignment"""
        target = node.value.get('target')
        value = self._evaluate(node.value.get('value'))

        if isinstance(target, ASTNode):
            if target.type == 'identifier':
                self.scope.set(target.value, value)
            elif target.type == 'global_ref':
                # r@Name = value - store in promoted globals
                self._promoted_globals[target.value] = value
                self.global_scope.set(target.value, value)
            elif target.type == 'shared_ref':
                # $Name = value - create/update shared object
                from ..cssl_bridge import _live_objects, SharedObjectProxy
                name = target.value
                _live_objects[name] = value
                self.global_scope.set(f'${name}', SharedObjectProxy(name, value))
            elif target.type == 'module_ref':
                # @Name = value - store in promoted globals (like global keyword)
                self._promoted_globals[target.value] = value
                self.global_scope.set(target.value, value)
            elif target.type == 'member_access':
                self._set_member(target, value)
            elif target.type == 'index_access':
                self._set_index(target, value)
            elif target.type == 'this_access':
                # this->member = value
                if self._current_instance is None:
                    raise CSSLRuntimeError("'this' used outside of class method context")
                member = target.value.get('member')
                self._current_instance.set_member(member, value)
        elif isinstance(target, str):
            self.scope.set(target, value)

        return value

    def _exec_tuple_assignment(self, node: ASTNode) -> Any:
        """Execute tuple unpacking assignment: a, b, c = shuffled_func()

        Used with shuffled functions that return multiple values.
        """
        targets = node.value.get('targets', [])
        value = self._evaluate(node.value.get('value'))

        # Convert value to list if it's a tuple or iterable
        if isinstance(value, (list, tuple)):
            values = list(value)
        elif hasattr(value, '__iter__') and not isinstance(value, (str, dict)):
            values = list(value)
        else:
            # Single value - assign to first target only
            values = [value]

        # Assign values to targets
        for i, target in enumerate(targets):
            if i < len(values):
                var_name = target.value if isinstance(target, ASTNode) else target
                self.scope.set(var_name, values[i])
            else:
                # More targets than values - set to None
                var_name = target.value if isinstance(target, ASTNode) else target
                self.scope.set(var_name, None)

        # Assignment statements don't produce a visible result
        return None

    def _exec_expression(self, node: ASTNode) -> Any:
        """Execute expression statement"""
        return self._evaluate(node.value)

    def _exec_type_instantiation(self, node: ASTNode) -> Any:
        """Execute type instantiation as statement (e.g., vector<int>)"""
        return self._evaluate(node)

    def _exec_await(self, node: ASTNode) -> Any:
        """Execute await statement - waits for expression to complete"""
        # Evaluate the awaited expression
        # The expression is typically a call like wait_for_booted()
        result = self._evaluate(node.value)

        # If result is a callable (like a coroutine or future), wait for it
        if hasattr(result, '__await__'):
            import asyncio
            loop = asyncio.get_event_loop()
            return loop.run_until_complete(result)

        # If result is a boolean condition waiting function, it already handled the waiting
        return result

    def _exec_then(self, node: ASTNode) -> Any:
        """Execute then block"""
        for child in node.children:
            self._execute_node(child)
        return None

    def _exec_else(self, node: ASTNode) -> Any:
        """Execute else block"""
        for child in node.children:
            self._execute_node(child)
        return None

    def _exec_try_block(self, node: ASTNode) -> Any:
        """Execute try block"""
        for child in node.children:
            self._execute_node(child)
        return None

    def _exec_catch_block(self, node: ASTNode) -> Any:
        """Execute catch block"""
        for child in node.children:
            self._execute_node(child)
        return None

    def _evaluate(self, node: Any) -> Any:
        """Evaluate an expression node to get its value"""
        if node is None:
            return None

        if not isinstance(node, ASTNode):
            return node

        if node.type == 'literal':
            value = node.value
            # String interpolation - replace {var} or <var> with scope values
            if isinstance(value, str):
                has_fstring = '{' in value and '}' in value
                has_legacy = '<' in value and '>' in value
                if has_fstring or has_legacy:
                    value = self._interpolate_string(value)
            return value

        # NEW: Type literals (list, dict) - create empty instances
        if node.type == 'type_literal':
            type_name = node.value
            if type_name == 'list':
                return []
            elif type_name == 'dict':
                return {}
            return None

        if node.type == 'identifier':
            name = node.value
            value = self.scope.get(name)
            # Fallback to global scope
            if value is None:
                value = self.global_scope.get(name)
            # Fallback to promoted globals (from 'global' keyword)
            if value is None:
                value = self._promoted_globals.get(name)
            # Fallback to builtins
            if value is None and self.builtins.has_function(name):
                return self.builtins.get_function(name)
            return value

        if node.type == 'module_ref':
            # User-defined globals have priority over SDK modules
            # Check promoted globals first, then global scope, then SDK modules
            value = self._promoted_globals.get(node.value)
            if value is None:
                value = self.global_scope.get(node.value)
            if value is None:
                value = self.get_module(node.value)  # SDK modules as fallback
            return value

        if node.type == 'self_ref':
            # s@<name> reference to global struct
            return self.get_global_struct(node.value)

        if node.type == 'global_ref':
            # r@<name> global variable reference
            # Check promoted globals first, then global scope
            value = self._promoted_globals.get(node.value)
            if value is None:
                value = self.global_scope.get(node.value)
            return value

        if node.type == 'shared_ref':
            # $<name> shared object reference
            # Returns the SharedObjectProxy for live access
            from ..cssl_bridge import _live_objects, SharedObjectProxy
            name = node.value
            if name in _live_objects:
                return SharedObjectProxy(name, _live_objects[name])
            # Check if stored in runtime's scope as $name
            scoped_val = self.global_scope.get(f'${name}')
            if scoped_val is not None:
                return scoped_val
            raise CSSLRuntimeError(f"Shared object '${name}' not found. Use share() to share objects.")

        if node.type == 'captured_ref':
            # %<name> captured reference - use value captured at infusion registration time
            name = node.value
            # First check captured values from current injection context
            if name in self._current_captured_values:
                captured_value = self._current_captured_values[name]
                # Only use captured value if it's not None
                if captured_value is not None:
                    return captured_value
            # Fall back to normal resolution if not captured or capture was None
            value = self.scope.get(name)
            if value is None:
                value = self.global_scope.get(name)
            if value is None:
                # For critical builtins like 'exit', create direct wrapper
                if name == 'exit':
                    runtime = self
                    value = lambda code=0, rt=runtime: rt.exit(code)
                else:
                    value = getattr(self.builtins, f'builtin_{name}', None)
            if value is None:
                # Check original functions (for replaced functions)
                value = self._original_functions.get(name)
            if value is not None:
                return value
            raise CSSLRuntimeError(f"Captured reference '%{name}' not found.")

        if node.type == 'instance_ref':
            # instance<"name"> - get shared instance by name
            # Works like $name but with explicit syntax
            from ..cssl_bridge import _live_objects, SharedObjectProxy
            name = node.value
            if name in _live_objects:
                return SharedObjectProxy(name, _live_objects[name])
            # Check if stored in runtime's scope
            scoped_val = self.global_scope.get(f'${name}')
            if scoped_val is not None:
                return scoped_val
            # Return None if instance doesn't exist (can be created via ==>)
            return None

        if node.type == 'new':
            # Create new instance of a class: new ClassName(args)
            return self._eval_new(node)

        if node.type == 'this_access':
            # this->member access
            return self._eval_this_access(node)

        if node.type == 'type_instantiation':
            # Create new instance of a type: stack<string>, vector<int>, map<K,V>, etc.
            type_name = node.value.get('type')
            element_type = node.value.get('element_type', 'dynamic')
            value_type = node.value.get('value_type')  # For map<K, V>
            init_values = node.value.get('init_values')  # For inline init: map<K,V>{...}

            # Helper to populate container with init values
            def _populate_container(container, init_vals):
                if init_vals and isinstance(init_vals, list):
                    for val_node in init_vals:
                        val = self._evaluate(val_node) if isinstance(val_node, ASTNode) else val_node
                        if hasattr(container, 'push'):
                            container.push(val)
                        elif hasattr(container, 'add'):
                            container.add(val)
                        elif hasattr(container, 'append'):
                            container.append(val)
                return container

            if type_name == 'stack':
                s = Stack(element_type)
                return _populate_container(s, init_values)
            elif type_name == 'vector':
                v = Vector(element_type)
                return _populate_container(v, init_values)
            elif type_name == 'datastruct':
                d = DataStruct(element_type)
                return _populate_container(d, init_values)
            elif type_name == 'shuffled':
                return Shuffled(element_type)
            elif type_name == 'iterator':
                return Iterator(element_type)
            elif type_name == 'combo':
                return Combo(element_type)
            elif type_name == 'dataspace':
                return DataSpace(element_type)
            elif type_name == 'openquote':
                return OpenQuote()
            elif type_name == 'array':
                a = Array(element_type)
                return _populate_container(a, init_values)
            elif type_name == 'list':
                l = List(element_type)
                return _populate_container(l, init_values)
            elif type_name in ('dictionary', 'dict'):
                return Dictionary(element_type)
            elif type_name == 'map':
                # Create Map with key_type and value_type
                m = Map(element_type, value_type or 'dynamic')
                # If inline initialization provided, populate the map
                if init_values:
                    for key, value_node in init_values.items():
                        value = self._evaluate(value_node)
                        m.insert(key, value)
                return m
            else:
                return None

        if node.type == 'binary':
            return self._eval_binary(node)

        if node.type == 'unary':
            return self._eval_unary(node)

        if node.type == 'call':
            return self._eval_call(node)

        if node.type == 'typed_call':
            # Handle OpenFind<type>(args) style calls
            return self._eval_typed_call(node)

        if node.type == 'member_access':
            return self._eval_member_access(node)

        if node.type == 'index_access':
            return self._eval_index_access(node)

        if node.type == 'array':
            return [self._evaluate(elem) for elem in node.value]

        if node.type == 'object':
            return {k: self._evaluate(v) for k, v in node.value.items()}

        if node.type == 'reference':
            # &variable - return a reference object wrapping the actual value
            inner = node.value
            if isinstance(inner, ASTNode):
                if inner.type == 'identifier':
                    # Return a reference wrapper with the variable name
                    return {'__ref__': True, 'name': inner.value, 'value': self.scope.get(inner.value)}
                elif inner.type == 'module_ref':
                    return {'__ref__': True, 'name': inner.value, 'value': self.get_module(inner.value)}
            return {'__ref__': True, 'value': self._evaluate(inner)}

        # Handle action_block - execute and return last expression value
        if node.type == 'action_block':
            return self._evaluate_action_block(node)

        return None

    def _evaluate_action_block(self, node: ASTNode) -> Any:
        """Evaluate an action block and return the last expression's value.

        Used for: v <== { %version; } - captures %version at this moment

        Returns the value of the last expression in the block.
        If the block contains a captured_ref (%name), that's what gets returned.
        """
        last_value = None
        for child in node.children:
            if child.type == 'captured_ref':
                # Direct captured reference - return its value
                last_value = self._evaluate(child)
            elif child.type == 'expression':
                # Expression statement - evaluate and keep value
                last_value = self._evaluate(child.value if hasattr(child, 'value') else child)
            elif child.type == 'identifier':
                # Just an identifier - evaluate it
                last_value = self._evaluate(child)
            elif child.type in ('call', 'member_access', 'binary', 'unary'):
                # Expression types
                last_value = self._evaluate(child)
            else:
                # Execute other statements
                result = self._execute_node(child)
                if result is not None:
                    last_value = result
        return last_value

    def _eval_binary(self, node: ASTNode) -> Any:
        """Evaluate binary operation with auto-casting support"""
        op = node.value.get('op')
        left = self._evaluate(node.value.get('left'))
        right = self._evaluate(node.value.get('right'))

        # === AUTO-CAST FOR STRING OPERATIONS ===
        if op == '+':
            # String concatenation with auto-cast
            if isinstance(left, str) or isinstance(right, str):
                return str(left if left is not None else '') + str(right if right is not None else '')
            # List concatenation
            if isinstance(left, list) and isinstance(right, list):
                result = type(left)(left._element_type) if hasattr(left, '_element_type') else []
                if hasattr(result, 'extend'):
                    result.extend(left)
                    result.extend(right)
                else:
                    result = list(left) + list(right)
                return result
            # Numeric addition
            return (left or 0) + (right or 0)

        if op == '-':
            return self._to_number(left) - self._to_number(right)

        if op == '*':
            # String repeat: "abc" * 3 = "abcabcabc"
            if isinstance(left, str) and isinstance(right, (int, float)):
                return left * int(right)
            if isinstance(right, str) and isinstance(left, (int, float)):
                return right * int(left)
            return self._to_number(left) * self._to_number(right)

        if op == '/':
            r = self._to_number(right)
            return self._to_number(left) / r if r != 0 else 0

        if op == '//':
            r = self._to_number(right)
            return self._to_number(left) // r if r != 0 else 0

        if op == '%':
            r = self._to_number(right)
            return self._to_number(left) % r if r != 0 else 0

        if op == '**':
            return self._to_number(left) ** self._to_number(right)

        # === COMPARISON OPERATIONS ===
        if op == '==':
            return left == right
        if op == '!=':
            return left != right
        if op == '<':
            return self._compare(left, right) < 0
        if op == '>':
            return self._compare(left, right) > 0
        if op == '<=':
            return self._compare(left, right) <= 0
        if op == '>=':
            return self._compare(left, right) >= 0

        # === LOGICAL OPERATIONS ===
        if op == 'and' or op == '&&':
            return left and right
        if op == 'or' or op == '||':
            return left or right

        # === BITWISE OPERATIONS ===
        if op == '&':
            return int(left or 0) & int(right or 0)
        if op == '|':
            return int(left or 0) | int(right or 0)
        if op == '^':
            return int(left or 0) ^ int(right or 0)
        if op == '<<':
            return int(left or 0) << int(right or 0)
        if op == '>>':
            return int(left or 0) >> int(right or 0)

        # === IN OPERATOR ===
        if op == 'in':
            if right is None:
                return False
            return left in right

        return None

    def _to_number(self, value: Any) -> Union[int, float]:
        """Convert value to number with auto-casting"""
        if value is None:
            return 0
        if isinstance(value, (int, float)):
            return value
        if isinstance(value, str):
            value = value.strip()
            if not value:
                return 0
            try:
                if '.' in value:
                    return float(value)
                return int(value)
            except ValueError:
                return 0
        if isinstance(value, bool):
            return 1 if value else 0
        if isinstance(value, (list, tuple)):
            return len(value)
        return 0

    def _compare(self, left: Any, right: Any) -> int:
        """Compare two values with auto-casting, returns -1, 0, or 1"""
        # Handle None
        if left is None and right is None:
            return 0
        if left is None:
            return -1
        if right is None:
            return 1

        # Both strings - compare as strings
        if isinstance(left, str) and isinstance(right, str):
            if left < right:
                return -1
            elif left > right:
                return 1
            return 0

        # Both numbers - compare as numbers
        if isinstance(left, (int, float)) and isinstance(right, (int, float)):
            if left < right:
                return -1
            elif left > right:
                return 1
            return 0

        # Mixed types - try to convert to numbers
        try:
            l = self._to_number(left)
            r = self._to_number(right)
            if l < r:
                return -1
            elif l > r:
                return 1
            return 0
        except:
            # Fallback to string comparison
            l_str = str(left)
            r_str = str(right)
            if l_str < r_str:
                return -1
            elif l_str > r_str:
                return 1
            return 0

    def _eval_unary(self, node: ASTNode) -> Any:
        """Evaluate unary operation"""
        op = node.value.get('op')
        operand = self._evaluate(node.value.get('operand'))

        if op == 'not':
            return not operand
        if op == '-':
            return -operand

        return None

    def _eval_call(self, node: ASTNode) -> Any:
        """Evaluate function call with optional named arguments"""
        callee_node = node.value.get('callee')
        args = [self._evaluate(a) for a in node.value.get('args', [])]

        # Evaluate named arguments (kwargs)
        kwargs_raw = node.value.get('kwargs', {})
        kwargs = {k: self._evaluate(v) for k, v in kwargs_raw.items()} if kwargs_raw else {}

        # Get function name for injection check FIRST (before evaluating callee)
        func_name = None
        if isinstance(callee_node, ASTNode):
            if callee_node.type == 'identifier':
                func_name = callee_node.value
            elif callee_node.type == 'member_access':
                func_name = callee_node.value.get('member')

        # Check if function has injections
        has_injections = func_name and func_name in self._function_injections
        is_replaced = func_name and self._function_replaced.get(func_name, False)

        # If function is FULLY REPLACED (<<==), run injection and skip original
        # This allows creating new functions via infusion: new_func <<== { ... }
        if is_replaced:
            self._execute_function_injections(func_name)
            return None  # Injection ran, don't try to find original

        # Now evaluate the callee (only if not replaced)
        callee = self._evaluate(callee_node)

        # Execute added injections (+<<==) before original
        if has_injections and not is_replaced:
            self._execute_function_injections(func_name)

        # Execute original function
        if callable(callee):
            if kwargs:
                return callee(*args, **kwargs)
            return callee(*args)

        if isinstance(callee, ASTNode) and callee.type == 'function':
            return self._call_function(callee, args, kwargs)

        callee_name = callee_node.value if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value') else str(callee_node)
        raise CSSLRuntimeError(
            f"Cannot call '{callee_name}' - it is not a function",
            node.line,
            context=f"Type: {type(callee).__name__}",
            hint=ERROR_HINTS['undefined_function']
        )

    def _eval_typed_call(self, node: ASTNode) -> Any:
        """Evaluate typed function call like OpenFind<string>(0)"""
        name = node.value.get('name')
        type_param = node.value.get('type_param', 'dynamic')
        args = [self._evaluate(a) for a in node.value.get('args', [])]

        # Handle OpenFind<type>(index)
        if name == 'OpenFind':
            # OpenFind searches for a value of the specified type
            # from the open parameters in scope
            open_params = self.scope.get('Params') or []
            index = args[0] if args else 0

            # Search for value of matching type at or near the index
            type_map = {
                'string': str, 'str': str,
                'int': int, 'integer': int,
                'float': float, 'double': float,
                'bool': bool, 'boolean': bool,
                'list': list, 'array': list,
                'dict': dict, 'json': dict,
            }

            target_type = type_map.get(type_param.lower())

            if isinstance(open_params, (list, tuple)):
                # Find first matching type starting from index
                for i in range(index, len(open_params)):
                    if target_type is None or isinstance(open_params[i], target_type):
                        return open_params[i]
                # Also search before index
                for i in range(0, min(index, len(open_params))):
                    if target_type is None or isinstance(open_params[i], target_type):
                        return open_params[i]

            return None

        # Fallback: call as regular function with type hint
        func = self.builtins.get_function(name)
        if func and callable(func):
            return func(type_param, *args)

        raise CSSLRuntimeError(
            f"Unknown typed function: {name}<{type_param}>",
            node.line,
            context=f"Available typed functions: OpenFind<type>",
            hint="Typed functions use format: FunctionName<Type>(args)"
        )

    def _eval_new(self, node: ASTNode) -> CSSLInstance:
        """Evaluate 'new ClassName(args)' expression.

        Creates a new instance of a CSSL class and calls its constructor.
        """
        class_name = node.value.get('class')
        args = [self._evaluate(arg) for arg in node.value.get('args', [])]
        kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}

        # Get class definition from scope
        class_def = self.scope.get(class_name)
        if class_def is None:
            class_def = self.global_scope.get(class_name)

        if class_def is None:
            raise CSSLRuntimeError(
                f"Class '{class_name}' not found",
                node.line,
                hint="Make sure the class is defined before instantiation"
            )

        if not isinstance(class_def, CSSLClass):
            raise CSSLRuntimeError(
                f"'{class_name}' is not a class",
                node.line,
                hint=f"'{class_name}' is of type {type(class_def).__name__}"
            )

        # Create new instance
        instance = CSSLInstance(class_def)

        # Call constructor if defined
        if class_def.constructor:
            self._call_method(instance, class_def.constructor, args, kwargs)

        return instance

    def _eval_this_access(self, node: ASTNode) -> Any:
        """Evaluate 'this->member' access.

        Returns the value of a member from the current class instance.
        """
        if self._current_instance is None:
            raise CSSLRuntimeError(
                "'this' used outside of class method context",
                node.line if hasattr(node, 'line') else 0,
                hint="'this->' can only be used inside class methods"
            )

        member = node.value.get('member')

        # Check if it's a chained access (this->a->b)
        if 'object' in node.value:
            # First evaluate the object part
            obj = self._evaluate(node.value.get('object'))
            if obj is None:
                return None
            if hasattr(obj, member):
                return getattr(obj, member)
            if isinstance(obj, dict):
                return obj.get(member)
            return None

        # Direct this->member access
        instance = self._current_instance

        # Check if it's a member variable
        if instance.has_member(member):
            return instance.get_member(member)

        # Check if it's a method
        if instance.has_method(member):
            # Return a callable that will invoke the method with instance context
            method_node = instance.get_method(member)
            return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)

        raise CSSLRuntimeError(
            f"'{instance._class.name}' has no member or method '{member}'",
            node.line if hasattr(node, 'line') else 0
        )

    def _call_method(self, instance: CSSLInstance, method_node: ASTNode, args: list, kwargs: dict = None) -> Any:
        """Call a method on an instance with 'this' context.

        Sets up the instance as the current 'this' context and executes the method.
        """
        kwargs = kwargs or {}
        func_info = method_node.value
        params = func_info.get('params', [])
        modifiers = func_info.get('modifiers', [])

        # Check for undefined modifier
        is_undefined = 'undefined' in modifiers

        # Create new scope for method
        new_scope = Scope(parent=self.scope)

        # Bind parameters
        for i, param in enumerate(params):
            param_name = param['name'] if isinstance(param, dict) else param

            if param_name in kwargs:
                new_scope.set(param_name, kwargs[param_name])
            elif i < len(args):
                new_scope.set(param_name, args[i])
            else:
                new_scope.set(param_name, None)

        # Save current state
        old_scope = self.scope
        old_instance = self._current_instance

        # Set up method context
        self.scope = new_scope
        self._current_instance = instance

        try:
            for child in method_node.children:
                if not self._running:
                    break
                self._execute_node(child)
        except CSSLReturn as ret:
            return ret.value
        except Exception as e:
            if is_undefined:
                return None
            raise
        finally:
            # Restore previous state
            self.scope = old_scope
            self._current_instance = old_instance

        return None

    def _eval_member_access(self, node: ASTNode) -> Any:
        """Evaluate member access"""
        obj = self._evaluate(node.value.get('object'))
        member = node.value.get('member')

        if obj is None:
            return None

        # Special handling for Parameter.return() -> Parameter.return_()
        # since 'return' is a Python keyword
        if isinstance(obj, Parameter) and member == 'return':
            member = 'return_'

        # === CSSL CLASS INSTANCE METHODS ===
        if isinstance(obj, CSSLInstance):
            # Check for member variable
            if obj.has_member(member):
                return obj.get_member(member)
            # Check for method
            if obj.has_method(member):
                method_node = obj.get_method(member)
                return lambda *args, **kwargs: self._call_method(obj, method_node, list(args), kwargs)
            raise CSSLRuntimeError(f"'{obj._class.name}' has no member or method '{member}'")

        # === STRING METHODS ===
        if isinstance(obj, str):
            string_methods = self._get_string_method(obj, member)
            if string_methods is not None:
                return string_methods

        # === LIST/ARRAY METHODS for plain lists ===
        if isinstance(obj, list) and not isinstance(obj, (Stack, Vector, Array)):
            list_methods = self._get_list_method(obj, member)
            if list_methods is not None:
                return list_methods

        if hasattr(obj, member):
            return getattr(obj, member)

        if isinstance(obj, dict):
            return obj.get(member)

        return None

    def _get_string_method(self, s: str, method: str) -> Any:
        """Get string method implementation for CSSL.

        Provides C++/Java/JS style string methods that Python doesn't have.
        """
        # === C++/Java/JS STRING METHODS ===
        if method == 'contains':
            return lambda substr: substr in s
        elif method == 'indexOf':
            return lambda substr, start=0: s.find(substr, start)
        elif method == 'lastIndexOf':
            return lambda substr: s.rfind(substr)
        elif method == 'charAt':
            return lambda index: s[index] if 0 <= index < len(s) else ''
        elif method == 'charCodeAt':
            return lambda index: ord(s[index]) if 0 <= index < len(s) else -1
        elif method == 'substring':
            return lambda start, end=None: s[start:end] if end else s[start:]
        elif method == 'substr':
            return lambda start, length=None: s[start:start+length] if length else s[start:]
        elif method == 'slice':
            return lambda start, end=None: s[start:end] if end else s[start:]

        # === TRIM METHODS ===
        elif method == 'trim':
            return lambda: s.strip()
        elif method == 'trimStart' or method == 'trimLeft' or method == 'ltrim':
            return lambda: s.lstrip()
        elif method == 'trimEnd' or method == 'trimRight' or method == 'rtrim':
            return lambda: s.rstrip()

        # === CASE METHODS ===
        elif method in ('toUpperCase', 'toUpper', 'upper'):
            return lambda: s.upper()
        elif method in ('toLowerCase', 'toLower', 'lower'):
            return lambda: s.lower()
        elif method == 'capitalize':
            return lambda: s.capitalize()
        elif method == 'title':
            return lambda: s.title()
        elif method == 'swapcase':
            return lambda: s.swapcase()

        # === REPLACE METHODS ===
        elif method == 'replaceAll':
            return lambda old, new: s.replace(old, new)
        elif method == 'replaceFirst':
            return lambda old, new: s.replace(old, new, 1)

        # === CHECK METHODS ===
        elif method == 'isEmpty':
            return lambda: len(s) == 0
        elif method == 'isBlank':
            return lambda: len(s.strip()) == 0
        elif method == 'isDigit' or method == 'isNumeric':
            return lambda: s.isdigit()
        elif method == 'isAlpha':
            return lambda: s.isalpha()
        elif method == 'isAlphaNumeric' or method == 'isAlnum':
            return lambda: s.isalnum()
        elif method == 'isSpace' or method == 'isWhitespace':
            return lambda: s.isspace()
        elif method == 'isUpper':
            return lambda: s.isupper()
        elif method == 'isLower':
            return lambda: s.islower()

        # === STARTS/ENDS WITH ===
        elif method == 'startsWith' or method == 'startswith':
            return lambda prefix: s.startswith(prefix)
        elif method == 'endsWith' or method == 'endswith':
            return lambda suffix: s.endswith(suffix)

        # === LENGTH/SIZE ===
        elif method == 'length' or method == 'size':
            return lambda: len(s)

        # === SPLIT/JOIN ===
        elif method == 'toArray':
            return lambda sep=None: list(s.split(sep) if sep else list(s))
        elif method == 'lines':
            return lambda: s.splitlines()
        elif method == 'words':
            return lambda: s.split()

        # === PADDING ===
        elif method == 'padStart' or method == 'padLeft' or method == 'lpad':
            return lambda width, char=' ': s.rjust(width, char[0] if char else ' ')
        elif method == 'padEnd' or method == 'padRight' or method == 'rpad':
            return lambda width, char=' ': s.ljust(width, char[0] if char else ' ')
        elif method == 'center':
            return lambda width, char=' ': s.center(width, char[0] if char else ' ')
        elif method == 'zfill':
            return lambda width: s.zfill(width)

        # === REPEAT ===
        elif method == 'repeat':
            return lambda n: s * n

        # === REVERSE ===
        elif method == 'reverse':
            return lambda: s[::-1]

        # === FORMAT ===
        elif method == 'format':
            return lambda *args, **kwargs: s.format(*args, **kwargs)

        # === ENCODING ===
        elif method == 'encode':
            return lambda encoding='utf-8': s.encode(encoding)
        elif method == 'bytes':
            return lambda encoding='utf-8': list(s.encode(encoding))

        # === NUMERIC CONVERSION ===
        elif method == 'toInt' or method == 'toInteger':
            return lambda base=10: int(s, base) if s.lstrip('-').isdigit() else 0
        elif method == 'toFloat' or method == 'toDouble':
            try:
                return lambda: float(s)
            except:
                return lambda: 0.0
        elif method == 'toBool':
            return lambda: s.lower() in ('true', '1', 'yes', 'on')

        # === C++ ITERATOR STYLE ===
        elif method == 'begin':
            return lambda: 0
        elif method == 'end':
            return lambda: len(s)

        # Return None if not a string method
        return None

    def _get_list_method(self, lst: list, method: str) -> Any:
        """Get list method implementation for plain Python lists in CSSL."""
        if method == 'contains':
            return lambda item: item in lst
        elif method == 'indexOf':
            def index_of(item):
                try:
                    return lst.index(item)
                except ValueError:
                    return -1
            return index_of
        elif method == 'lastIndexOf':
            def last_index_of(item):
                for i in range(len(lst) - 1, -1, -1):
                    if lst[i] == item:
                        return i
                return -1
            return last_index_of
        elif method == 'length' or method == 'size':
            return lambda: len(lst)
        elif method == 'isEmpty':
            return lambda: len(lst) == 0
        elif method == 'first':
            return lambda: lst[0] if lst else None
        elif method == 'last':
            return lambda: lst[-1] if lst else None
        elif method == 'at':
            return lambda i: lst[i] if 0 <= i < len(lst) else None
        elif method == 'slice':
            return lambda start, end=None: lst[start:end] if end else lst[start:]
        elif method == 'join':
            return lambda sep=',': sep.join(str(x) for x in lst)
        elif method == 'find':
            def find_item(val):
                for item in lst:
                    if item == val:
                        return item
                return None
            return find_item
        elif method == 'push':
            def push_item(item):
                lst.append(item)
                return lst
            return push_item
        elif method == 'push_back':
            def push_back_item(item):
                lst.append(item)
                return lst
            return push_back_item
        elif method == 'toArray':
            return lambda: list(lst)
        # === C++ ITERATOR STYLE ===
        elif method == 'begin':
            return lambda: 0
        elif method == 'end':
            return lambda: len(lst)

        return None

    def _eval_index_access(self, node: ASTNode) -> Any:
        """Evaluate index access"""
        obj = self._evaluate(node.value.get('object'))
        index = self._evaluate(node.value.get('index'))

        if obj is None:
            return None

        try:
            return obj[index]
        except (IndexError, KeyError, TypeError):
            return None

    def _set_member(self, node: ASTNode, value: Any):
        """Set member value"""
        obj = self._evaluate(node.value.get('object'))
        member = node.value.get('member')

        if obj is None:
            return

        # Check for CSSLInstance - use set_member method
        if isinstance(obj, CSSLInstance):
            obj.set_member(member, value)
            return

        # Check for SharedObjectProxy - directly access underlying object
        # This is more robust than relying on the proxy's __setattr__
        if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
            # This is a SharedObjectProxy - get the real object directly
            real_obj = object.__getattribute__(obj, '_direct_object')
            if real_obj is None:
                # Fallback to _live_objects registry
                name = object.__getattribute__(obj, '_name')
                from ..cssl_bridge import _live_objects
                real_obj = _live_objects.get(name)
            if real_obj is not None:
                setattr(real_obj, member, value)
                return

        if hasattr(obj, member):
            setattr(obj, member, value)
        elif isinstance(obj, dict):
            obj[member] = value
        else:
            # Try setattr anyway for objects that support dynamic attributes
            try:
                setattr(obj, member, value)
            except (AttributeError, TypeError):
                pass

    def _set_index(self, node: ASTNode, value: Any):
        """Set index value"""
        obj = self._evaluate(node.value.get('object'))
        index = self._evaluate(node.value.get('index'))

        if obj is not None:
            try:
                obj[index] = value
            except (IndexError, KeyError, TypeError):
                pass

    def _set_module_value(self, path: str, value: Any):
        """Set a value on a module path or promoted global"""
        parts = path.split('.')
        if len(parts) < 2:
            # Single name (no dots) - set in promoted_globals and global_scope
            self._promoted_globals[path] = value
            self.global_scope.set(path, value)
            return

        obj = self._modules.get(parts[0])
        # Also check promoted_globals for the base object
        if obj is None:
            obj = self._promoted_globals.get(parts[0])
        if obj is None:
            obj = self.global_scope.get(parts[0])
        if obj is None:
            return

        for part in parts[1:-1]:
            if hasattr(obj, part):
                obj = getattr(obj, part)
            elif isinstance(obj, dict):
                obj = obj.get(part)
            else:
                return

        final_attr = parts[-1]
        if hasattr(obj, final_attr):
            setattr(obj, final_attr, value)
        elif isinstance(obj, dict):
            obj[final_attr] = value

    # String interpolation (supports both <var> and {var} syntax)
    def _interpolate_string(self, string: str) -> str:
        """Replace {variable} and <variable> placeholders with values from scope.

        Both syntaxes are supported (variables only, not expressions):
            "Hello {name}!"   -> "Hello John!"  (f-string style)
            "Hello <name>!"   -> "Hello John!"  (legacy CSSL style)

        Examples:
            string name = "Alice";
            int age = 30;
            printl("Hello {name}, you are {age} years old!");
            printl("Hello <name>, you are <age> years old!");

        Note: Only simple variable names are supported, not expressions.
        Use string concatenation for complex expressions.
        """
        import re

        def replacer(match):
            var_name = match.group(1)
            # Try scope first
            value = self.scope.get(var_name)
            # Try promoted globals
            if value is None:
                value = self._promoted_globals.get(var_name)
            # Try modules
            if value is None:
                value = self._modules.get(var_name)
            # Try global scope
            if value is None:
                value = self.global_scope.get(var_name)
            # Return string representation or empty string if None
            return str(value) if value is not None else ''

        # Support both {var} and <var> syntax - simple variable names only
        # Pattern: {identifier} or <identifier>
        patterns = [
            r'\{([A-Za-z_][A-Za-z0-9_]*)\}',  # {name} f-string style
            r'<([A-Za-z_][A-Za-z0-9_]*)>',     # <name> legacy CSSL style
        ]

        result = string
        for pattern in patterns:
            result = re.sub(pattern, replacer, result)
        return result

    # NEW: Promote variable to global scope via global()
    def promote_to_global(self, s_ref_name: str):
        """Promote s@<name> to @<name> (make globally accessible) - NEW

        Example: global(s@cache) makes @cache available
        """
        # Extract the base name from s@<path>
        parts = s_ref_name.split('.')
        base_name = parts[0]

        # Get the value from global structs
        value = self.get_global_struct(s_ref_name)

        if value is not None:
            # Register as module reference
            self._modules[base_name] = value
            # Also store in promoted globals for string interpolation
            self._promoted_globals[base_name] = value

    # NEW: Scan for captured_ref nodes and capture their current values
    def _scan_and_capture_refs(self, node: ASTNode) -> Dict[str, Any]:
        """Scan AST for %<name> captured references and capture their current values.

        This is called at infusion registration time to capture values.
        Example: old_exit <<== { %exit(); } captures 'exit' at definition time.
        """
        captured = {}

        def scan_node(n):
            if not isinstance(n, ASTNode):
                return

            # Found a captured_ref - capture its current value
            if n.type == 'captured_ref':
                name = n.value
                if name not in captured:
                    # Try to find value - check multiple sources
                    value = None

                    # 1. Check _original_functions first (for functions that were JUST replaced)
                    if value is None:
                        value = self._original_functions.get(name)

                    # 2. Check scope
                    if value is None:
                        value = self.scope.get(name)

                    # 3. Check global_scope
                    if value is None:
                        value = self.global_scope.get(name)

                    # 4. Check builtins (most common case for exit, print, etc.)
                    if value is None:
                        # For critical builtins like 'exit', create a direct wrapper
                        # that captures the runtime reference to ensure correct behavior
                        if name == 'exit':
                            runtime = self  # Capture runtime in closure
                            value = lambda code=0, rt=runtime: rt.exit(code)
                        else:
                            value = getattr(self.builtins, f'builtin_{name}', None)

                    # 5. Check if there's a user-defined function in scope
                    if value is None:
                        # Look for function definitions
                        func_def = self.global_scope.get(f'__func_{name}')
                        if func_def is not None:
                            value = func_def

                    # Only capture if we found something
                    if value is not None:
                        captured[name] = value

            # Check call node's callee
            if n.type == 'call':
                callee = n.value.get('callee')
                if callee:
                    scan_node(callee)
                for arg in n.value.get('args', []):
                    scan_node(arg)

            # Recurse into children
            if hasattr(n, 'children') and n.children:
                for child in n.children:
                    scan_node(child)

            # Check value dict for nested nodes
            if hasattr(n, 'value') and isinstance(n.value, dict):
                for key, val in n.value.items():
                    if isinstance(val, ASTNode):
                        scan_node(val)
                    elif isinstance(val, list):
                        for item in val:
                            if isinstance(item, ASTNode):
                                scan_node(item)

        scan_node(node)
        return captured

    # NEW: Register permanent function injection
    def register_function_injection(self, func_name: str, code_block: ASTNode):
        """Register code to be permanently injected into a function - NEW

        Example: exit() <== { println("Cleanup..."); }
        Makes every call to exit() also execute the injected code

        Captures %<name> references at registration time.
        """
        # Scan for %<name> captured references and capture their current values
        captured_values = self._scan_and_capture_refs(code_block)

        if func_name not in self._function_injections:
            self._function_injections[func_name] = []
        self._function_injections[func_name].append((code_block, captured_values))

    # NEW: Execute injected code for a function
    def _execute_function_injections(self, func_name: str):
        """Execute all injected code blocks for a function - NEW

        Includes protection against recursive execution to prevent doubled output.
        Uses captured values for %<name> references.
        """
        # Prevent recursive injection execution (fixes doubled output bug)
        if getattr(self, '_injection_executing', False):
            return

        if func_name in self._function_injections:
            self._injection_executing = True
            old_captured = self._current_captured_values.copy()
            try:
                for injection in self._function_injections[func_name]:
                    # Handle both tuple format (code_block, captured_values) and legacy ASTNode format
                    if isinstance(injection, tuple):
                        code_block, captured_values = injection
                        self._current_captured_values = captured_values
                    else:
                        code_block = injection
                        self._current_captured_values = {}

                    if isinstance(code_block, ASTNode):
                        if code_block.type == 'action_block':
                            for child in code_block.children:
                                # Check if exit() was called
                                if not self._running:
                                    break
                                self._execute_node(child)
                        else:
                            self._execute_node(code_block)
            finally:
                self._injection_executing = False
                self._current_captured_values = old_captured

    # Output functions for builtins
    def set_output_callback(self, callback: Callable[[str, str], None]):
        """Set output callback for console integration"""
        self._output_callback = callback

    def _emit_output(self, text: str, level: str = 'normal'):
        """Emit output through callback or print"""
        if self._output_callback:
            self._output_callback(text, level)
        else:
            print(text, end='')

    def output(self, text: str):
        """Output text"""
        self.output_buffer.append(text)
        self._emit_output(text, 'normal')

    def debug(self, message: str):
        """Debug output"""
        text = f"[DEBUG] {message}\n"
        self._emit_output(text, 'debug')

    def error(self, message: str):
        """Error output"""
        text = f"[ERROR] {message}\n"
        self._emit_output(text, 'error')

    def warn(self, message: str):
        """Warning output"""
        text = f"[WARN] {message}\n"
        self._emit_output(text, 'warning')

    def log(self, level: str, message: str):
        """Log with level"""
        level_map = {'debug': 'debug', 'info': 'normal', 'warn': 'warning', 'warning': 'warning', 'error': 'error'}
        output_level = level_map.get(level.lower(), 'normal')
        text = f"[{level.upper()}] {message}\n"
        self._emit_output(text, output_level)

    def exit(self, code: int = 0):
        """Exit runtime"""
        self._exit_code = code
        self._running = False

    def get_output(self) -> str:
        """Get buffered output"""
        return ''.join(self.output_buffer)

    def clear_output(self):
        """Clear output buffer"""
        self.output_buffer.clear()


class CSSLServiceRunner:
    """
    Runs CSSL services with event integration
    """

    def __init__(self, runtime: CSSLRuntime):
        self.runtime = runtime
        self.running_services: Dict[str, ServiceDefinition] = {}
        self.event_manager = get_event_manager()

    def load_service(self, source: str) -> ServiceDefinition:
        """Load and parse a CSSL service"""
        ast = parse_cssl(source)
        service = self.runtime._exec_service(ast)
        return service

    def load_service_file(self, filepath: str) -> ServiceDefinition:
        """Load a service from file"""
        with open(filepath, 'r', encoding='utf-8') as f:
            source = f.read()
        return self.load_service(source)

    def start_service(self, service: ServiceDefinition) -> bool:
        """Start a loaded service"""
        if service.name in self.running_services:
            return False

        self.running_services[service.name] = service

        try:
            # Execute Initialization struct first if exists
            if 'Initialization' in service.structs:
                init_struct = service.structs['Initialization']
                self.runtime._exec_struct(init_struct)
            elif 'Init' in service.structs:
                init_struct = service.structs['Init']
                self.runtime._exec_struct(init_struct)

            # Execute main function if exists
            if 'main' in service.functions:
                main_func = service.functions['main']
                self.runtime._call_function(main_func, [])
            else:
                # Try to find a main-like function
                for func_name in ['Main', 'run', 'Run', 'start', 'Start']:
                    if func_name in service.functions:
                        self.runtime._call_function(service.functions[func_name], [])
                        break

        except CSSLReturn:
            pass  # Normal return from main
        except CSSLRuntimeError as e:
            # Runtime error with line number
            self.runtime.error(f"Service '{service.name}' Fehler (Zeile {e.line}): {e}")
            return False
        except Exception as e:
            # Try to extract line info
            line_info = ""
            if hasattr(e, 'line') and e.line:
                line_info = f" (Zeile {e.line})"
            self.runtime.error(f"Service '{service.name}' Fehler{line_info}: {e}")
            return False

        return True

    def stop_service(self, service_name: str) -> bool:
        """Stop a running service"""
        if service_name not in self.running_services:
            return False

        service = self.running_services[service_name]

        # Execute cleanup if exists
        if 'cleanup' in service.functions:
            cleanup_func = service.functions['cleanup']
            self.runtime._call_function(cleanup_func, [])

        del self.running_services[service_name]
        return True

    def get_running_services(self) -> List[str]:
        """Get list of running service names"""
        return list(self.running_services.keys())


# Convenience function
def run_cssl(source: str, service_engine=None) -> Any:
    """Run CSSL source code"""
    runtime = CSSLRuntime(service_engine)
    return runtime.execute(source)


def run_cssl_file(filepath: str, service_engine=None) -> Any:
    """Run a CSSL file"""
    runtime = CSSLRuntime(service_engine)
    return runtime.execute_file(filepath)
