import 'package:analyzer/dart/ast/ast.dart' hide TypeParameter;
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:d4rt/d4rt.dart';
import 'package:d4rt/src/bridge/bridged_enum.dart';
import 'package:d4rt/src/utils/extensions/string.dart';
import 'package:d4rt/src/module_loader.dart';

/// Main visitor that walks the AST and interprets the code.
/// Uses a two-pass approach (DeclarationVisitor first).
class InterpreterVisitor extends GeneralizingAstVisitor<Object?> {
  Environment environment;
  final Environment globalEnvironment;
  final ModuleLoader moduleLoader; // Field for ModuleLoader
  InterpretedFunction? currentFunction; // Track the function being executed
  AsyncExecutionState? currentAsyncState;
  Set<String> _currentStatementLabels = {};

  InterpreterVisitor({
    required this.globalEnvironment,
    required this.moduleLoader, // Accept ModuleLoader in the constructor
    Uri? initiallibrary, // New: Optional URI for the initial source
  }) : environment = globalEnvironment {
    if (initiallibrary != null) {
      // Sets the base URI in the ModuleLoader for the initial source code.
      // This is crucial for resolving relative imports in this initial source code.
      moduleLoader.currentlibrary = initiallibrary;
      Logger.debug(
          "[InterpreterVisitor] Initial source URI set in ModuleLoader to: $initiallibrary");
    }
    // Initialize currentAsyncState if it's null and we are in an async context implicitly
    // This might be more complex depending on how top-level async calls are handled
  }

  @override
  Object? visitCompilationUnit(CompilationUnit node) {
    // Restore simple sequential processing
    Object? lastValue;
    for (final declaration in node.declarations) {
      lastValue = declaration.accept<Object?>(this);
    }
    return lastValue;
  }

  @override
  Object? visitBlock(Block node) {
    return executeBlock(node.statements, Environment(enclosing: environment));
  }

  Object? executeBlock(
      List<Statement> statements, Environment blockEnvironment) {
    final previousEnvironment = environment;
    Object? lastValue;
    try {
      environment = blockEnvironment;
      for (final statement in statements) {
        // Explicitly handle declarations within blocks
        if (statement is FunctionDeclaration ||
            statement is ClassDeclaration ||
            statement is MixinDeclaration ||
            statement
                is TopLevelVariableDeclaration || // Technically not a statement, but might appear?
            statement is VariableDeclarationStatement) {
          // This is a proper statement
          lastValue = statement.accept<Object?>(this);
          // Check for suspension after declaration execution
          if (lastValue is AsyncSuspensionRequest) {
            return lastValue; // Propagate suspension
          }
          lastValue = null; // Declarations don't produce a carry value
        } else {
          lastValue = statement.accept<Object?>(this);
          // if the execution of the statement returns an async suspension request,
          // we propagate it immediately upwards.
          if (lastValue is AsyncSuspensionRequest) {
            return lastValue;
          }
        }
        // Handle ReturnException, BreakException, ContinueException if needed (propagate or consume)
        // This simple version just propagates them implicitly by not catching them here.
      }
    } finally {
      environment = previousEnvironment;
    }
    return lastValue;
  }

  @override
  Object? visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
    for (final variable in node.variables.variables) {
      if (variable.name.lexeme == '_') {
        // Evaluate initializer for potential side effects, but don't define
        variable.initializer?.accept<Object?>(this);
      } else {
        Object? value;
        if (variable.initializer != null) {
          value = variable.initializer!.accept<Object?>(this);
        }
        environment.define(variable.name.lexeme, value);
      }
    }
    return null;
  }

  @override
  Object? visitPatternVariableDeclarationStatement(
      PatternVariableDeclarationStatement node) {
    final patternDecl = node.declaration;
    final pattern = patternDecl.pattern;
    final initializer = patternDecl.expression;

    // Evaluate the right-hand side value
    final rhsValue = initializer.accept<Object?>(this);

    // Match and bind the pattern to the value
    try {
      _matchAndBind(pattern, rhsValue, environment);
    } on PatternMatchException catch (e) {
      // Convert pattern match failures to standard RuntimeError for now
      throw RuntimeError("Pattern match failed: ${e.message}");
    } catch (e, s) {
      // Add stack trace capture
      Logger.error(
          "during pattern binding: $e\nStack trace:\n$s"); // Display the stack trace
      // Catch other potential errors during binding
      throw RuntimeError("Error during pattern binding: $e");
    }
    return null;
  }

  @override
  Object? visitVariableDeclarationStatement(VariableDeclarationStatement node) {
    // Simply visit the declaration list. The list visitor handles definition.
    return node.variables.accept<Object?>(this);
    // return null; // Statements usually return null unless they are Return/Throw/etc.
  }

  // Handles the list of variables in a single declaration statement.

  @override
  Object? visitExpressionStatement(ExpressionStatement node) {
    return node.expression.accept<Object?>(this);
  }

  @override
  Object? visitAsExpression(AsExpression node) {
    final value = node.expression.accept<Object?>(this);
    final typeNode = node.type;
    if (typeNode is NamedType) {
      final typeName = typeNode.name2.lexeme;
      switch (typeName) {
        case 'int':
          if (value is int) return value;
          break;
        case 'double':
          if (value is double) return value;
          break;
        case 'num':
          if (value is num) return value;
          break;
        case 'String':
          if (value is String) return value;
          break;
        case 'bool':
          if (value is bool) return value;
          break;
        case 'List':
          if (value is List) return value;
          break;
        case 'Null':
          if (value == null) return value;
          break;
        case 'Object':
          if (value != null) return value;
          break;
        case 'dynamic':
          return value;
        default:
          // For custom/interpreted types, we can add logic here
          // For now, we accept all (permissive behavior)
          return value;
      }
    }
    throw RuntimeError(
        "Cast failed with 'as' : the value does not match the target type (${typeNode.toSource()})");
  }

  @override
  Object? visitIntegerLiteral(IntegerLiteral node) {
    return node.value;
  }

  @override
  Object? visitDoubleLiteral(DoubleLiteral node) {
    return node.value;
  }

  @override
  Object? visitBooleanLiteral(BooleanLiteral node) {
    return node.value;
  }

  @override
  Object? visitStringLiteral(StringLiteral node) {
    if (node is SimpleStringLiteral) {
      return node.value;
    } else if (node is AdjacentStrings) {
      // Handle adjacent strings like 'Hello ' 'World!'
      final buffer = StringBuffer();
      for (final stringLiteral in node.strings) {
        if (stringLiteral is SimpleStringLiteral) {
          buffer.write(stringLiteral.value);
        } else {
          // Recursively handle nested adjacent strings or other string types
          final value = visitStringLiteral(stringLiteral);
          buffer.write(value.toString());
        }
      }
      return buffer.toString();
    }
    throw UnimplementedError(
      'Type de StringLiteral non géré: ${node.runtimeType}',
    );
  }

  @override
  Object? visitNullLiteral(NullLiteral node) {
    return null;
  }

  @override
  Object? visitSimpleIdentifier(SimpleIdentifier node) {
    final name = node.name;

    Logger.debug(
        "[visitSimpleIdentifier] Looking for '$name'. Visitor env: ${environment.hashCode}");

    // Lexical search & Bridges
    try {
      // Use environment.get() which handles strings and bridges
      final value = environment.get(name);
      // If get() succeeds, the value is found (variable or bridge)
      Logger.debug(
          "[visitSimpleIdentifier] Found '$name' via environment.get() -> ${value?.runtimeType}");
      if (name == 'initialValue') {
        Logger.debug(
            "[visitSimpleIdentifier] Returning '$name' = $value (from lexical/bridge)");
      }
      return value;
    } on RuntimeError catch (getErr) {
      // Ignore get() error for now, try 'this' then
      Logger.debug(
          "[visitSimpleIdentifier] '$name' not found lexically or as bridge. Trying implicit 'this'. Error: ${getErr.message}");
    }

    // Implicit attempt via 'this'
    // (Only if not found lexically/as bridge)
    Object? thisInstance;
    try {
      thisInstance = environment.get('this');
    } on RuntimeError {
      // 'this' does not exist in the current environment.
      // Before giving up, try searching static methods in the current class if we're in a static context
      if (currentFunction != null &&
          currentFunction!.ownerType is InterpretedClass) {
        final ownerClass = currentFunction!.ownerType as InterpretedClass;
        Logger.debug(
            "[visitSimpleIdentifier] 'this' not found, but we're in class '${ownerClass.name}'. Checking static members for '$name'.");

        // Check static methods
        final staticMethod = ownerClass.findStaticMethod(name);
        if (staticMethod != null) {
          Logger.debug(
              "[visitSimpleIdentifier] Found static method '$name' in current class '${ownerClass.name}'.");
          return staticMethod;
        }

        // Check static getters
        final staticGetter = ownerClass.findStaticGetter(name);
        if (staticGetter != null) {
          Logger.debug(
              "[visitSimpleIdentifier] Found static getter '$name' in current class '${ownerClass.name}'.");
          return staticGetter;
        }

        // Check static fields
        try {
          final staticField = ownerClass.getStaticField(name);
          Logger.debug(
              "[visitSimpleIdentifier] Found static field '$name' in current class '${ownerClass.name}'.");
          return staticField;
        } on RuntimeError {
          // Static field not found, continue to final error
        }
      }

      // This is the end of the search, the identifier is undefined.
      throw RuntimeError("Undefined variable: $name");
    }

    // 'this' was found, now we try to access the member.
    try {
      if (thisInstance is InterpretedInstance) {
        // Access the property on 'this'
        final member = thisInstance.get(name, visitor: this);
        Logger.debug(
            "[visitSimpleIdentifier] Found '$name' via implicit InterpretedInstance 'this'. Member: ${member?.runtimeType}");
        return member; // Return the field value or bound function (getter/method)
      } else if (toBridgedInstance(thisInstance).$2) {
        final bridgedInstance = toBridgedInstance(thisInstance).$1!;

        Logger.debug(
            "[visitSimpleIdentifier] Trying implicit 'this' access on BridgedInstance (${bridgedInstance.bridgedClass.name}) for member '$name'.");
        // Check instance getter FIRST
        final getterAdapter =
            bridgedInstance.bridgedClass.findInstanceGetterAdapter(name);
        if (getterAdapter != null) {
          Logger.debug(
              "[visitSimpleIdentifier] Found BRIDGED GETTER '$name' via implicit 'this'. Calling adapter...");
          final getterResult =
              getterAdapter(this, bridgedInstance.nativeObject);
          if (name == 'initialValue') {
            Logger.debug(
                "[visitSimpleIdentifier] Returning '$name' = $getterResult (from BridgedInstance getter this)");
          }
          return getterResult;
        }
        Logger.debug(
            "[visitSimpleIdentifier]   Bridged getter '$name' not found. Checking for method...");
        // Then check instance method
        final methodAdapter =
            bridgedInstance.bridgedClass.findInstanceMethodAdapter(name);
        if (methodAdapter != null) {
          Logger.debug(
              "[visitSimpleIdentifier] Found BRIDGED METHOD '$name' via implicit 'this'. Binding...");
          // Return a callable bound to the instance

          final boundCallable =
              BridgedMethodCallable(bridgedInstance, methodAdapter, name);
          if (name == 'initialValue') {
            Logger.debug(
                "[visitSimpleIdentifier] Returning '$name' = $boundCallable (bound method from BridgedInstance this)");
          }
          return boundCallable;
        }
        Logger.debug(
            "[visitSimpleIdentifier]   Bridged method '$name' not found either.");
        // If neither getter nor method, error
        throw RuntimeError(
            "Undefined property or method '$name' on bridged instance of '${bridgedInstance.bridgedClass.name}' accessed via implicit 'this'.");
      } // +++ NEW BLOCK +++
      else if (thisInstance is InterpretedEnumValue) {
        Logger.debug(
            "[visitSimpleIdentifier] Found '$name' via implicit InterpretedEnumValue 'this'.");
        // Delegate to the get method of the enum value, passing visitor
        // This will execute getters or return bound methods/fields.
        final enumMember = thisInstance.get(name, this);
        if (name == 'initialValue') {
          Logger.debug(
              "[visitSimpleIdentifier] Returning '$name' = $enumMember (from EnumValue this)");
        }
        return enumMember;
      }
      throw RuntimeError(
          "Undefined variable: $name (this exists as native type ${thisInstance?.runtimeType}");
    } on RuntimeError catch (thisErr) {
      // 'this' not found OR instance.get() failed
      // If get() failed with a specific error, propagate it if it is NOT "Undefined property".
      if (thisErr.message.contains("Undefined property '$name'")) {
        Logger.debug(
            "[SimpleIdentifier] Direct access failed for '$name' via implicit 'this'. Trying extension lookup on ${thisInstance?.runtimeType}.");
        if (thisInstance != null) {
          // Check that 'this' exists before searching
          try {
            final extensionMember =
                environment.findExtensionMember(thisInstance, name);

            if (extensionMember is InterpretedExtensionMethod) {
              if (extensionMember.isGetter) {
                Logger.debug(
                    "[SimpleIdentifier] Found extension getter '$name' via implicit 'this'. Calling...");
                // Extension getters are called with the instance as the only positional argument
                final extensionPositionalArgs = [thisInstance];
                try {
                  return extensionMember
                      .call(this, extensionPositionalArgs, {});
                } on ReturnException catch (e) {
                  return e.value;
                } catch (e) {
                  throw RuntimeError(
                      "Error executing extension getter '$name' via implicit 'this': $e");
                }
              } else if (!extensionMember.isOperator &&
                  !extensionMember.isSetter) {
                // Return the extension method itself (not bound)
                Logger.debug(
                    "[SimpleIdentifier] Found extension method '$name' via implicit 'this'. Returning callable.");
                // Return a bound instance instead of the raw method.
                return BoundExtensionMethodCallable(
                    thisInstance, extensionMember);
              }
              // Operators/setters are generally not accessible directly via a simple identifier
            }
            // No suitable extension member found, fall through to final error
            Logger.debug(
                "[SimpleIdentifier] No suitable extension member found for '$name' via implicit 'this'.");
          } on RuntimeError catch (findError) {
            // Error during extension lookup itself
            Logger.debug(
                "[SimpleIdentifier] Error during extension lookup for '$name' via implicit 'this': ${findError.message}");
            // Fall through to final error
          }
        } else {
          Logger.debug(
              "[SimpleIdentifier] Cannot search extension for '$name' because implicit 'this' is null.");
        }
      }
      // Relaunch the error if it was NOT "Undefined property" OR if extension lookup failed.
      else if (thisErr.message
          .contains("non implémenté pour BridgedInstance")) {
        // Relaunch the specific BridgeInstance errors
        rethrow;
      }

      // Before final error, try static method lookup in enclosing class
      final enclosingClass = _findEnclosingClass();
      if (enclosingClass != null) {
        Logger.debug(
            "[visitSimpleIdentifier] Final attempt: checking static members for '$name' in enclosing class '${enclosingClass.name}'.");

        // Check static methods
        final staticMethod = enclosingClass.findStaticMethod(name);
        if (staticMethod != null) {
          Logger.debug(
              "[visitSimpleIdentifier] Found static method '$name' in enclosing class '${enclosingClass.name}' (final attempt).");
          return staticMethod;
        }

        // Check static getters
        final staticGetter = enclosingClass.findStaticGetter(name);
        if (staticGetter != null) {
          Logger.debug(
              "[visitSimpleIdentifier] Found static getter '$name' in enclosing class '${enclosingClass.name}' (final attempt).");
          return staticGetter;
        }

        // Check static fields
        try {
          final staticField = enclosingClass.getStaticField(name);
          Logger.debug(
              "[visitSimpleIdentifier] Found static field '$name' in enclosing class '${enclosingClass.name}' (final attempt).");
          return staticField;
        } on RuntimeError {
          // Static field not found, continue to final error
        }
      }

      // If the initial error was 'Undefined property' AND that extension lookup failed,
      // or if the initial error was something else, raise the final "Undefined variable" error.
      throw RuntimeError(
          "Undefined variable: $name (Original error: ${thisErr.message})");
    }
  }

  @override
  Object? visitThisExpression(ThisExpression node) {
    try {
      return environment.get('this');
    } on RuntimeError {
      // This should ideally not happen if called within a valid method/constructor context
      throw RuntimeError("Keyword 'this' used outside of an instance context.");
    }
  }

  /// Helper method to find an enclosing class that might contain static members
  /// Searches through the environment chain and currentFunction hierarchy
  InterpretedClass? _findEnclosingClass() {
    // First, try the current function's owner
    if (currentFunction != null &&
        currentFunction!.ownerType is InterpretedClass) {
      return currentFunction!.ownerType as InterpretedClass;
    }

    // If that doesn't work, search the environment chain for class instances
    // This is a fallback approach - look for any classes defined in parent scopes
    Environment? current = environment;
    while (current != null) {
      for (final value in current.values.values) {
        if (value is InterpretedClass) {
          // Found a class in this environment - this might be our enclosing class
          // We need to check if we're inside a method of this class
          // This is a heuristic - in a proper implementation, we'd track the lexical scope better
          Logger.debug(
              "[_findEnclosingClass] Found class '${value.name}' in environment chain");
          return value;
        }
      }
      current = current.enclosing;
    }

    return null;
  }

  @override
  Object? visitPrefixedIdentifier(PrefixedIdentifier node) {
    final prefixValue = node.prefix.accept<Object?>(this);
    if (prefixValue is AsyncSuspensionRequest) {
      // Propagate the suspension so that the state machine resumes this node after resolution
      return prefixValue;
    }
    final memberName = node.identifier.name;

    // Handle the case where the prefix is an environment (prefixed import)
    if (prefixValue is Environment) {
      Logger.debug(
          "[PrefixedIdentifier] The prefix '${node.prefix.toSource()}' resolved to an Environment. Searching for '$memberName' in this environment.");
      try {
        // The call to get() on the prefixed environment could return a function (which must be called if it's a getter)
        // or a direct value.
        final member = prefixValue.get(memberName);
        // If it's an InterpretedFunction and a getter, it must be called.
        if (member is InterpretedFunction && member.isGetter) {
          Logger.debug(
              "[PrefixedIdentifier] Member '$memberName' is a getter. Calling...");
          return member.call(this, [],
              {}); // Call without 'this' because it's a prefixed import
        }
        Logger.debug(
            "[PrefixedIdentifier] Member '$memberName' found directly: $member");
        return member; // Return the value/function directly
      } on RuntimeError catch (e) {
        throw RuntimeError(
            "Erreur lors de la récupération du membre '$memberName' de l'import préfixé '${node.prefix.toSource()}': ${e.message}");
      }
    }

    if (prefixValue is InterpretedClass) {
      // Static access
      try {
        return prefixValue.getStaticField(memberName);
      } on RuntimeError catch (_) {
        InterpretedFunction? staticMember =
            prefixValue.findStaticGetter(memberName);
        staticMember ??= prefixValue.findStaticMethod(memberName);
        if (staticMember != null) {
          if (staticMember.isGetter) {
            return staticMember.call(this, [], {});
          } else {
            return staticMember;
          }
        } else {
          throw RuntimeError(
              "Undefined static member '$memberName' on class '${prefixValue.name}'.");
        }
      }
    } else if (prefixValue is InterpretedEnum) {
      if (memberName == 'values') {
        Logger.debug(
            "[PrefixedIdentifier] Accessing static getter 'values' on enum '${prefixValue.name}'.");
        return prefixValue.valuesList;
      } else {
        final value = prefixValue.values[memberName];
        if (value != null) {
          Logger.debug(
              "[PrefixedIdentifier] Accessing static value '$memberName' on enum '${prefixValue.name}'.");
          return value;
        } else {
          throw RuntimeError(
              "Undefined static value '$memberName' on enum '${prefixValue.name}'. Available: ${prefixValue.valueNames.join(', ')}");
        }
      }
    } else if (prefixValue is BridgedClass) {
      final bridgedClass = prefixValue;
      Logger.debug(
          "[PrefixedIdentifier] Static access on BridgedClass: ${bridgedClass.name}.$memberName");
      final staticGetter = bridgedClass.findStaticGetterAdapter(memberName);
      if (staticGetter != null) {
        return staticGetter(this);
      }
      final staticMethod = bridgedClass.findStaticMethodAdapter(memberName);
      if (staticMethod != null) {
        throw UnimplementedError(
            "Returning bridged static methods as values from PrefixedIdentifier is not yet supported.");
      } else {
        throw RuntimeError(
            "Undefined static member '$memberName' on bridged class '${bridgedClass.name}'.");
      }
    } else if (prefixValue is InterpretedInstance) {
      try {
        final member = prefixValue.get(memberName);
        if (member is InterpretedFunction && member.isGetter) {
          return member.bind(prefixValue).call(this, [], {});
        } else {
          return member;
        }
      } on RuntimeError catch (e) {
        if (e.message.contains("Undefined property '$memberName'")) {
          final extensionMember =
              environment.findExtensionMember(prefixValue, memberName);
          if (extensionMember is InterpretedExtensionMethod) {
            if (extensionMember.isGetter) {
              return extensionMember.call(this, [prefixValue], {});
            } else if (!extensionMember.isOperator &&
                !extensionMember.isSetter) {
              return extensionMember;
            }
          }
        }
        throw RuntimeError(
            "${e.message} (accessing property via PrefixedIdentifier '$memberName')");
      }
    } else if (prefixValue is InterpretedEnumValue) {
      if (memberName == 'index') {
        Logger.debug(
            "[PrefixedIdentifier] Accessing getter 'index' on enum value '$prefixValue'.");
        return prefixValue.index;
      } else if (memberName == 'toString') {
        Logger.debug(
            "[PrefixedIdentifier] Accessing method 'toString' on enum value '$prefixValue'. Returning callable.");
        // Return directly the string for simplicity in prefixed access?
        // No, return a callable function to be consistent with methods.
        return NativeFunction((_, args, __, ___) {
          if (args.isNotEmpty) {
            throw RuntimeError("toString() takes no arguments.");
          }
          return prefixValue.toString();
        }, arity: 0, name: 'toString');
      } else if (memberName == 'name') {
        Logger.debug(
            "[PrefixedIdentifier] Explicitly accessing 'name' on enum value '$prefixValue'. Returning value.");
        return prefixValue.name; // Access directly the 'name' property
      } else {
        Logger.debug(
            "[PrefixedIdentifier] Accessing member '$memberName' on enum value '$prefixValue'. Calling get()...");
        try {
          // Pass 'this' (the visitor) to allow getter execution if needed.
          return prefixValue.get(memberName, this);
        } on ReturnException catch (e) {
          // If get() executes a getter that throws ReturnException
          return e.value;
        } catch (e) {
          // Propagate other errors from get()
          throw RuntimeError(
              "Error getting member '$memberName' from enum value '$prefixValue': $e");
        }
      }
    } else if (toBridgedInstance(prefixValue).$2) {
      final bridgedInstance = toBridgedInstance(prefixValue).$1!;
      final getterAdapter =
          bridgedInstance.bridgedClass.findInstanceGetterAdapter(memberName);
      if (getterAdapter != null) {
        return getterAdapter(this, bridgedInstance.nativeObject);
      }
      final methodAdapter =
          bridgedInstance.bridgedClass.findInstanceMethodAdapter(memberName);
      if (methodAdapter != null) {
        return BridgedMethodCallable(bridgedInstance, methodAdapter, memberName)
            .call(this, [], {});
      }

      // No adapter found, try extension methods/getters
      try {
        final extensionMember =
            environment.findExtensionMember(bridgedInstance, memberName);
        if (extensionMember is InterpretedExtensionMethod) {
          if (extensionMember.isGetter) {
            Logger.debug(
                "[PrefixedIdentifier] Found extension getter '$memberName' for ${bridgedInstance.bridgedClass.name}. Calling...");
            final extensionArgs = <Object?>[bridgedInstance];
            return extensionMember.call(this, extensionArgs, {});
          } else if (!extensionMember.isOperator && !extensionMember.isSetter) {
            Logger.debug(
                "[PrefixedIdentifier] Found extension method '$memberName' for ${bridgedInstance.bridgedClass.name}. Returning callable.");
            return BoundExtensionMethodCallable(
                bridgedInstance, extensionMember);
          }
        }
      } on RuntimeError catch (findError) {
        Logger.debug(
            "[PrefixedIdentifier] Error finding extension '$memberName' for ${bridgedInstance.bridgedClass.name}: ${findError.message}");
      }

      throw RuntimeError(
          "Undefined property or method '$memberName' on bridged instance of '${bridgedInstance.bridgedClass.name}'.");
    } else if (prefixValue is InterpretedRecord) {
      // Accessing field of a record
      final record = prefixValue;
      Logger.debug(
          "[PrefixedIdentifier] Access on InterpretedRecord: .$memberName");
      // Check if it's a positional field access ($1, $2, ...)
      if (memberName.startsWith('\$') && memberName.length > 1) {
        try {
          final index = int.parse(memberName.substring(1)) - 1;
          if (index >= 0 && index < record.positionalFields.length) {
            return record.positionalFields[index];
          } else {
            throw RuntimeError(
                "Record positional field index \$$index out of bounds (0..${record.positionalFields.length - 1}).");
          }
        } catch (e) {
          // Handle parse errors or other issues
          throw RuntimeError(
              "Invalid positional record field accessor '$memberName'.");
        }
      } else {
        // Check if it's a named field access
        if (record.namedFields.containsKey(memberName)) {
          return record.namedFields[memberName];
        } else {
          throw RuntimeError(
              "Record has no field named '$memberName'. Available fields: ${record.namedFields.keys.join(', ')}");
        }
      }
    } else if (prefixValue is BridgedEnum) {
      Logger.debug(
          "[PrefixedIdentifier] Accessing value on BridgedEnum: ${prefixValue.name}.$memberName");
      final enumValue = prefixValue.getValue(memberName);
      if (enumValue != null) {
        return enumValue; // Return the BridgedEnumValue
      } else {
        throw RuntimeError(
            "Undefined enum value '$memberName' on bridged enum '${prefixValue.name}'.");
      }
    } else if (prefixValue is BridgedEnumValue) {
      final bridgedEnumValue = prefixValue;
      Logger.debug(
          "[PrefixedIdentifier] Accessing property '$memberName' on BridgedEnumValue (within InterpretedEnumValue block): $bridgedEnumValue");
      try {
        // Use the get() method of BridgedEnumValue
        return bridgedEnumValue.get(memberName);
      } on ReturnException catch (e) {
        return e.value;
      } on RuntimeError {
        // Relaunch the RuntimeErrors directly
        rethrow;
      } catch (e, s) {
        // Catch other potential errors (ex: from the adapter)
        Logger.error(
            "[PrefixedIdentifier] Native exception during bridged enum property get '$bridgedEnumValue.$memberName': $e\n$s");
        throw RuntimeError(
            "Native error during bridged enum property get '$memberName' on $bridgedEnumValue: $e");
      }
    } else {
      try {
        final extensionMember =
            environment.findExtensionMember(prefixValue, memberName);

        if (extensionMember is InterpretedExtensionMethod) {
          // Handle extension getter call immediately
          if (extensionMember.isGetter) {
            Logger.debug(
                "[PrefixedIdentifier] Found extension getter '$memberName' (fallback). Calling...");
            final extensionPositionalArgs = [
              prefixValue
            ]; // Getter takes receiver
            return extensionMember.call(this, extensionPositionalArgs, {});
          } else if (!extensionMember.isOperator && !extensionMember.isSetter) {
            // Return extension method itself if it's not a getter/setter/operator
            Logger.debug(
                "[PrefixedIdentifier] Found extension method '$memberName' (fallback). Returning callable.");
            return extensionMember;
          }
          // If it's an operator or setter, we probably shouldn't reach here via PrefixedIdentifier
          // Fall through to Stdlib if it wasn't a getter or regular method.
        }
        // If no suitable extension found, proceed to Stdlib call
        Logger.debug(
            "[PrefixedIdentifier] No suitable extension found for '$memberName' (fallback). Trying Stdlib...");
      } on RuntimeError catch (findError) {
        // If findExtensionMember itself threw (e.g., type not found), proceed to Stdlib
        Logger.debug(
            "[PrefixedIdentifier] Error finding extension '$memberName' (fallback): ${findError.message}. Trying Stdlib...");
      }

      throw RuntimeError(
          "Cannot access property '$memberName' on target of type ${prefixValue?.runtimeType}.");
    }
  }

  @override
  Object? visitBinaryExpression(BinaryExpression node) {
    final operator = node.operator.type;
    final leftOperandValue = node.leftOperand.accept<Object?>(this);
    final rightOperandValue = node.rightOperand.accept<Object?>(this);
    if (leftOperandValue is AsyncSuspensionRequest) {
      return leftOperandValue;
    }
    if (rightOperandValue is AsyncSuspensionRequest) {
      return rightOperandValue;
    }
    // Handle logical OR (||) with short-circuiting FIRST
    if (operator == TokenType.BAR_BAR) {
      // Evaluate left operand FIRST
      final leftValue = leftOperandValue;
      if (leftValue is! bool) {
        throw RuntimeError(
            "Left operand of '||' must be bool, got ${leftValue?.runtimeType}.");
      }
      // If left is true, return true without evaluating right
      if (leftValue) return true;
      // Left is false, evaluate right operand
      final rightValue = rightOperandValue;
      if (rightValue is! bool) {
        throw RuntimeError(
            "Right operand of '||' must be bool, got ${rightValue?.runtimeType}.");
      }
      return rightValue;
    }

    // Handle logical AND (&&) with short-circuiting SECOND
    if (operator == TokenType.AMPERSAND_AMPERSAND) {
      // Evaluate left operand FIRST
      final leftValue = leftOperandValue;
      if (leftValue is! bool) {
        throw RuntimeError(
            "Left operand of '&&' must be bool, got ${leftValue?.runtimeType}.");
      }
      // If left is false, return false without evaluating right
      if (!leftValue) return false;
      // Left is true, evaluate right operand
      final rightValue = rightOperandValue;
      if (rightValue is! bool) {
        throw RuntimeError(
            "Right operand of '&&' must be bool, got ${rightValue?.runtimeType}.");
      }
      return rightValue;
    }
    if (operator == TokenType.QUESTION_QUESTION) {
      final leftValue = leftOperandValue;
      final rightValue = rightOperandValue;
      return leftValue ?? rightValue;
    }

    final leftBridgedInstance = toBridgedInstance(leftOperandValue);
    final left = leftBridgedInstance.$2
        ? leftBridgedInstance.$1!.nativeObject
        : (leftOperandValue is InterpretedFunction)
            ? leftOperandValue.call(this, [])
            : leftOperandValue;
    final rightBridgedInstance = toBridgedInstance(rightOperandValue);
    final right = rightBridgedInstance.$2
        ? rightBridgedInstance.$1!.nativeObject
        : (rightOperandValue is InterpretedFunction)
            ? rightOperandValue.call(this, [])
            : rightOperandValue;

    if (left is num && right is num) {
      switch (operator) {
        case TokenType.PLUS:
          return left + right;
        case TokenType.MINUS:
          return left - right;
        case TokenType.STAR:
          return left * right;
        case TokenType.SLASH:
          if (right == 0) throw RuntimeError("Division par zéro.");
          return left.toDouble() / right.toDouble();
        case TokenType.GT:
          return left > right;
        case TokenType.LT:
          return left < right;
        case TokenType.GT_EQ:
          return left >= right;
        case TokenType.LT_EQ:
          return left <= right;
        case TokenType.PERCENT:
          if (right == 0) throw RuntimeError("Modulo par zéro.");
          return left % right;
        case TokenType.TILDE_SLASH:
          if (right == 0) throw RuntimeError("Division entière par zéro.");
          return left ~/ right;
        default:
          break;
      }
    }

    // Moved this block BEFORE standard ==, !=, <, <=, >, >= checks
    final operatorName = operator.lexeme;

    // Check for class operator methods FIRST (before extensions and built-in operators)
    if (leftOperandValue is InterpretedInstance) {
      final operatorMethod = leftOperandValue.findOperator(operatorName);
      if (operatorMethod != null) {
        Logger.debug(
            "[BinaryExpression] Found class operator '$operatorName' on ${leftOperandValue.klass.name}. Calling...");
        try {
          return operatorMethod
              .bind(leftOperandValue)
              .call(this, [rightOperandValue], {});
        } on ReturnException catch (e) {
          return e.value;
        } catch (e) {
          throw RuntimeError(
              "Error executing class operator '$operatorName': $e");
        }
      }
    }

    // Only try extension immediately for operators where standard checks might bypass it
    // (e.g., ==, !=, <, >, <=, >= which have generic fallbacks)
    bool checkExtensionEarly = [
      '==',
      '!=',
      '<',
      '<=',
      '>',
      '>=',
      '|', // Also check early for missing operators like |
      '&', // and &
      '^' // and ^ if BigInt support was incomplete
      // Add other operators here if needed
    ].contains(operatorName);

    if (checkExtensionEarly) {
      try {
        final extensionOperator =
            environment.findExtensionMember(leftOperandValue, operatorName);

        if (extensionOperator is InterpretedExtensionMethod &&
            extensionOperator.isOperator) {
          Logger.debug(
              "[BinaryExpression] Found extension operator '$operatorName' (early check) for type ${leftOperandValue?.runtimeType}. Calling...");
          final extensionPositionalArgs = [leftOperandValue, rightOperandValue];
          try {
            return extensionOperator.call(this, extensionPositionalArgs, {});
          } on ReturnException catch (e) {
            return e.value;
          } catch (e) {
            throw RuntimeError(
                "Error executing extension operator '$operatorName': $e");
          }
        }
        // If no suitable extension operator found early, continue to standard checks
        Logger.debug(
            "[BinaryExpression] No suitable extension operator '$operatorName' found (early check) for type ${leftOperandValue?.runtimeType}. Continuing...");
      } on RuntimeError catch (findError) {
        // findExtensionMember throws if no member is found at all.
        Logger.debug(
            "[BinaryExpression] No extension member '$operatorName' found (early check) for type ${leftOperandValue?.runtimeType}. Error: ${findError.message}");
        // Continue to standard checks even if lookup failed early
      }
    }

    switch (operator.lexeme) {
      case '+':
        if (left is String && right is String) return left + right;
        if (left is BigInt && right is BigInt) return left + right;
        if (left is Duration && right is Duration) return left + right;
        if (left is List && right is List) return left + right;
      case '-':
        if (left is BigInt && right is BigInt) return left - right;
        if (left is Duration && right is Duration) return left - right;
      case '*':
        if (left is String && right is int) return left * right;
        if (left is BigInt && right is BigInt) return left * right;
        if (left is Duration && right is num) return left * right;
      case '/':
        if (left is BigInt && right is BigInt) return left / right;
      case '~/':
        if (left is BigInt && right is BigInt) return left ~/ right;
        if (left is Duration && right is int) return left ~/ right;
      case '%':
        if (left is BigInt && right is BigInt) return left % right;
      case '==':
        return left == right;
      case '!=':
        return left != right;
      case '<':
        return left as dynamic < right;
      case '<=':
        return left as dynamic <= right;
      case '>':
        return left as dynamic > right;
      case '>=':
        return left as dynamic >= right;
      case '^':
        if (left is int && right is int) return left ^ right;
        if (left is BigInt && right is BigInt) return left ^ right;
        throw RuntimeError('Unsupported binary operator "$operator"');
      case '&':
        if (left is int && right is int) return left & right;
        if (left is BigInt && right is BigInt) return left & right;
        throw RuntimeError('Unsupported binary operator "$operator"');
      case '|':
        if (left is int && right is int) return left | right;
        if (left is BigInt && right is BigInt) return left | right;
        throw RuntimeError('Unsupported binary operator "$operator"');
      case '>>':
        if (left is int && right is int) return left >> right;
        if (left is BigInt && right is int) return left >> right;
        throw RuntimeError('Unsupported binary operator "$operator"');
      case '<<':
        if (left is int && right is int) return left << right;
        if (left is BigInt && right is int) return left << right;
        throw RuntimeError('Unsupported binary operator "$operator"');
      case '>>>':
        if (left is int && right is int) return left >>> right;
        // Note: BigInt doesn't support >>> operator in Dart
        throw RuntimeError('Unsupported binary operator "$operator"');
      default:
        break;
    }

    if (operator == TokenType.PLUS && (left is String || right is String)) {
      return '${stringify(left)}${stringify(right)}'; // stringify already handles BridgedInstance indirectly via toString
    }

    if (!checkExtensionEarly) {
      // Only run this if we didn't already check (and potentially succeed/fail) earlier
      try {
        final extensionOperator =
            environment.findExtensionMember(leftOperandValue, operatorName);

        if (extensionOperator is InterpretedExtensionMethod &&
            extensionOperator.isOperator) {
          Logger.debug(
              "[BinaryExpression] Found extension operator '$operatorName' (late check) for type ${leftOperandValue?.runtimeType}. Calling...");
          final extensionPositionalArgs = [leftOperandValue, rightOperandValue];
          try {
            return extensionOperator.call(this, extensionPositionalArgs, {});
          } on ReturnException catch (e) {
            return e.value; // Should not happen for operators, but handle
          } catch (e) {
            throw RuntimeError(
                "Error executing extension operator '$operatorName': $e");
          }
        }
        Logger.debug(
            "[BinaryExpression] No suitable extension operator '$operatorName' found (late check) for type ${leftOperandValue?.runtimeType}.");
      } on RuntimeError catch (findError) {
        Logger.debug(
            "[BinaryExpression] No extension member '$operatorName' found (late check) for type ${leftOperandValue?.runtimeType}. Error: ${findError.message}");
        // Fall through to the final standard error below.
      }
    }

    throw RuntimeError(
      'Opérateur non supporté ($operator) pour les types ${leftOperandValue?.runtimeType} et ${rightOperandValue?.runtimeType}',
    );
  }

  @override
  Object? visitIndexExpression(IndexExpression node) {
    final target = node.target;
    final index = node.index;
    final targetValue = target?.accept<Object?>(this);
    final indexValue = index.accept<Object?>(this);

    if (targetValue is AsyncSuspensionRequest) return targetValue;
    if (indexValue is AsyncSuspensionRequest) return indexValue;

    if (targetValue is Map) {
      return targetValue[indexValue];
    }
    if (targetValue is String && indexValue is int) {
      return targetValue[indexValue];
    } else if (targetValue is List) {
      if (indexValue is int) {
        if (indexValue < 0 || indexValue >= targetValue.length) {
          throw RuntimeError('Index out of range: $indexValue');
        }
        return targetValue[indexValue];
      } else {
        throw RuntimeError('List index must be an integer');
      }
    } else if (targetValue is InterpretedInstance) {
      // Check for class operator [] method
      final operatorMethod = targetValue.findOperator('[]');
      if (operatorMethod != null) {
        Logger.debug(
            "[visitIndexExpression] Found class operator '[]' on ${targetValue.klass.name}. Calling...");
        try {
          return operatorMethod.bind(targetValue).call(this, [indexValue], {});
        } on ReturnException catch (e) {
          return e.value;
        } catch (e) {
          throw RuntimeError("Error executing class operator '[]': $e");
        }
      }
    } else if (toBridgedInstance(targetValue).$2) {
      final bridgedInstance = toBridgedInstance(targetValue).$1!;
      final bridgedClass = bridgedInstance.bridgedClass;
      final operatorName = '[]';

      final methodAdapter =
          bridgedClass.findInstanceMethodAdapter(operatorName);

      if (methodAdapter != null) {
        Logger.debug(
            "[visitIndexExpression] Found bridged operator '$operatorName' for ${bridgedClass.name}. Calling adapter...");
        try {
          return methodAdapter(
              this, bridgedInstance.nativeObject, [indexValue], {});
        } catch (e, s) {
          Logger.error(
              "[visitIndexExpression] Native exception during bridged operator '$operatorName' on ${bridgedClass.name}: $e\\n$s");
          throw RuntimeError(
              "Native error during bridged operator '$operatorName' on ${bridgedClass.name}: $e");
        }
      }
      Logger.debug(
          "[visitIndexExpression] Bridged operator '$operatorName' not found directly for ${bridgedClass.name}. Trying extensions.");
    }

    const operatorNameForExtension = '[]';
    try {
      final extensionOperator = environment.findExtensionMember(
          targetValue, operatorNameForExtension);

      if (extensionOperator is InterpretedExtensionMethod &&
          extensionOperator.isOperator) {
        Logger.debug(
            "[IndexExpression] Found extension operator '[]' for type ${targetValue?.runtimeType}. Calling...");
        final extensionPositionalArgs = [targetValue, indexValue];
        try {
          return extensionOperator.call(this, extensionPositionalArgs, {});
        } on ReturnException catch (e) {
          return e.value;
        } catch (e) {
          throw RuntimeError("Error executing extension operator '[]': $e");
        }
      }
      Logger.debug(
          "[IndexExpression] No suitable extension operator '[]' found for type ${targetValue?.runtimeType}.");
    } on RuntimeError catch (findError) {
      Logger.debug(
          "[IndexExpression] No extension member '[]' found for type ${targetValue?.runtimeType}. Error: ${findError.message}");
    }

    throw RuntimeError(
        'Unsupported target for indexing: ${targetValue?.runtimeType}');
  }

  @override
  Object? visitAssignmentExpression(AssignmentExpression node) {
    final lhs = node.leftHandSide;
    // Evaluate RHS once, used by multiple branches below
    Object? rhsValue = node.rightHandSide.accept<Object?>(this);

    // Handle suspension on the right-hand side
    if (rhsValue is AsyncSuspensionRequest) {
      Logger.debug(
          "[visitAssignmentExpression] RHS suspended. Propagating suspension.");
      // The state machine (_determineNextNodeAfterAwait) will handle resumption.
      // It needs to know this AssignmentExpression was the context.
      return rhsValue;
    }
    // END NEW

    final operatorType = node.operator.type;

    // Case 1: Simple variable assignment (lexical or implicit this)
    if (lhs is SimpleIdentifier) {
      final variableName = lhs.name;

      Environment? definingEnv =
          environment.findDefiningEnvironment(variableName);

      if (definingEnv != null) {
        if (operatorType == TokenType.EQ) {
          return environment.assign(
              variableName, rhsValue); // Use original assign for lexical
        } else {
          // Handle compound assignments on lexical variables
          final currentValue =
              environment.get(variableName); // Get from lexical scope
          Object? newValue =
              computeCompoundValue(currentValue, rhsValue, operatorType);
          return environment.assign(
              variableName, newValue); // Assign back to lexical scope
        }
      } else {
        try {
          final thisInstance = environment.get('this');
          if (thisInstance is InterpretedInstance) {
            if (operatorType == TokenType.EQ) {
              Logger.debug(
                  "[Assignment - implicit this] Checking for direct setter '$variableName' on ${thisInstance.runtimeType}");
              final setter =
                  thisInstance.klass.findInstanceSetter(variableName);
              if (setter != null) {
                Logger.debug(
                    "[Assignment - implicit this] Found direct setter. Calling...");
                // Call setter on 'this'
                setter.bind(thisInstance).call(this, [rhsValue], {});
                return rhsValue; // Assignment expression returns RHS value
              } else {
                Logger.debug(
                    "[Assignment - implicit this] No direct setter found. Trying extension setter for '$variableName' on ${thisInstance.runtimeType}");
                final extensionSetter =
                    environment.findExtensionMember(thisInstance, variableName);
                if (extensionSetter is InterpretedExtensionMethod &&
                    extensionSetter.isSetter) {
                  Logger.debug(
                      "[Assignment - implicit this] Found extension setter '$variableName'. Calling...");
                  final extensionPositionalArgs = [
                    thisInstance, // Target is 'this'
                    rhsValue // Value to assign
                  ];
                  try {
                    extensionSetter.call(this, extensionPositionalArgs, {});
                    Logger.debug(
                        "[Assignment - implicit this] Extension setter call finished.");
                    return rhsValue; // Simple assignment returns RHS
                  } catch (e) {
                    throw RuntimeError(
                        "Error executing extension setter '$variableName' via implicit 'this': $e");
                  }
                } else {
                  Logger.debug(
                      "[Assignment - implicit this] No extension setter found for '$variableName'. Falling back to direct field set.");
                  // Assign directly to field on 'this' (original fallback)
                  // WARNING: This might be incorrect if the intent was purely extension based.
                  // Dart would typically throw if no setter (direct or extension) exists and no field exists.
                  // Consider throwing here if direct field assignment isn't desired.
                  try {
                    thisInstance.set(variableName, rhsValue, this);
                    Logger.debug(
                        "[Assignment - implicit this] Direct field set successful (?).");
                    return rhsValue; // Assignment expression returns RHS value
                  } on RuntimeError catch (fieldSetError) {
                    Logger.debug(
                        "[Assignment - implicit this] Direct field set failed: ${fieldSetError.message}");
                    // If both direct setter, extension setter, and direct field set fail, THEN throw.
                    throw RuntimeError(
                        "Cannot assign to '$variableName' on implicit 'this': No setter (direct or extension) or assignable field found.");
                  }
                }
              }
            } else {
              // 1. Get current value from 'this' (field or getter)
              final currentValue = thisInstance
                  .get(variableName); // May throw if undefined on instance

              // 2. Calculate new value
              Object? newValue =
                  computeCompoundValue(currentValue, rhsValue, operatorType);

              // 3. Set new value on 'this' (field or setter)
              final setter =
                  thisInstance.klass.findInstanceSetter(variableName);
              if (setter != null) {
                setter.bind(thisInstance).call(this, [newValue], {});
              } else {
                thisInstance.set(variableName, newValue, this);
              }
              // Compound assignment returns the NEW value
              return newValue;
            }
          } else if (toBridgedInstance(thisInstance).$2) {
            if (rhsValue is BridgedEnumValue) {
              rhsValue = rhsValue.nativeValue;
            }
            final bridgedInstance = toBridgedInstance(thisInstance).$1!;
            final bridgedClass = bridgedInstance.bridgedClass;
            final setterAdapter =
                bridgedClass.findInstanceSetterAdapter(variableName);

            if (setterAdapter != null) {
              if (operatorType == TokenType.EQ) {
                // Simple assignment: this.bridgedProp = value
                Logger.debug(
                    "[Assignment] Assigning to bridged 'this'.$variableName via setter adapter.");
                setterAdapter(this, thisInstance.nativeObject, rhsValue);
                return rhsValue; // Simple assignment returns RHS value
              } else {
                // Compound assignment: this.bridgedProp op= value
                // 1. Get current value (requires a getter adapter)
                final getterAdapter =
                    bridgedClass.findInstanceGetterAdapter(variableName);
                if (getterAdapter == null) {
                  throw RuntimeError(
                      "Cannot perform compound assignment on '${bridgedClass.name}.$variableName' via implicit 'this': No getter found.");
                }
                final currentValue =
                    getterAdapter(this, thisInstance.nativeObject);
                // 2. Calculate new value
                Object? newValue =
                    computeCompoundValue(currentValue, rhsValue, operatorType);
                // 3. Set new value via setter adapter
                Logger.debug(
                    "[Assignment] Compound assigning to bridged 'this'.$variableName via setter adapter.");
                setterAdapter(this, thisInstance.nativeObject, newValue);
                return newValue; // Compound assignment returns new value
              }
            } else {
              // No setter adapter found
              throw RuntimeError(
                  "Cannot assign to property '$variableName' on bridged instance of '${bridgedClass.name}' accessed via implicit 'this': No setter found.");
            }
          } else {
            // 'this' exists but is not an InterpretedInstance or BridgedInstance
            throw RuntimeError(
                "Assigning to undefined variable '$variableName'.");
          }
        } on RuntimeError catch (e) {
          // If 'this' doesn't exist or getting/setting on 'this' failed
          // Use the original error if it came from get/set, otherwise standard undefined.
          if (e.message.contains("Undefined property '$variableName'") ||
              e.message.contains("Undefined static member")) {
            rethrow; // Propagate specific error from get/set
          }
          throw RuntimeError(
              "Assigning to undefined variable '$variableName'.");
        }
      }
    }

    // Case 2: PropertyAccess assignment (target.property op= value)
    else if (lhs is PropertyAccess) {
      final targetExpression = lhs.target; // Keep expression for check below
      final targetValue = targetExpression?.accept<Object?>(this);
      final propertyName = lhs.propertyName.name;
      // rhsValue and operatorType already available from the top

      if (targetValue is BoundSuper) {
        // This handles cases like: super.value = expression;
        if (operatorType != TokenType.EQ) {
          throw UnimplementedError(
              "Compound assignment operators (+=, -=, etc.) on 'super' properties are not yet implemented.");
        }
        final instance = targetValue.instance;
        final startClass = targetValue.startLookupClass;
        InterpretedClass? currentClass = startClass;
        InterpretedFunction? superSetter;

        // Look for the setter in the superclass hierarchy starting from startClass
        while (currentClass != null) {
          final setter = currentClass.findInstanceSetter(propertyName);
          if (setter != null) {
            superSetter = setter;
            break;
          }
          currentClass = currentClass.superclass;
        }

        if (superSetter != null) {
          // Bind the found super setter to the original instance ('this') and call it
          superSetter.bind(instance).call(this, [rhsValue], {});
          return rhsValue; // Simple assignment returns RHS value
        } else {
          // No setter found in superclass hierarchy.
          // Dart would typically throw an error here unless a field is directly accessible
          // via super and assignable, which isn't common. Let's throw.
          throw RuntimeError(
              "Setter for '$propertyName' not found in superclass chain of '${instance.klass.name}' for 'super' assignment.");
        }
      } else if (targetValue is InterpretedInstance) {
        // This code block was accidentally removed or modified, restore it.
        if (operatorType == TokenType.EQ) {
          // Simple assignment: target.property = rhsValue
          final setter = targetValue.klass.findInstanceSetter(propertyName);
          if (setter != null) {
            setter.bind(targetValue).call(this, [rhsValue], {});
            Logger.debug(
                "[Assignment] Assigned via direct setter for '$propertyName'");
            return rhsValue;
          }
          // No direct setter, try extension setter
          final extensionSetter =
              environment.findExtensionMember(targetValue, propertyName);
          if (extensionSetter is InterpretedExtensionMethod &&
              extensionSetter.isSetter) {
            Logger.debug(
                "[Assignment] Assigning via extension setter for '$propertyName'");
            final extensionPositionalArgs = [
              targetValue,
              rhsValue
            ]; // Target + value
            try {
              extensionSetter.call(this, extensionPositionalArgs, {});
              return rhsValue;
            } catch (e) {
              throw RuntimeError(
                  "Error executing extension setter '$propertyName': $e");
            }
          }
          // No direct or extension setter, assign to field
          Logger.debug(
              "[Assignment] No direct or extension setter found for '$propertyName', assigning to field.");
          targetValue.set(propertyName, rhsValue, this);
          return rhsValue; // Simple Assignment returns RHS value
        } else {
          // Compound assignment: target.property op= rhsValue
          // 1. Get current value
          final currentValue = targetValue
              .get(propertyName); // Use instance.get (handles field/getter)
          // 2. Calculate new value
          Object? newValue =
              computeCompoundValue(currentValue, rhsValue, operatorType);
          // 3. Set new value (via setter or direct field access)
          final setter = targetValue.klass.findInstanceSetter(propertyName);
          if (setter != null) {
            setter.bind(targetValue).call(this, [newValue], {});
          } else {
            // Assign directly to field if no setter (using the instance's set method)
            targetValue.set(propertyName, newValue, this);
          }
          return newValue; // Compound returns new value
        }
      }
      // Handle assignment to static property (targetValue is InterpretedClass)
      else if (targetValue is InterpretedClass) {
        if (operatorType == TokenType.EQ) {
          // Simple assignment: Class.property = rhsValue
          final staticSetter = targetValue.findStaticSetter(propertyName);
          if (staticSetter != null) {
            staticSetter.call(this, [rhsValue], {});
          } else {
            // Assign directly to static field if no setter
            targetValue.setStaticField(propertyName, rhsValue);
          }
          return rhsValue; // Simple Assignment returns RHS value
        } else {
          // Compound assignment: Class.property op= rhsValue
          // 1. Get current value (static field or getter)
          Object? currentValue;
          final staticGetter = targetValue.findStaticGetter(propertyName);
          if (staticGetter != null) {
            currentValue = staticGetter.call(this, [], {});
          } else {
            // If no getter, try getting the field directly
            try {
              currentValue = targetValue.getStaticField(propertyName);
            } catch (_) {
              throw RuntimeError(
                  "Cannot get value for compound assignment on static member '$propertyName'. No getter or field found.");
            }
          }

          // 2. Calculate new value
          Object? newValue =
              computeCompoundValue(currentValue, rhsValue, operatorType);

          // 3. Set new value (static setter or direct field access)
          final staticSetter = targetValue.findStaticSetter(propertyName);
          if (staticSetter != null) {
            staticSetter.call(this, [newValue], {});
          } else {
            targetValue.setStaticField(propertyName, newValue);
          }
          return newValue; // Compound returns new value
        }
      } else if (toBridgedInstance(targetValue).$2) {
        if (rhsValue is BridgedEnumValue) {
          rhsValue = rhsValue.nativeValue;
        }
        final bridgedInstance = toBridgedInstance(targetValue).$1!;
        final setterAdapter = bridgedInstance.bridgedClass
            .findInstanceSetterAdapter(propertyName);

        if (setterAdapter != null) {
          if (operatorType == TokenType.EQ) {
            // Simple assignment: bridgedInstance.property = value
            Logger.debug(
                "[Assignment] Assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.");
            setterAdapter(this, bridgedInstance.nativeObject, rhsValue);
            return rhsValue; // Simple assignment returns RHS value
          } else {
            // Compound assignment: bridgedInstance.property op= value
            // 1. Get current value (requires a getter adapter)
            final getterAdapter = bridgedInstance.bridgedClass
                .findInstanceGetterAdapter(propertyName);
            if (getterAdapter == null) {
              throw RuntimeError(
                  "Cannot perform compound assignment on '${bridgedInstance.bridgedClass.name}.$propertyName': No getter adapter found.");
            }
            final currentValue =
                getterAdapter(this, bridgedInstance.nativeObject);
            // 2. Calculate new value
            Object? newValue =
                computeCompoundValue(currentValue, rhsValue, operatorType);
            // 3. Set new value via setter adapter
            Logger.debug(
                "[Assignment] Compound assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.");
            setterAdapter(this, bridgedInstance.nativeObject, newValue);
            return newValue; // Compound assignment returns new value
          }
        } else {
          // No setter adapter found
          throw RuntimeError(
              "Cannot assign to property '$propertyName' on bridged instance of '${bridgedInstance.bridgedClass.name}': No setter adapter found.");
        }
      } else if (targetValue is BoundBridgedSuper) {
        if (rhsValue is BridgedEnumValue) {
          rhsValue = rhsValue.nativeValue;
        }
        // This handles: super.property = rhsValue;
        if (operatorType != TokenType.EQ) {
          throw UnimplementedError(
              "Compound assignment operators (+=, -=, etc.) on bridged 'super' properties are not yet implemented.");
        }
        final instance = targetValue.instance; // Instance 'this'
        final bridgedSuper = targetValue.startLookupClass;
        final nativeSuperObject = instance.bridgedSuperObject;

        if (nativeSuperObject == null) {
          throw RuntimeError(
              "Internal error: Cannot assign to super property '$propertyName' on bridged superclass '${bridgedSuper.name}' because the native super object is missing.");
        }

        // Find the bridged setter adapter
        final setterAdapter =
            bridgedSuper.findInstanceSetterAdapter(propertyName);
        if (setterAdapter != null) {
          try {
            // Call the setter adapter with the native object and the new value
            setterAdapter(this, nativeSuperObject, rhsValue);
            return rhsValue; // Assignment returns the right value
          } catch (e, s) {
            Logger.error(
                "Native exception during super assignment to bridged setter '${bridgedSuper.name}.$propertyName': $e\\n$s");
            throw RuntimeError(
                "Native error during super assignment to bridged setter '$propertyName': $e");
          }
        } else {
          // No setter found
          throw RuntimeError(
              "Setter for '$propertyName' not found in bridged superclass '${bridgedSuper.name}' for 'super' assignment.");
        }
      } else {
        throw RuntimeError(
            "Assignment target must be an instance, class, or super property, got ${targetValue?.runtimeType}.");
      }
    }
    // Case 3: PrefixedIdentifier assignment (prefix.identifier op= value)
    else if (lhs is PrefixedIdentifier) {
      final target = lhs.prefix.accept<Object?>(this);
      final propertyName = lhs.identifier.name;
      // rhsValue and operatorType already available from the top

      if (target is InterpretedInstance) {
        if (operatorType == TokenType.EQ) {
          // Simple assignment: target.property = rhsValue
          final setter = target.klass.findInstanceSetter(propertyName);
          if (setter != null) {
            setter.bind(target).call(this, [rhsValue], {});
            Logger.debug(
                "[Assignment] Assigned via direct setter for PrefixedIdentifier '$propertyName'");
            return rhsValue;
          }

          final extensionSetter =
              environment.findExtensionMember(target, propertyName);
          if (extensionSetter is InterpretedExtensionMethod &&
              extensionSetter.isSetter) {
            Logger.debug(
                "[Assignment] Assigning via extension setter for PrefixedIdentifier '$propertyName'");
            final extensionPositionalArgs = [
              target,
              rhsValue
            ]; // Target + value
            try {
              extensionSetter.call(this, extensionPositionalArgs, {});
              return rhsValue;
            } catch (e) {
              throw RuntimeError(
                  "Error executing extension setter '$propertyName': $e");
            }
          }

          Logger.debug(
              "[Assignment] No direct or extension setter found for PrefixedIdentifier '$propertyName', assigning to field.");
          target.set(propertyName, rhsValue, this);
          return rhsValue; // Simple Assignment returns RHS value
        } else {
          // Compound assignment: target.property op= rhsValue
          final currentValue = target.get(propertyName);
          Object? newValue =
              computeCompoundValue(currentValue, rhsValue, operatorType);
          final setter = target.klass.findInstanceSetter(propertyName);
          if (setter != null) {
            setter.bind(target).call(this, [newValue], {});
          } else {
            target.set(propertyName, newValue, this);
          }
          return newValue; // Compound returns new value
        }
      } else if (target is InterpretedClass) {
        if (operatorType == TokenType.EQ) {
          // Simple assignment: Class.property = rhsValue
          final staticSetter = target.findStaticSetter(propertyName);
          if (staticSetter != null) {
            staticSetter.call(this, [rhsValue], {});
          } else {
            // Assign directly to static field if no setter
            target.setStaticField(propertyName, rhsValue);
          }
          return rhsValue; // Simple Assignment returns RHS value
        } else {
          // Compound assignment: Class.property op= rhsValue
          Object? currentValue;
          final staticGetter = target.findStaticGetter(propertyName);
          if (staticGetter != null) {
            currentValue = staticGetter.call(this, [], {});
          } else {
            try {
              currentValue = target.getStaticField(propertyName);
            } catch (_) {
              throw RuntimeError(
                  "Cannot get value for compound assignment on static member '$propertyName'. No getter or field found.");
            }
          }
          Object? newValue =
              computeCompoundValue(currentValue, rhsValue, operatorType);
          final staticSetter = target.findStaticSetter(propertyName);
          if (staticSetter != null) {
            staticSetter.call(this, [newValue], {});
          } else {
            target.setStaticField(propertyName, newValue);
          }
          return newValue; // Compound returns new value
        }
      } else if (target is BridgedClass) {
        final bridgedClass = target;
        if (operatorType == TokenType.EQ) {
          // Simple assignment: BridgedClass.property = rhsValue
          final staticSetter =
              bridgedClass.findStaticSetterAdapter(propertyName);
          if (staticSetter == null) {
            throw RuntimeError(
                "Bridged class '${bridgedClass.name}' has no static setter named '$propertyName'.");
          }
          Logger.debug(
              "[Assignment] Assigning to static bridged property '${bridgedClass.name}.$propertyName' via setter adapter.");
          staticSetter(this, rhsValue);
          return rhsValue; // Simple Assignment returns RHS value
        } else {
          // Compound assignment: BridgedClass.property op= rhsValue
          // 1. Get current static value
          final staticGetter =
              bridgedClass.findStaticGetterAdapter(propertyName);
          if (staticGetter == null) {
            throw RuntimeError(
                "Cannot perform compound assignment on static '${bridgedClass.name}.$propertyName': No static getter found.");
          }
          final currentValue = staticGetter(this);
          // 2. Calculate new value
          Object? newValue =
              computeCompoundValue(currentValue, rhsValue, operatorType);
          // 3. Set new static value
          final staticSetter =
              bridgedClass.findStaticSetterAdapter(propertyName);
          if (staticSetter == null) {
            // Should have been caught by getter check, but defensive programming
            throw RuntimeError(
                "Cannot perform compound assignment on static '${bridgedClass.name}.$propertyName': No static setter found after getter.");
          }
          Logger.debug(
              "[Assignment] Compound assigning to static bridged property '${bridgedClass.name}.$propertyName' via setter adapter.");
          staticSetter(this, newValue);
          return newValue; // Compound returns new value
        }
      } else if (toBridgedInstance(target).$2) {
        if (rhsValue is BridgedEnumValue) {
          rhsValue = rhsValue.nativeValue;
        }
        final bridgedInstance = toBridgedInstance(target).$1!;

        final setterAdapter = bridgedInstance.bridgedClass
            .findInstanceSetterAdapter(propertyName);

        if (setterAdapter != null) {
          if (operatorType == TokenType.EQ) {
            // Simple assignment: bridgedInstance.property = value
            Logger.debug(
                "[Assignment - PropertyAccess] Assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.");
            setterAdapter(this, bridgedInstance.nativeObject, rhsValue);
            return rhsValue; // Simple assignment returns RHS value
          } else {
            // Compound assignment: bridgedInstance.property op= value
            // 1. Get current value (requires a getter adapter)
            final getterAdapter = bridgedInstance.bridgedClass
                .findInstanceGetterAdapter(propertyName);
            if (getterAdapter == null) {
              throw RuntimeError(
                  "Cannot perform compound assignment on '${bridgedInstance.bridgedClass.name}.$propertyName': No getter adapter found.");
            }
            final currentValue =
                getterAdapter(this, bridgedInstance.nativeObject);
            // 2. Calculate new value
            Object? newValue =
                computeCompoundValue(currentValue, rhsValue, operatorType);
            // 3. Set new value via setter adapter
            Logger.debug(
                "[Assignment - PropertyAccess] Compound assigning to bridged instance property '${bridgedInstance.bridgedClass.name}.$propertyName' via setter adapter.");
            setterAdapter(this, bridgedInstance.nativeObject, newValue);
            return newValue; // Compound assignment returns new value
          }
        } else {
          // No setter adapter found
          throw RuntimeError(
              "Cannot assign to property '$propertyName' on bridged instance of '${bridgedInstance.bridgedClass.name}': No setter adapter found.");
        }
      } else {
        throw RuntimeError(
            "Assignment target must be an instance or class for PrefixedIdentifier, got ${target?.runtimeType}.");
      }
    } else {
      if (lhs is IndexExpression) {
        final targetValue = lhs.target?.accept<Object?>(this);
        final indexValue = lhs.index.accept<Object?>(this);

        // Determine the value to actually assign
        Object? finalValueToAssign;
        if (operatorType == TokenType.EQ) {
          finalValueToAssign = rhsValue; // Simple assignment
        } else {
          // Compound assignment (e.g., list[i] += 10)
          // 1. Get current value using index operator []
          Object? currentValue;
          if (targetValue is Map) {
            currentValue = targetValue[indexValue];
          } else if (targetValue is List && indexValue is int) {
            if (indexValue < 0 || indexValue >= targetValue.length) {
              throw RuntimeError(
                  'Index out of range for compound assignment read: $indexValue');
            }
            currentValue = targetValue[indexValue];
          } else if (targetValue is InterpretedInstance) {
            // Check for class operator [] method for reading current value
            final operatorMethod = targetValue.findOperator('[]');
            if (operatorMethod != null) {
              try {
                currentValue = operatorMethod
                    .bind(targetValue)
                    .call(this, [indexValue], {});
              } on ReturnException catch (e) {
                currentValue = e.value;
              } catch (e) {
                throw RuntimeError(
                    "Error executing class operator '[]' for compound read: $e");
              }
            } else {
              // No class operator found, try extensions
              try {
                final extensionGetter =
                    environment.findExtensionMember(targetValue, '[]');
                if (extensionGetter is InterpretedExtensionMethod &&
                    extensionGetter.isOperator) {
                  final extensionPositionalArgs = [targetValue, indexValue];
                  try {
                    currentValue =
                        extensionGetter.call(this, extensionPositionalArgs, {});
                  } on ReturnException catch (e) {
                    currentValue = e.value;
                  } catch (e) {
                    throw RuntimeError(
                        "Error executing extension operator '[]' for compound read: $e");
                  }
                } else {
                  throw RuntimeError(
                      'Cannot read current value for compound index assignment on ${targetValue.klass.name}: No operator [] found (class or extension).');
                }
              } on RuntimeError catch (e) {
                throw RuntimeError(
                    'Cannot read current value for compound index assignment on ${targetValue.klass.name}: ${e.message}');
              }
            }
          } else if (toBridgedInstance(targetValue).$2) {
            // Handle BridgedInstance for reading current value via [] operator
            final bridgedInstance = toBridgedInstance(targetValue).$1!;
            final bridgedClass = bridgedInstance.bridgedClass;
            final operatorName = '[]';

            final methodAdapter =
                bridgedClass.findInstanceMethodAdapter(operatorName);
            if (methodAdapter != null) {
              Logger.debug(
                  "[visitAssignmentExpression-Index] Found bridged operator '$operatorName' for ${bridgedClass.name}. Calling adapter for compound read...");
              try {
                currentValue = methodAdapter(
                    this, bridgedInstance.nativeObject, [indexValue], {});
              } catch (e, s) {
                Logger.error(
                    "[visitAssignmentExpression-Index] Native exception during bridged operator '$operatorName' read on ${bridgedClass.name}: $e\\n$s");
                throw RuntimeError(
                    "Native error during bridged operator '$operatorName' read on ${bridgedClass.name}: $e");
              }
            } else {
              throw RuntimeError(
                  'Cannot read current value for compound index assignment on ${bridgedClass.name}: No bridged operator [] found.');
            }
          } else {
            try {
              final extensionGetter =
                  environment.findExtensionMember(targetValue, '[]');
              if (extensionGetter is InterpretedExtensionMethod &&
                  extensionGetter.isOperator) {
                final extensionPositionalArgs = [targetValue, indexValue];
                try {
                  currentValue =
                      extensionGetter.call(this, extensionPositionalArgs, {});
                } on ReturnException catch (e) {
                  currentValue = e.value;
                } // Handle potential returns
                catch (e) {
                  throw RuntimeError(
                      "Error executing extension operator '[]' for compound read: $e");
                }
              } else {
                throw RuntimeError(
                    'Cannot read current value for compound index assignment on type ${targetValue?.runtimeType}: No standard or extension operator [] found.');
              }
            } on RuntimeError catch (e) {
              throw RuntimeError(
                  'Cannot read current value for compound index assignment on type ${targetValue?.runtimeType}: ${e.message}');
            }
          }

          // 2. Calculate the new value
          finalValueToAssign =
              computeCompoundValue(currentValue, rhsValue, operatorType);
        }

        // Now, perform the assignment with finalValueToAssign
        if (targetValue is Map) {
          targetValue[indexValue] = finalValueToAssign;
          return finalValueToAssign;
        } else if (targetValue is List && indexValue is int) {
          if (indexValue < 0 || indexValue >= targetValue.length) {
            throw RuntimeError(
                'Index out of range for assignment: $indexValue');
          }
          targetValue[indexValue] = finalValueToAssign;
          return finalValueToAssign;
        } else if (targetValue is InterpretedInstance) {
          // Check for class operator []= method
          final operatorMethod = targetValue.findOperator('[]=');
          if (operatorMethod != null) {
            Logger.debug(
                "[visitAssignmentExpression-Index] Found class operator '[]=' on ${targetValue.klass.name}. Calling...");
            try {
              operatorMethod
                  .bind(targetValue)
                  .call(this, [indexValue, finalValueToAssign], {});
              return finalValueToAssign;
            } on ReturnException catch (_) {
              return finalValueToAssign; // []= should not return a value, but assignment expression returns assigned value
            } catch (e) {
              throw RuntimeError("Error executing class operator '[]=': $e");
            }
          } else {
            // No class operator found, try extensions
            const operatorName = '[]=';
            try {
              final extensionSetter =
                  environment.findExtensionMember(targetValue, operatorName);
              if (extensionSetter is InterpretedExtensionMethod &&
                  extensionSetter.isOperator) {
                Logger.debug(
                    "[Assignment] Found extension operator '[]=' for ${targetValue.klass.name}. Calling...");
                // Args: receiver (targetValue), index (indexValue), value (finalValueToAssign)
                final extensionPositionalArgs = [
                  targetValue,
                  indexValue,
                  finalValueToAssign
                ];
                try {
                  extensionSetter.call(this, extensionPositionalArgs, {});
                  // '[]=' operator should not return a meaningful value, but the assignment expression returns the assigned value
                  return finalValueToAssign;
                } catch (e) {
                  throw RuntimeError(
                      "Error executing extension operator '[]=': $e");
                }
              } else {
                throw RuntimeError(
                    'Cannot assign to index on ${targetValue.klass.name}: No operator []= found (class or extension).');
              }
            } on RuntimeError catch (findError) {
              throw RuntimeError(
                  'Cannot assign to index on ${targetValue.klass.name}: ${findError.message}');
            }
          }
        } else if (toBridgedInstance(targetValue).$2) {
          final bridgedInstance = toBridgedInstance(targetValue).$1!;
          final bridgedClass = bridgedInstance.bridgedClass;
          final operatorName = '[]=';

          final methodAdapter =
              bridgedClass.findInstanceMethodAdapter(operatorName);
          if (methodAdapter != null) {
            Logger.debug(
                "[visitAssignmentExpression-Index] Found bridged operator '$operatorName' for ${bridgedClass.name}. Calling adapter...");
            try {
              methodAdapter(this, bridgedInstance.nativeObject,
                  [indexValue, finalValueToAssign], {});
              return finalValueToAssign;
            } catch (e, s) {
              Logger.error(
                  "[visitAssignmentExpression-Index] Native exception during bridged operator '$operatorName' on ${bridgedClass.name}: $e\\n$s");
              throw RuntimeError(
                  "Native error during bridged operator '$operatorName' on ${bridgedClass.name}: $e");
            }
          }
          throw RuntimeError(
              "[Bridged operator '$operatorName' not found directly for ${bridgedClass.name}. Trying extensions.");
        } else {
          const operatorName = '[]=';
          try {
            final extensionSetter =
                environment.findExtensionMember(targetValue, operatorName);
            if (extensionSetter is InterpretedExtensionMethod &&
                extensionSetter.isOperator) {
              Logger.debug(
                  "[Assignment] Found extension operator '[]=' for type ${targetValue?.runtimeType}. Calling...");
              // Args: receiver (targetValue), index (indexValue), value (finalValueToAssign)
              final extensionPositionalArgs = [
                targetValue,
                indexValue,
                finalValueToAssign
              ];
              try {
                extensionSetter.call(this, extensionPositionalArgs, {});
                // '[]=' operator should not return a meaningful value, but the assignment expression returns the assigned value
                return finalValueToAssign;
              } catch (e) {
                throw RuntimeError(
                    "Error executing extension operator '[]=': $e");
              }
            } // else: No suitable extension operator found, fall through
            Logger.debug(
                "[Assignment] No suitable extension operator '[]=' found for type ${targetValue?.runtimeType}.");
          } on RuntimeError catch (findError) {
            Logger.debug(
                "[Assignment] No extension member '[]=' found for type ${targetValue?.runtimeType}. Error: ${findError.message}");
            // Fall through to the final error
          }

          // If neither standard nor extension assignment worked
          throw RuntimeError(
              'Unsupported target for index assignment: ${targetValue?.runtimeType}');
        }
      } else {
        throw UnimplementedError(
            'Assignation à une cible non gérée: ${lhs.runtimeType}');
      }
    }
  }

  @override
  Object? visitMethodInvocation(MethodInvocation node) {
    Object? calleeValue;
    Object? targetValue; // Keep track of the target object/class
    // Argument lists - declared here, evaluated later if needed

    // Determine if this is a conditional call by inspecting the source
    final isNullAware = node.toSource().contains('?.');

    if (node.target == null) {
      // Simple function call (or class constructor call)
      calleeValue = node.methodName.accept<Object?>(this);
      targetValue = null; // No target
    } else {
      // Property/Method call on a target (instance or class)
      targetValue = node.target!.accept<Object?>(this);
      final methodName = node.methodName.name;

      // Null safety support: if the target is null and the call is null-aware, return null
      if (targetValue == null) {
        if (isNullAware) {
          return null;
        }
        throw RuntimeError(
            "Cannot invoke method '$methodName' on null. Use '?.' for null-aware method invocation.");
      }

      // BLOCK FOR HANDLING PREFIXED IMPORTS
      if (targetValue is Environment) {
        Logger.debug(
            "[MethodInvocation] Target is an Environment (prefixed import '${node.target!.toSource()}'). Looking for method '$methodName' in this environment.");
        try {
          calleeValue = targetValue.get(methodName);
          // The 'targetValue' for the call will be null because this is not an instance method on the environment itself,
          // but a function retrieved from this environment.
          // Functions obtained in this way are already "autonomous" or correctly bound if they come from classes.
        } on RuntimeError catch (e) {
          throw RuntimeError(
              "Method '$methodName' not found in imported module '${node.target!.toSource()}'. Error: ${e.message}");
        }
        // calleeValue is now the function/method of the imported module.
        // The call logic will be handled in the final visitMethodInvocation.
      } else if (targetValue is InterpretedInstance) {
        // Instance method call
        try {
          // Get should return the BOUND method
          calleeValue = targetValue.get(methodName);
          Logger.debug(
              "[MethodInvocation] Found direct instance member '$methodName' on ${targetValue.klass.name}. Type: ${calleeValue?.runtimeType}");
        } on RuntimeError catch (e) {
          if (e.message.contains("Undefined property '$methodName'")) {
            Logger.debug(
                "[MethodInvocation] Direct instance method '$methodName' failed/not found on ${targetValue.klass.name}. Error: ${e.message}. Trying extension method...");
            try {
              final extensionCallable =
                  environment.findExtensionMember(targetValue, methodName);

              if (extensionCallable is InterpretedExtensionMethod &&
                  !extensionCallable
                      .isOperator && // Ensure it's a regular method
                  !extensionCallable.isGetter &&
                  !extensionCallable.isSetter) {
                Logger.debug(
                    "[MethodInvocation] Found extension method '$methodName'. Evaluating args and calling...");

                // Evaluate arguments (must be done here as direct call failed)
                final evaluationResult =
                    _evaluateArgumentsAsync(node.argumentList);
                if (evaluationResult is AsyncSuspensionRequest) {
                  return evaluationResult; // Propagate suspension
                }
                final (positionalArgs, namedArgs) =
                    evaluationResult as (List<Object?>, Map<String, Object?>);
                List<RuntimeType>? evaluatedTypeArguments;
                final typeArgsNode = node.typeArguments;
                if (typeArgsNode != null) {
                  evaluatedTypeArguments = typeArgsNode.arguments
                      .map((typeNode) => _resolveTypeAnnotation(typeNode))
                      .toList();
                }

                // Prepare arguments for extension method:
                // First arg is the receiver (the target instance)
                final extensionPositionalArgs = [
                  targetValue,
                  ...positionalArgs
                ];

                // Call the extension method
                try {
                  // Return the result of the extension call directly
                  return extensionCallable.call(this, extensionPositionalArgs,
                      namedArgs, evaluatedTypeArguments);
                } on ReturnException catch (returnExc) {
                  return returnExc.value;
                } catch (execError) {
                  throw RuntimeError(
                      "Error executing extension method '$methodName': $execError");
                }
              } else {
                // No suitable extension found, rethrow the original error from direct lookup
                Logger.debug(
                    "[MethodInvocation] Extension method '$methodName' not found or not applicable. Rethrowing original error.");
                throw RuntimeError(
                    "Instance of '${targetValue.klass.name}' has no method named '$methodName' and no suitable extension method found. Original error: (${e.message})");
              }
            } on RuntimeError catch (findError) {
              // Error during the findExtensionMember call itself
              Logger.debug(
                  "[MethodInvocation] Error during extension lookup for '$methodName': ${findError.message}. Rethrowing original error.");
              throw RuntimeError(
                  "Instance of '${targetValue.klass.name}' has no method named '$methodName'. Error during extension lookup: ${findError.message}. Original error: (${e.message})");
            }
          } else {
            // The error during direct get() wasn't "Undefined property", rethrow it
            rethrow;
          }
        }
        // We check if it's callable later (if direct lookup succeeded)
      } else if (targetValue is InterpretedEnumValue) {
        try {
          // Get should return the BOUND method (or execute getter)
          // Pass the visitor to potentially execute getters
          calleeValue = targetValue.get(methodName, this);
          Logger.debug(
              "[MethodInvocation] Found enum instance member '$methodName' on $targetValue. Type: ${calleeValue?.runtimeType}");
        } on RuntimeError catch (e) {
          // Try Extension Method if Direct Fails (similar to InterpretedInstance)
          if (e.message.contains("Undefined property '$methodName'")) {
            Logger.debug(
                "[MethodInvocation] Direct enum method '$methodName' failed/not found on $targetValue. Error: ${e.message}. Trying extension method...");
            try {
              final extensionCallable =
                  environment.findExtensionMember(targetValue, methodName);
              if (extensionCallable is InterpretedExtensionMethod &&
                  !extensionCallable.isOperator &&
                  !extensionCallable.isGetter &&
                  !extensionCallable.isSetter) {
                Logger.debug(
                    "[MethodInvocation] Found extension method '$methodName' for enum value. Evaluating args and calling...");
                final evaluationResult =
                    _evaluateArgumentsAsync(node.argumentList);
                if (evaluationResult is AsyncSuspensionRequest) {
                  return evaluationResult; // Propagate suspension
                }
                final (positionalArgs, namedArgs) =
                    evaluationResult as (List<Object?>, Map<String, Object?>);
                List<RuntimeType>?
                    evaluatedTypeArguments; // Handle type args if needed

                final extensionPositionalArgs = [
                  targetValue,
                  ...positionalArgs
                ];
                try {
                  return extensionCallable.call(this, extensionPositionalArgs,
                      namedArgs, evaluatedTypeArguments);
                } on ReturnException catch (returnExc) {
                  return returnExc.value;
                } catch (execError) {
                  throw RuntimeError(
                      "Error executing extension method '$methodName' on enum value: $execError");
                }
              } else {
                if (methodName == 'toString') {
                  return targetValue.toString();
                } else if (methodName == 'runtimeType') {
                  return targetValue.runtimeType;
                }
                Logger.debug(
                    "[MethodInvocation] Extension method '$methodName' for enum value not found or not applicable. Rethrowing original error.");
                throw RuntimeError(
                    "Enum value '$targetValue' has no method named '$methodName' and no suitable extension method found. Original error: (${e.message})");
              }
            } on RuntimeError catch (findError) {
              Logger.debug(
                  "[MethodInvocation] Error during extension lookup for '$methodName' on enum value: ${findError.message}. Rethrowing original error.");
              throw RuntimeError(
                  "Enum value '$targetValue' has no method named '$methodName'. Error during extension lookup: ${findError.message}. Original error: (${e.message})");
            }
          } else {
            rethrow; // Rethrow other errors from get()
          }
        }
      } else if (toBridgedInstance(targetValue).$2) {
        final bridgedInstance = toBridgedInstance(targetValue).$1!;
        final bridgedClass = bridgedInstance.bridgedClass;
        switch (methodName) {
          case 'toString':
            return targetValue.toString();
          default:
        }
        // Use directly methods because we need the BridgedMethodCallable
        final adapter = bridgedClass.methods[methodName];

        if (adapter != null) {
          final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
          if (evaluationResult is AsyncSuspensionRequest) {
            return evaluationResult; // Propagate suspension
          }
          final (positionalArgs, namedArgs) =
              evaluationResult as (List<Object?>, Map<String, Object?>);

          try {
            // Call the adapter with the native object
            return adapter(
                this, bridgedInstance.nativeObject, positionalArgs, namedArgs);
          } on ReturnException catch (e) {
            // Native calls shouldn't throw ReturnException directly, but handle defensively
            return e.value;
          } catch (e, s) {
            // Add the stack trace for debugging
            Logger.log("Native Error Stack Trace: $s"); // Print stack trace
            // Catch potential errors from the native code/adapter
            throw RuntimeError(
                "Native error during bridged method call '$methodName' on ${bridgedClass.name}: $e");
          }
        } else {
          // No adapter found for this method name, try extension methods
          Logger.debug(
              "[visitMethodInvocation] Bridged method '$methodName' not found directly for ${bridgedClass.name}. Trying extensions.");
          try {
            final extensionMethod =
                environment.findExtensionMember(targetValue, methodName);
            if (extensionMethod is InterpretedExtensionMethod) {
              Logger.debug(
                  "[visitMethodInvocation] Found extension method '$methodName' for ${bridgedClass.name}. Calling...");
              final evaluationResult =
                  _evaluateArgumentsAsync(node.argumentList);
              if (evaluationResult is AsyncSuspensionRequest) {
                return evaluationResult; // Propagate suspension
              }
              final (positionalArgs, namedArgs) =
                  evaluationResult as (List<Object?>, Map<String, Object?>);

              final extensionArgs = <Object?>[targetValue];
              extensionArgs.addAll(positionalArgs);
              return extensionMethod.call(this, extensionArgs, namedArgs);
            } else {
              throw RuntimeError(
                  "Bridged class '${bridgedClass.name}' has no instance method named '$methodName'.");
            }
          } on RuntimeError catch (findError) {
            throw RuntimeError(
                "Bridged class '${bridgedClass.name}' has no instance method named '$methodName'. Error during extension lookup: ${findError.message}");
          }
        }
        // Note: This block returns directly or throws, it does not set calleeValue.
      }
      // Handle static method call OR NAMED CONSTRUCTOR call
      else if (targetValue is InterpretedClass) {
        // Check for NAMED CONSTRUCTOR first
        final namedConstructor = targetValue.findConstructor(methodName);
        if (namedConstructor != null) {
          // It's a named constructor call
          if (targetValue.isAbstract) {
            throw RuntimeError(
                "Cannot instantiate abstract class '${targetValue.name}'.");
          }

          final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
          if (evaluationResult is AsyncSuspensionRequest) {
            return evaluationResult; // Propagate suspension
          }
          final (positionalArgs, namedArgs) =
              evaluationResult as (List<Object?>, Map<String, Object?>);

          try {
            // 1. Create and initialize instance fields (using the class's public helper)
            // Pass null for type arguments as they aren't applicable to named constructor resolution here
            final instance =
                targetValue.createAndInitializeInstance(this, null);
            // 2. Bind 'this' and call the named constructor logic
            final boundConstructor = namedConstructor.bind(instance);
            boundConstructor.call(
                this, positionalArgs, namedArgs); // Pass evaluated args
            // Constructor call implicitly returns the bound instance.
            return instance; // Return the created and potentially modified instance
          } on ReturnException catch (e) {
            return e.value;
          } on RuntimeError catch (e) {
            throw RuntimeError(
                "Error during named constructor '$methodName' for class '${targetValue.name}': ${e.message}");
          }
        } else {
          // Not a named constructor, check for STATIC METHOD
          final staticMethod = targetValue.findStaticMethod(methodName);
          if (staticMethod != null) {
            calleeValue =
                staticMethod; // It's already the function, no binding needed
          } else {
            throw RuntimeError(
                "Class '${targetValue.name}' has no static method or named constructor named '$methodName'.");
          }
        }
      } else if (targetValue is InterpretedEnum) {
        final staticMethod = targetValue.staticMethods[methodName];
        if (staticMethod != null) {
          calleeValue = staticMethod; // Static method, no binding needed
        } else {
          // Before throwing, let's check if it's a built-in method call like 'values'
          // This could potentially be handled by the stdlib call later, but maybe check here?
          // For now, assume only user-defined static methods are intended.
          throw RuntimeError(
              "Enum '${targetValue.name}' has no static method named '$methodName'.");
        }
      } else if (targetValue is BridgedEnumValue) {
        // This is a method call on a bridged enum value.
        // It must use the invoke() method of BridgedEnumValue.
        final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
        if (evaluationResult is AsyncSuspensionRequest) {
          return evaluationResult; // Propagate suspension
        }
        final (positionalArgs, namedArgs) =
            evaluationResult as (List<Object?>, Map<String, Object?>);
        try {
          return targetValue.invoke(
              this, methodName, positionalArgs, namedArgs);
        } on RuntimeError {
          // Relaunch the RuntimeErrors directly
          rethrow;
        } catch (e, s) {
          // Catch other potential errors (ex: from the adapter)
          Logger.error(
              "[visitMethodInvocation] Native exception during bridged enum method call '$targetValue.$methodName': $e\n$s");
          throw RuntimeError(
              "Native error during bridged enum method call '$methodName' on $targetValue: $e");
        }
      } else if (targetValue is BridgedClass) {
        // This is a method call on a bridged class (bridged constructor or static method)
        final bridgedClass = targetValue;
        final methodName = node.methodName.name;
        Logger.debug(
            "[visitMethodInvocation] Target is BridgedClass: '$methodName' on '${bridgedClass.name}'");

        // 1. Try to find a constructor adapter
        final constructorAdapter =
            bridgedClass.findConstructorAdapter(methodName);

        if (constructorAdapter != null) {
          Logger.debug(
              "[visitMethodInvocation] Found Bridged CONSTRUCTOR adapter for '$methodName'");
          final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
          if (evaluationResult is AsyncSuspensionRequest) {
            return evaluationResult; // Propagate suspension
          }
          final (positionalArgs, namedArgs) =
              evaluationResult as (List<Object?>, Map<String, Object?>);

          try {
            final nativeObject =
                constructorAdapter(this, positionalArgs, namedArgs);

            if (nativeObject == null) {
              throw RuntimeError(
                  "Bridged constructor adapter for '${bridgedClass.name}.$methodName' returned null unexpectedly.");
            }
            final bridgedInstance = BridgedInstance(bridgedClass, nativeObject);
            Logger.debug(
                "[visitMethodInvocation]   Created BridgedInstance wrapping native: ${nativeObject.runtimeType}");
            return bridgedInstance; // Retourner l'instance pontée créée
          } on RuntimeError catch (e) {
            // Relaunch the adapter error
            throw RuntimeError(
                "Error during bridged constructor '$methodName' for class '${bridgedClass.name}': ${e.message}");
          } catch (e, s) {
            // Catch native errors from the adapter/native constructor
            Logger.error(
                "[visitMethodInvocation] Native exception during bridged constructor '${bridgedClass.name}.$methodName': $e\n$s");
            throw RuntimeError(
                "Native error during bridged constructor '$methodName' for class '${bridgedClass.name}': $e");
          }
        } else {
          final staticMethodAdapter =
              bridgedClass.findStaticMethodAdapter(methodName);

          if (staticMethodAdapter != null) {
            Logger.debug(
                "[visitMethodInvocation] Found Bridged STATIC METHOD adapter for '$methodName'");
            final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
            if (evaluationResult is AsyncSuspensionRequest) {
              return evaluationResult; // Propagate suspension
            }
            final (positionalArgs, namedArgs) =
                evaluationResult as (List<Object?>, Map<String, Object?>);

            try {
              final result =
                  staticMethodAdapter(this, positionalArgs, namedArgs);

              return result;
            } on RuntimeError catch (e) {
              throw RuntimeError(
                  "Error during static bridged method call '$methodName' on ${bridgedClass.name}: ${e.message}");
            } catch (e, s) {
              Logger.warn(
                  "[visitMethodInvocation] Native exception during static bridged method call '${bridgedClass.name}.$methodName': $e\n$s");
              throw RuntimeError(
                  "Native error during static bridged method call '$methodName' on ${bridgedClass.name}: $e");
            }
          } else {
            throw RuntimeError(
                "Bridged class '${bridgedClass.name}' has no constructor or static method named '$methodName'.");
          }
        }
      } else if (targetValue is BoundSuper) {
        final instance = targetValue.instance;
        final startClass = targetValue.startLookupClass;
        InterpretedClass? currentClass = startClass;
        InterpretedFunction? superMethod;

        // Look for the method in the superclass hierarchy
        while (currentClass != null) {
          final method = currentClass.findInstanceMethod(methodName);
          if (method != null) {
            superMethod = method;
            break;
          }
          currentClass = currentClass.superclass;
        }

        if (superMethod != null) {
          // Bind the found super method to the original instance ('this')
          calleeValue = superMethod.bind(instance);
        } else {
          throw RuntimeError(
              "Method '$methodName' not found in superclass chain of '${instance.klass.name}'.");
        }
        // Arguments are evaluated below, calleeValue is now the bound super method
      } else if (targetValue is BoundBridgedSuper) {
        final instance = targetValue.instance; // L'instance 'this' interprétée
        final bridgedSuper = targetValue.startLookupClass;
        final nativeSuperObject =
            instance.bridgedSuperObject; // Retrieve the native object

        if (nativeSuperObject == null) {
          throw RuntimeError(
              "Internal error: Cannot call super method '$methodName' on bridged superclass '${bridgedSuper.name}' because the native super object is missing.");
        }

        // Find the method adapter in the bridged class
        final methodAdapter =
            bridgedSuper.findInstanceMethodAdapter(methodName);

        if (methodAdapter != null) {
          // Evaluate the arguments
          final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
          if (evaluationResult is AsyncSuspensionRequest) {
            return evaluationResult; // Propagate suspension
          }
          final (positionalArgs, namedArgs) =
              evaluationResult as (List<Object?>, Map<String, Object?>);

          // Call the adapter with the native object as target
          try {
            return methodAdapter(
                this, nativeSuperObject, positionalArgs, namedArgs);
          } catch (e, s) {
            Logger.error(
                "Native exception during super call to bridged method '${bridgedSuper.name}.$methodName': $e\n$s");
            throw RuntimeError(
                "Native error during super call to bridged method '$methodName': $e");
          }
        } else {
          throw RuntimeError(
              "Method '$methodName' not found in bridged superclass '${bridgedSuper.name}'.");
        }
        // This block returns directly or throws an exception
      }

      //
      else {
        final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
        if (evaluationResult is AsyncSuspensionRequest) {
          return evaluationResult; // Propagate suspension
        }
        final (positionalArgs, namedArgs) =
            evaluationResult as (List<Object?>, Map<String, Object?>);
        List<RuntimeType>? evaluatedTypeArguments;
        final typeArgsNode = node.typeArguments;
        if (typeArgsNode != null) {
          evaluatedTypeArguments = typeArgsNode.arguments
              .map((typeNode) => _resolveTypeAnnotation(typeNode))
              .toList();
        }

        final extensionCallable =
            environment.findExtensionMember(targetValue, methodName);

        if (extensionCallable is InterpretedExtensionMethod) {
          Logger.debug(
              "[MethodInvocation] Found extension method '$methodName'. Calling...");
          // Prepend the target instance to the positional arguments for the extension call
          final extensionPositionalArgs = [targetValue, ...positionalArgs];
          try {
            // Call the extension method
            return extensionCallable.call(this, extensionPositionalArgs,
                namedArgs, evaluatedTypeArguments);
          } on ReturnException catch (e) {
            return e.value;
          } catch (e) {
            throw RuntimeError(
                "Error executing extension method '$methodName': $e");
          }
        } else {
          // No extension method found either, rethrow the original stdlib error
          Logger.debug(
              "[MethodInvocation] Extension method '$methodName' not found. Rethrowing original error.");
          throw RuntimeError(
              "Undefined property or method '$methodName' on ${targetValue.runtimeType}.");
        }
      }
    }

    // Check if the resolved value is callable
    if (calleeValue is Callable) {
      final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
      if (evaluationResult is AsyncSuspensionRequest) {
        return evaluationResult; // Propagate suspension
      }
      final (positionalArgs, namedArgs) =
          evaluationResult as (List<Object?>, Map<String, Object?>);

      // Evaluate Type Arguments for Method Invocation
      List<RuntimeType>? evaluatedTypeArguments;
      final typeArgsNode = node.typeArguments;
      if (typeArgsNode != null) {
        evaluatedTypeArguments = typeArgsNode.arguments
            .map((typeNode) => _resolveTypeAnnotation(typeNode))
            .toList();
        Logger.debug(
            "[MethodInvocation] Evaluated type arguments: $evaluatedTypeArguments");
      }

      // Perform the call
      try {
        // The call logic now works for functions, bound instance methods,
        // static methods, and constructors (which are handled by InterpretedClass.call)
        // Pass the evaluated type arguments
        return calleeValue.call(
            this, positionalArgs, namedArgs, evaluatedTypeArguments);
      } on ReturnException catch (e) {
        return e.value;
      }
      // Catch other potential runtime errors from the call itself
    } else if (calleeValue is BridgedClass && node.target == null) {
      // Call of a bridged default constructor (ex: StringBuffer())
      final bridgedClass = calleeValue;
      final constructorAdapter = bridgedClass
          .findConstructorAdapter(''); // Search for the default constructor ''

      if (constructorAdapter != null) {
        Logger.debug(
            "[visitMethodInvocation] Calling default bridged constructor for '${bridgedClass.name}'");

        final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
        if (evaluationResult is AsyncSuspensionRequest) {
          return evaluationResult; // Propagate suspension
        }
        final (positionalArgs, namedArgs) =
            evaluationResult as (List<Object?>, Map<String, Object?>);

        try {
          final nativeObject =
              constructorAdapter(this, positionalArgs, namedArgs);
          if (nativeObject == null) {
            throw RuntimeError(
                "Default bridged constructor adapter for '${bridgedClass.name}' returned null.");
          }
          final bridgedInstance = BridgedInstance(bridgedClass, nativeObject);
          Logger.debug(
              "[visitMethodInvocation]   Created BridgedInstance wrapping native: ${nativeObject.runtimeType}");
          return bridgedInstance;
        } on RuntimeError catch (e) {
          throw RuntimeError(
              "Error during default bridged constructor for '${bridgedClass.name}': ${e.message}");
        } catch (e, s) {
          Logger.error(
              "[visitMethodInvocation] Native exception during default bridged constructor '${bridgedClass.name}': $e\n$s");
          throw RuntimeError(
              "Native error during default bridged constructor for '${bridgedClass.name}': $e");
        }
      } else {
        // If we have a BridgedClass but no default constructor ''
        throw RuntimeError(
            "'${bridgedClass.name}' is not callable (no default constructor bridge found).");
      }
    } else {
      // Callee is NOT a standard Callable or a BridgedClass constructor
      // Try Extension 'call' Method
      const methodName = 'call';
      try {
        final extensionMethod =
            environment.findExtensionMember(calleeValue, methodName);

        if (extensionMethod is InterpretedExtensionMethod &&
            !extensionMethod
                .isOperator && // Ensure it's a regular method named 'call'
            !extensionMethod.isGetter &&
            !extensionMethod.isSetter) {
          Logger.debug(
              "[MethodInvoke] Found extension method 'call' for non-callable type ${calleeValue?.runtimeType}. Calling...");

          // Need to re-evaluate args here as they weren't necessarily evaluated
          // if calleeValue wasn't Callable earlier.
          final (positionalArgs, namedArgs) =
              _evaluateArguments(node.argumentList);
          List<RuntimeType>? evaluatedTypeArguments;
          final typeArgsNode = node.typeArguments;
          if (typeArgsNode != null) {
            evaluatedTypeArguments = typeArgsNode.arguments
                .map((typeNode) => _resolveTypeAnnotation(typeNode))
                .toList();
          }

          // Prepare arguments for extension method:
          // First arg is the receiver (the object being called)
          final extensionPositionalArgs = [calleeValue, ...positionalArgs];

          try {
            // Call the extension method
            return extensionMethod.call(this, extensionPositionalArgs,
                namedArgs, evaluatedTypeArguments);
          } on ReturnException catch (e) {
            return e.value;
          } catch (e) {
            throw RuntimeError("Error executing extension method 'call': $e");
          }
        }
        Logger.debug(
            "[MethodInvoke] No suitable extension method 'call' found for non-callable type ${calleeValue?.runtimeType}.");
      } on RuntimeError catch (findError) {
        Logger.debug(
            "[MethodInvoke] No extension member 'call' found for non-callable type ${calleeValue?.runtimeType}. Error: ${findError.message}");
        // Fall through to the final standard error below.
      }

      // Original Error: The expression evaluated did not yield a callable function or an object with a callable 'call' extension.
      String nameForError = "<unknown>";
      if (node.target == null) {
        nameForError = node.methodName.name;
      } else {
        nameForError = node.toString(); // Approximate representation
      }
      if (calleeValue != null) {
        return calleeValue;
      }
      throw RuntimeError(
          "'$nameForError' (type: ${calleeValue?.runtimeType}) is not callable and has no 'call' extension method.");
    }
  }

  // Update PropertyAccess to call getters AND handle 'super'
  @override
  Object? visitPropertyAccess(PropertyAccess node) {
    final target = node.target?.accept<Object?>(this);
    if (target is AsyncSuspensionRequest) {
      // Propagate suspension so the state machine resumes this node after resolution
      return target;
    }

    // Determine if this is a conditional access by inspecting the source
    final isNullAware = node.toSource().contains('?.');
    final propertyName = node.propertyName.name;

    // Null safety support: if the target is null and the access is null-aware, return null
    if (target == null) {
      if (isNullAware) {
        return null;
      }
      throw RuntimeError(
          "Cannot access property '$propertyName' on null. Use '?.' for null-aware access.");
    }

    Logger.debug(
        "[PropertyAccess: ${node.toSource()}] Target type: ${target.runtimeType}, Target value: ${target.toString()}");

    if (target is InterpretedInstance) {
      // Standard Instance Access: Try direct first, then extension
      try {
        final member = target.get(propertyName); // .get() handles inheritance
        if (member is InterpretedFunction && member.isGetter) {
          return member
              .call(this, [], {}); // .get already returned bound getter
        } else {
          return member; // field value or bound method
        }
      } on RuntimeError catch (e) {
        // Try Extension Lookup Before Error
        if (e.message.contains("Undefined property '$propertyName'")) {
          Logger.debug(
              "[PropertyAccess] Direct access failed for '$propertyName'. Trying extension lookup on ${target.runtimeType}.");
          try {
            final extensionMember =
                environment.findExtensionMember(target, propertyName);

            if (extensionMember is InterpretedExtensionMethod) {
              if (extensionMember.isGetter) {
                Logger.debug(
                    "[PropertyAccess] Found extension getter '$propertyName'. Calling...");
                // Getters are called with the instance as the first (and only) positional argument
                final extensionPositionalArgs = [target];
                return extensionMember.call(this, extensionPositionalArgs, {});
              } else if (!extensionMember.isOperator &&
                  !extensionMember.isSetter) {
                // Return the extension method itself (it's not bound yet)
                Logger.debug(
                    "[PropertyAccess] Found extension method '$propertyName'. Returning callable.");
                return extensionMember;
              }
            }
            // No suitable extension found, fall through to rethrow original error
            Logger.debug(
                "[PropertyAccess] No suitable extension member found for '$propertyName'.");
          } on RuntimeError catch (findError) {
            // Error during extension lookup itself
            Logger.debug(
                "[PropertyAccess] Error during extension lookup for '$propertyName': ${findError.message}");
            // Fall through to rethrow original error
          }
        }
        // Rethrow original error if it wasn't "Undefined property"or if extension lookup failed
        throw RuntimeError(
            "${e.message} (accessing property via PropertyAccess '$propertyName')");
      }
    } else if (target is InterpretedEnumValue) {
      try {
        // Get should execute the getter or return the field/bound method
        // Pass the visitor to potentially execute getters
        final member = target.get(propertyName, this);

        // Check if the result from get was already the final value (field or executed getter)
        // or if it's a bound method that shouldn't be called here.
        if (member is Callable) {
          // Property access should generally not return a raw callable method.
          // If get() returned a bound method, it means the propertyName matched a method name,
          // which is not what property access typically expects.
          // However, Dart allows accessing methods like properties to get a tear-off.
          // So, we return the bound method (Callable) here.
          Logger.debug(
              "[PropertyAccess] Accessed enum method '$propertyName' on $target as tear-off. Returning bound method.");
          return member;
        } else {
          // Must be a field value or the result of an executed getter.
          Logger.debug(
              "[PropertyAccess] Accessed enum field/getter '$propertyName' on $target. Value: $member");
          return member;
        }
      } on RuntimeError catch (e) {
        // Try Extension Getter if Direct Fails (similar to InterpretedInstance)
        if (e.message.contains("Undefined property '$propertyName'")) {
          Logger.debug(
              "[PropertyAccess] Direct access failed for '$propertyName' on enum $target. Trying extension lookup...");
          try {
            final extensionMember =
                environment.findExtensionMember(target, propertyName);
            if (extensionMember is InterpretedExtensionMethod) {
              if (extensionMember.isGetter) {
                Logger.debug(
                    "[PropertyAccess] Found extension getter '$propertyName' for enum. Calling...");
                final extensionPositionalArgs = [target];
                return extensionMember.call(this, extensionPositionalArgs, {});
              } else if (!extensionMember.isOperator &&
                  !extensionMember.isSetter) {
                Logger.debug(
                    "[PropertyAccess] Found extension method '$propertyName' for enum. Returning tear-off.");
                return extensionMember;
              }
            }
            Logger.debug(
                "[PropertyAccess] No suitable extension member found for '$propertyName' on enum.");
          } on RuntimeError catch (findError) {
            Logger.debug(
                "[PropertyAccess] Error during extension lookup for '$propertyName' on enum: ${findError.message}");
          }
        }
        // Rethrow original error or error from extension lookup
        throw RuntimeError(
            "${e.message} (accessing property via PropertyAccess '$propertyName' on enum value '$target')");
      }
    } else if (target is InterpretedEnum) {
      // Accessing static member on the enum itself
      InterpretedFunction? staticGetter = target.staticGetters[propertyName];
      if (staticGetter != null) {
        // Call the static getter
        return staticGetter.call(this, [], {});
      }
      Object? staticField = target.staticFields[propertyName];
      if (target.staticFields.containsKey(propertyName)) {
        // Return static field value (could be null)
        return staticField;
      }
      InterpretedFunction? staticMethod = target.staticMethods[propertyName];
      if (staticMethod != null) {
        // Return the static method itself (tear-off)
        return staticMethod;
      }

      // Check for built-in 'values'
      if (propertyName == 'values') {
        return target.valuesList;
      }

      // Not found
      throw RuntimeError(
          "Undefined static property '$propertyName' on enum '${target.name}'.");
    } else if (target is InterpretedClass) {
      // Static Access (no change)
      try {
        // Check static fields first (no inheritance for static fields in Dart)
        return target.getStaticField(propertyName);
      } on RuntimeError catch (_) {
        // If not a field, check static methods/getters
        InterpretedFunction? staticMember =
            target.findStaticGetter(propertyName);
        staticMember ??= target.findStaticMethod(propertyName);

        if (staticMember != null) {
          if (staticMember.isGetter) {
            return staticMember.call(this, [], {}); // Call static getter
          } else {
            // Return static method function itself (not bound)
            return staticMember;
          }
        } else {
          throw RuntimeError(
              "Undefined static member '$propertyName' on class '${target.name}'.");
        }
      }
    } else if (target is BoundSuper) {
      // Super Property Access
      final instance = target.instance;
      final startClass = target.startLookupClass;
      InterpretedClass? currentClass =
          startClass; // Start search from superclass

      while (currentClass != null) {
        // Check instance field in the bound instance's fields
        // Use the new public getter on the instance to access the field value
        try {
          final fieldValue = instance.getField(propertyName);
          // Field found on the instance, return its value regardless of where we are in the super hierarchy lookup
          return fieldValue;
        } on RuntimeError {
          // Field doesn't exist directly on the instance, continue searching for getters/methods
        }

        // Check instance getter in the current class in hierarchy
        final getter = currentClass.findInstanceGetter(propertyName);
        if (getter != null) {
          // Bind getter to the original instance and call
          return getter.bind(instance).call(this, [], {});
        }
        // Check instance method (less common for property access, but possible)
        final method = currentClass.findInstanceMethod(propertyName);
        if (method != null) {
          // Bind method to the original instance and return the bound method
          return method.bind(instance);
        }
        // Move up the hierarchy
        currentClass = currentClass.superclass;
      }
      // Not found in superclass hierarchy
      throw RuntimeError(
          "Undefined property '$propertyName' accessed via 'super' on instance of '${instance.klass.name}'.");
    } else if (target is BridgedClass) {
      final bridgedClass = target;
      Logger.debug(
          "[PropertyAccess] Static access on BridgedClass: ${bridgedClass.name}.$propertyName");

      final staticGetter = bridgedClass.findStaticGetterAdapter(propertyName);
      if (staticGetter != null) {
        Logger.debug("[PropertyAccess]   Found static getter adapter.");
        return staticGetter(this); // Call static getter adapter
      }

      final staticMethod = bridgedClass.findStaticMethodAdapter(propertyName);
      if (staticMethod != null) {
        Logger.debug("[PropertyAccess]   Found static method adapter.");
        throw UnimplementedError(
            "Returning bridged static methods as values from PropertyAccess is not yet supported.");
        // return BridgedStaticMethodCallable(bridgedClass, staticMethod, propertyName);
      } else {
        throw RuntimeError(
            "Undefined static member '$propertyName' on bridged class '${bridgedClass.name}'.");
      }
    } else if (toBridgedInstance(target).$2) {
      final bridgedInstance = toBridgedInstance(target).$1!;
      Logger.debug(
          "[PropertyAccess] Access on BridgedInstance: ${bridgedInstance.bridgedClass.name}.$propertyName");
      switch (propertyName) {
        case 'runtimeType':
          return target.runtimeType;
        case 'hashCode':
          return target.hashCode;
        default:
      }
      final getterAdapter =
          bridgedInstance.bridgedClass.findInstanceGetterAdapter(propertyName);
      if (getterAdapter != null) {
        Logger.debug("[PropertyAccess]   Found instance getter adapter.");
        return getterAdapter(
            this, bridgedInstance.nativeObject); // Call instance getter adapter
      }

      final methodAdapter =
          bridgedInstance.bridgedClass.findInstanceMethodAdapter(propertyName);
      if (methodAdapter != null) {
        Logger.debug(
            "[PropertyAccess]   Found instance method adapter. Binding...");
        // Return a callable bound to the instance
        return BridgedMethodCallable(
            bridgedInstance, methodAdapter, propertyName);
      }

      throw RuntimeError(
          "Undefined property or method '$propertyName' on bridged instance of '${bridgedInstance.bridgedClass.name}'.");
    } else if (target is InterpretedRecord) {
      // Accessing field of a record
      final record = target;
      Logger.debug(
          "[PropertyAccess] Access on InterpretedRecord: .$propertyName");
      // Check if it's a positional field access (\$1, \$2, ...)
      if (propertyName.startsWith('\$') && propertyName.length > 1) {
        try {
          final index = int.parse(propertyName.substring(1)) - 1;
          if (index >= 0 && index < record.positionalFields.length) {
            return record.positionalFields[index];
          } else {
            throw RuntimeError(
                "Record positional field index \$$index out of bounds (0..${record.positionalFields.length - 1}).");
          }
        } catch (e) {
          // Handle parse errors or other issues
          throw RuntimeError(
              "Invalid positional record field accessor '$propertyName'.");
        }
      } else {
        // Check if it's a named field access
        if (record.namedFields.containsKey(propertyName)) {
          return record.namedFields[propertyName];
        } else {
          throw RuntimeError(
              "Record has no field named '$propertyName'. Available fields: ${record.namedFields.keys.join(', ')}");
        }
      }
    } else if (target is BridgedEnumValue) {
      return target.get(propertyName);
    } else if (target is BridgedEnum) {
      Logger.debug(
          "[PropertyAccess] Accessing value on BridgedEnum: ${target.name}.$propertyName");
      final enumValue = target.getValue(propertyName);
      if (enumValue != null) {
        return enumValue; // Return the BridgedEnumValue
      } else {
        throw RuntimeError(
            "Undefined enum value '$propertyName' on bridged enum '${target.name}'.");
      }
    } else if (target is BoundBridgedSuper) {
      final instance = target.instance; // The interpreted 'this' instance
      final bridgedSuper = target.startLookupClass;
      final nativeSuperObject =
          instance.bridgedSuperObject; // Retrieve the native object

      if (nativeSuperObject == null) {
        throw RuntimeError(
            "Internal error: Cannot access super property '$propertyName' on bridged superclass '${bridgedSuper.name}' because the native super object is missing.");
      }

      // Try the bridged getter
      final getterAdapter =
          bridgedSuper.findInstanceGetterAdapter(propertyName);
      if (getterAdapter != null) {
        try {
          return getterAdapter(this, nativeSuperObject);
        } catch (e, s) {
          Logger.error(
              "Native exception during super access to bridged getter '${bridgedSuper.name}.$propertyName': $e\n$s");
          throw RuntimeError(
              "Native error during super access to bridged getter '$propertyName': $e");
        }
      }

      // Try the bridged method (for tear-off)
      final methodAdapter =
          bridgedSuper.findInstanceMethodAdapter(propertyName);
      if (methodAdapter != null) {
        // Return a callable bound to the native object
        return BridgedSuperMethodCallable(
            nativeSuperObject, methodAdapter, propertyName, bridgedSuper.name);
      }

      // Not found
      throw RuntimeError(
          "Undefined property or method '$propertyName' accessed via 'super' on bridged superclass '${bridgedSuper.name}'.");
    } else {
      Logger.debug(
          "[PropertyAccess] Looking for extension getter '$propertyName' for target type ${target.runtimeType}.");
      final extensionCallable =
          environment.findExtensionMember(target, propertyName);

      if (extensionCallable is InterpretedExtensionMethod &&
          extensionCallable.isGetter) {
        Logger.debug(
            "[PropertyAccess] Found extension getter '$propertyName'. Calling...");
        // Prepend the target instance to the positional arguments for the extension call
        final extensionPositionalArgs = [
          target
        ]; // Getters take no explicit args
        try {
          // Call the extension getter
          return extensionCallable.call(this, extensionPositionalArgs, {});
        } on ReturnException catch (e) {
          return e.value;
        } catch (e) {
          throw RuntimeError(
              "Error executing extension getter '$propertyName': $e");
        }
      } else {
        // No extension getter found either, rethrow the original stdlib error
        Logger.debug(
            "[PropertyAccess] Extension getter '$propertyName' not found. Rethrowing original error.");
        throw RuntimeError(
            "Undefined property or method '$propertyName' on ${target.runtimeType}.");
      }
    }
  }

  String stringify(Object? value) {
    if (value == null) return 'null';
    if (value is bool) return value.toString();
    return value.toString();
  }

  @override
  Object? visitIfStatement(IfStatement node) {
    Object? conditionValue; // Try to evaluate the condition
    conditionValue = node.expression.accept<Object?>(this);

    // If the evaluation returns an AsyncSuspensionRequest, return it immediately
    if (conditionValue is AsyncSuspensionRequest) {
      Logger.debug(
          "[IfStatement] Condition suspended (AsyncSuspensionRequest). Propagating.");
      return conditionValue;
    }

    // Synchronous logic (if no suspension occurred)
    bool conditionResult;
    final bridgedInstance = toBridgedInstance(conditionValue);
    if (conditionValue is bool) {
      conditionResult = conditionValue;
    } else if (bridgedInstance.$2 && bridgedInstance.$1?.nativeObject is bool) {
      conditionResult = bridgedInstance.$1!.nativeObject as bool;
    } else {
      throw RuntimeError(
          "The condition of an 'if' must be a boolean, but was ${conditionValue?.runtimeType}.");
    }

    if (conditionResult) {
      node.thenStatement.accept<Object?>(this);
    } else if (node.elseStatement != null) {
      node.elseStatement!.accept<Object?>(this);
    }

    return null; // The If instruction normally ends with null
  }

  @override
  Object? visitWhileStatement(WhileStatement node) {
    while (true) {
      // Handle condition being BridgedInstance<bool>
      final conditionValue = node.condition.accept<Object?>(this);
      bool conditionResult;
      final bridgedInstance = toBridgedInstance(conditionValue);
      if (conditionValue is bool) {
        conditionResult = conditionValue;
      } else if (bridgedInstance.$2 &&
          bridgedInstance.$1?.nativeObject is bool) {
        conditionResult = bridgedInstance.$1!.nativeObject as bool;
      } else {
        throw RuntimeError(
            "The condition of a 'while' loop must be a boolean, but was ${conditionValue?.runtimeType}.");
      }

      if (!conditionResult) {
        break;
      }

      try {
        // Condition is true, execute the body
        node.body.accept<Object?>(this);
      } on BreakException catch (e) {
        Logger.debug(
            "[While] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
        if (e.label == null || _currentStatementLabels.contains(e.label)) {
          // Unlabeled break OR labeled break targeting this loop.
          Logger.debug("[While] Breaking loop.");
          break; // Exit the while loop
        } else {
          // Labeled break targeting an outer construct.
          Logger.debug("[While] Rethrowing outer break...");
          rethrow;
        }
      } on ContinueException catch (e) {
        Logger.debug(
            "[While] Caught ContinueException (label: ${e.label}) with current labels: $_currentStatementLabels");
        if (e.label == null || _currentStatementLabels.contains(e.label)) {
          // Unlabeled continue OR labeled continue targeting this loop.
          Logger.debug("[While] Continuing loop.");
          continue; // Skip to the next iteration
        } else {
          // Labeled continue targeting an outer loop.
          Logger.debug("[While] Rethrowing outer continue...");
          rethrow;
        }
      }
      // Other exceptions will propagate up
    }
    return null;
  }

  @override
  Object? visitDoStatement(DoStatement node) {
    do {
      try {
        // Execute the body first
        node.body.accept<Object?>(this);
      } on BreakException catch (e) {
        Logger.debug(
            "[DoWhile] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
        if (e.label == null || _currentStatementLabels.contains(e.label)) {
          Logger.debug("[DoWhile] Breaking loop.");
          break; // Exit the do-while loop
        } else {
          Logger.debug("[DoWhile] Rethrowing outer break...");
          rethrow;
        }
      } on ContinueException catch (e) {
        Logger.debug(
            "[DoWhile] Caught ContinueException (label: ${e.label}) with current labels: $_currentStatementLabels");
        if (e.label == null || _currentStatementLabels.contains(e.label)) {
          Logger.debug("[DoWhile] Continuing loop condition check.");
          // For do-while, continue still needs to check the condition
          // So we fall through to the condition check below
        } else {
          Logger.debug("[DoWhile] Rethrowing outer continue...");
          rethrow;
        }
      }

      // Then evaluate the condition
      // Handle condition being BridgedInstance<bool>
      final conditionValue = node.condition.accept<Object?>(this);
      Logger.debug("[DoWhile] Condition value: $conditionValue");
      bool conditionResult;
      final bridgedInstance = toBridgedInstance(conditionValue);
      Logger.debug("[DoWhile] Bridged instance: $bridgedInstance");
      if (conditionValue is bool) {
        conditionResult = conditionValue;
      } else if (bridgedInstance.$2 &&
          bridgedInstance.$1?.nativeObject is bool) {
        conditionResult = bridgedInstance.$1!.nativeObject as bool;
      } else {
        throw RuntimeError(
            "The condition of a 'do-while' loop must be a boolean, but was ${conditionValue?.runtimeType}.");
      }

      if (!conditionResult) {
        break;
      }
    } while (true);

    return null;
  }

  @override
  Object? visitForStatement(ForStatement node) {
    final loopParts = node.forLoopParts;

    if (loopParts is ForPartsWithDeclarations) {
      // Classic for loop: for (var i = 0; ... ; ...)
      _executeClassicFor(loopParts.variables, loopParts.condition,
          loopParts.updaters, node.body);
    } else if (loopParts is ForPartsWithExpression) {
      // Classic for loop: for (i = 0; ... ; ...)
      _executeClassicFor(loopParts.initialization, loopParts.condition,
          loopParts.updaters, node.body);
    } else if (loopParts is ForEachPartsWithDeclaration) {
      // For-in loop: for (var item in list) or await for (var item in stream)
      if (node.awaitKeyword != null) {
        // await for loop - expect Stream
        return _executeAwaitForIn(
            loopParts.loopVariable, loopParts.iterable, node.body);
      } else {
        // Regular for-in loop - expect Iterable
        _executeForIn(loopParts.loopVariable, loopParts.iterable, node.body);
      }
    } else if (loopParts is ForEachPartsWithIdentifier) {
      // For-in loop: for (item in list) or await for (item in stream)
      if (node.awaitKeyword != null) {
        // await for loop - expect Stream
        return _executeAwaitForIn(
            loopParts.identifier, loopParts.iterable, node.body);
      } else {
        // Regular for-in loop - expect Iterable
        _executeForIn(loopParts.identifier, loopParts.iterable, node.body);
      }
    } else {
      // Should not happen with valid Dart code
      throw StateError('Unknown ForLoopParts type: ${loopParts.runtimeType}');
    }

    return null; // For loops don't produce a value
  }

  // Helper method to execute the logic of a classic for loop
  void _executeClassicFor(AstNode? initialization, Expression? condition,
      List<Expression>? updaters, Statement body) {
    // 1. Create loop environment
    final loopEnvironment = Environment(enclosing: environment);
    final previousEnvironment = environment;
    environment = loopEnvironment;

    try {
      // 2. Execute initialization (if it exists)
      if (initialization != null) {
        // If initialization is a VariableDeclarationList, visit it directly.
        // If it's an Expression (like in ForPartsWithExpression), visit it.
        initialization.accept<Object?>(this);
      }

      // 3. Loop execution
      while (true) {
        // 3.a Evaluate condition
        bool conditionResult = true; // Default to true if no condition
        if (condition != null) {
          final evalResult = condition.accept<Object?>(this);
          final bridgedInstance = toBridgedInstance(evalResult);
          if (evalResult is bool) {
            conditionResult = evalResult;
          } else if (bridgedInstance.$2 &&
              bridgedInstance.$1?.nativeObject is bool) {
            conditionResult = bridgedInstance.$1!.nativeObject as bool;
          } else {
            throw RuntimeError(
                "The condition of a 'for' loop must be a boolean, but was ${evalResult?.runtimeType}.");
          }
        }

        if (!conditionResult) {
          break;
        }

        // 3.b Execute body
        try {
          body.accept<Object?>(this);
        } on BreakException catch (e) {
          Logger.debug(
              "[For] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
          if (e.label == null || _currentStatementLabels.contains(e.label)) {
            Logger.debug("[For] Breaking loop.");
            break; // Exit the for loop entirely
          } else {
            Logger.debug("[For] Rethrowing outer break...");
            rethrow;
          }
        } on ContinueException catch (e) {
          Logger.debug(
              "[For] Caught ContinueException (label: ${e.label}) with current labels: $_currentStatementLabels");
          if (e.label == null || _currentStatementLabels.contains(e.label)) {
            Logger.debug("[For] Continuing to updaters.");
            // Skip directly to updaters for this iteration
            // The `continue` below will handle jumping to the next iteration
          } else {
            Logger.debug("[For] Rethrowing outer continue...");
            rethrow;
          }
        }

        // 3.c Execute updaters (if they exist)
        try {
          if (updaters != null) {
            for (final updater in updaters) {
              updater.accept<Object?>(this);
            }
          }
        } on BreakException {
          // This should technically not happen if break is only thrown from the body,
          // but handle defensively.
          break;
        }
        // Continue exception in updaters should just proceed to the next iteration check

        // `continue` effectively happens here by looping back
      }
    } finally {
      // 4. Restore previous environment
      environment = previousEnvironment;
    }
  }

  // Helper method to execute the logic of a for-in loop (simplified)
  void _executeForIn(AstNode loopVariableOrIdentifier,
      Expression iterableExpression, Statement body) {
    final expressionValue = iterableExpression.accept<Object?>(this);

    // Handle iterable being BridgedInstance<Iterable>
    Object? iterableValue;
    if (toBridgedInstance(expressionValue).$2) {
      final bridgedInstance = toBridgedInstance(expressionValue).$1!;
      if (bridgedInstance.nativeObject is Iterable) {
        iterableValue = bridgedInstance.nativeObject;
      } else {
        throw RuntimeError(
            'Value used in for-in loop must be an Iterable, but got BridgedInstance containing ${bridgedInstance.nativeObject.runtimeType}');
      }
    } else if (expressionValue is Iterable) {
      iterableValue = expressionValue;
    } else {
      throw RuntimeError(
          'Value used in for-in loop must be an Iterable, but got ${expressionValue?.runtimeType}');
    }

    if (iterableValue is Iterable) {
      // Check the unwrapped iterableValue
      final loopEnvironment = Environment(enclosing: environment);
      final previousEnvironment = environment;
      environment = loopEnvironment;

      try {
        String variableName;
        if (loopVariableOrIdentifier is DeclaredIdentifier) {
          variableName = loopVariableOrIdentifier.name.lexeme;
          environment.define(variableName, null); // Define before loop
        } else if (loopVariableOrIdentifier is SimpleIdentifier) {
          variableName = loopVariableOrIdentifier.name;
          try {
            environment.get(variableName); // Check existence
          } catch (e) {
            throw RuntimeError(
                "Variable '$variableName' for for-in loop is not defined.");
          }
        } else {
          throw StateError(
              'Unexpected for-in loop variable type: ${loopVariableOrIdentifier.runtimeType}');
        }

        // Iterate over the native list
        for (final element in iterableValue) {
          // Assign current element to the loop variable
          environment.assign(variableName, element);

          // Execute the body
          try {
            body.accept<Object?>(this);
          } on BreakException catch (e) {
            Logger.debug(
                "[ForIn] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
            if (e.label == null || _currentStatementLabels.contains(e.label)) {
              Logger.debug("[ForIn] Breaking loop.");
              break; // Exit the for-in loop
            } else {
              Logger.debug("[ForIn] Rethrowing outer break...");
              rethrow;
            }
          } on ContinueException catch (e) {
            Logger.debug(
                "[ForIn] Caught ContinueException (label: ${e.label}) with current labels: $_currentStatementLabels");
            if (e.label == null || _currentStatementLabels.contains(e.label)) {
              Logger.debug("[ForIn] Continuing loop.");
              continue; // Go to the next element
            } else {
              Logger.debug("[ForIn] Rethrowing outer continue...");
              rethrow;
            }
          }
        }
      } finally {
        // Restore previous environment
        environment = previousEnvironment;
      }
    } else {
      // Should not happen after the check above
      throw StateError(
          'Internal error: Expected Iterable but got ${iterableValue.runtimeType}');
    }
  }

  // Helper method to execute the logic of an await for-in loop (for streams)
  Object? _executeAwaitForIn(AstNode loopVariableOrIdentifier,
      Expression iterableExpression, Statement body) {
    final expressionValue = iterableExpression.accept<Object?>(this);

    // Handle stream being BridgedInstance<Stream>
    Object? streamValue;
    if (toBridgedInstance(expressionValue).$2) {
      final bridgedInstance = toBridgedInstance(expressionValue).$1!;
      if (bridgedInstance.nativeObject is Stream) {
        streamValue = bridgedInstance.nativeObject;
      } else {
        throw RuntimeError(
            'Value used in await for-in loop must be a Stream, but got BridgedInstance containing ${bridgedInstance.nativeObject.runtimeType}');
      }
    } else if (expressionValue is Stream) {
      streamValue = expressionValue;
    } else {
      throw RuntimeError(
          'Value used in await for-in loop must be a Stream, but got ${expressionValue?.runtimeType}');
    }

    if (streamValue is Stream) {
      // Create a suspension request for converting the stream to a list first
      if (currentAsyncState == null) {
        throw RuntimeError(
            "await for statement can only be used inside async functions");
      }

      // Convert the stream to a list first, then process it as a regular for-in loop
      return AsyncSuspensionRequest(
        _convertStreamAndProcessForIn(
            loopVariableOrIdentifier, streamValue, body),
        currentAsyncState!,
      );
    } else {
      // Should not happen after the check above
      throw StateError(
          'Internal error: Expected Stream but got ${streamValue.runtimeType}');
    }
  }

  // Convert stream to list and then process as regular for-in loop
  Future<Object?> _convertStreamAndProcessForIn(
      AstNode loopVariableOrIdentifier,
      Stream<Object?> stream,
      Statement body) async {
    // Convert stream to list
    final List<Object?> items = await stream.toList();

    // Now process as a regular for-in loop with the list
    _executeForInWithItems(loopVariableOrIdentifier, items, body);

    return null; // await for loops don't produce a value
  }

  // Execute for-in loop with a list of items (reused logic from _executeForIn)
  void _executeForInWithItems(
      AstNode loopVariableOrIdentifier, List<Object?> items, Statement body) {
    // Don't create a new environment - use the current one to preserve access to local variables
    String variableName;
    if (loopVariableOrIdentifier is DeclaredIdentifier) {
      variableName = loopVariableOrIdentifier.name.lexeme;
      // Define the loop variable in the current environment
      environment.define(variableName, null);
    } else if (loopVariableOrIdentifier is SimpleIdentifier) {
      variableName = loopVariableOrIdentifier.name;
      try {
        environment.get(variableName); // Check existence
      } catch (e) {
        throw RuntimeError(
            "Variable '$variableName' for for-in loop is not defined.");
      }
    } else {
      throw StateError(
          'Unexpected for-in loop variable type: ${loopVariableOrIdentifier.runtimeType}');
    }

    // Iterate over the items
    for (final element in items) {
      // Assign current element to the loop variable
      environment.assign(variableName, element);

      // Execute the body
      try {
        body.accept<Object?>(this);
      } on BreakException catch (e) {
        Logger.debug(
            "[AwaitForIn] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
        if (e.label == null || _currentStatementLabels.contains(e.label)) {
          Logger.debug("[AwaitForIn] Breaking loop.");
          break; // Exit the for-in loop
        } else {
          Logger.debug("[AwaitForIn] Rethrowing outer break...");
          rethrow;
        }
      } on ContinueException catch (e) {
        Logger.debug(
            "[AwaitForIn] Caught ContinueException (label: ${e.label}) with current labels: $_currentStatementLabels");
        if (e.label == null || _currentStatementLabels.contains(e.label)) {
          Logger.debug("[AwaitForIn] Continuing loop.");
          continue; // Go to the next element
        } else {
          Logger.debug("[AwaitForIn] Rethrowing outer continue...");
          rethrow;
        }
      }
    }
  }

  // Add handler for VariableDeclarationList used in ForPartsWithDeclarations
  @override
  Object? visitVariableDeclarationList(VariableDeclarationList node) {
    // We need to ensure variables are defined in the current environment.
    // visitVariableDeclaration is NOT automatically called for node.variables
    // by the generalizing visitor when visiting the list itself.
    for (final variable in node.variables) {
      if (variable.name.lexeme == '_') {
        // Evaluate initializer for potential side effects, but don't define
        variable.initializer?.accept<Object?>(this);
      } else {
        // Explicitly evaluate the initializer if present
        // Object? value;
        // if (variable.initializer != null) {
        //   value = variable.initializer!.accept<Object?>(this);
        // }
        // // Explicitly define the variable in the current environment
        // environment.define(variable.name.lexeme, value);

        Object? initValue;
        Object? result; // Value returned by accept() (could be suspension)

        if (variable.initializer != null) {
          result = variable.initializer!.accept<Object?>(this);
          if (result is AsyncSuspensionRequest) {
            // Async initializer: Define as null for now, result holds suspension
            Logger.debug(
                "[VariableDeclList] Async init for '${variable.name.lexeme}'. Defined as null.");
            environment.define(variable.name.lexeme, null);
            // Propagate the suspension request.
            // If there are multiple async inits, the LAST suspension request wins.
          } else {
            // Sync initializer: Use the computed value
            initValue = result;
            Logger.debug(
                "[VariableDeclList] Sync init for '${variable.name.lexeme}'. Defined as $initValue.");
            environment.define(variable.name.lexeme, initValue);
          }
        } else {
          // No initializer: Define as null
          Logger.debug(
              "[VariableDeclList] No init for '${variable.name.lexeme}'. Defined as null.");
          environment.define(variable.name.lexeme, null);
          result = null; // No suspension
        }

        // If the result of the variable's init was a suspension, we need to return it
        // to signal the state machine.
        if (result is AsyncSuspensionRequest) {
          // If any variable initialization caused suspension, return that suspension immediately.
          // The state machine needs to handle this before processing subsequent variables.
          Logger.debug(
              "[VariableDeclList] Propagating suspension from initializer of '${variable.name.lexeme}'.");
          return result;
        }
      }
    }
    // If no suspension occurred (or only sync initializers for all variables)
    return null; // Original behavior
  }

  @override
  Object? visitBreakStatement(BreakStatement node) {
    final label = node.label?.name;
    Logger.debug("[BreakStatement] Throwing BreakException (label: $label)");
    throw BreakException(label);
  }

  @override
  Object? visitContinueStatement(ContinueStatement node) {
    final label = node.label?.name;
    Logger.debug(
        "[ContinueStatement] Throwing ContinueException (label: $label)");
    throw ContinueException(label);
  }

  @override
  Object? visitYieldStatement(YieldStatement node) {
    final value = node.expression.accept<Object?>(this);
    Logger.debug(
        "[YieldStatement] Yielding value: $value (star: ${node.star != null})");
    return YieldValue(value, isYieldStar: node.star != null);
  }

  @override
  Object? visitListLiteral(ListLiteral node) {
    final List<Object?> list = [];
    for (final element in node.elements) {
      _processCollectionElement(element, list, isMap: false);
    }
    return list;
  }

  @override
  Object? visitParenthesizedExpression(ParenthesizedExpression node) {
    return node.expression.accept<Object?>(this);
  }

  @override
  Object? visitCascadeExpression(CascadeExpression node) {
    // 1. Evaluate the target expression ONCE.
    final targetValue = node.target.accept<Object?>(this);

    // 2. Execute each cascade section ON THE ORIGINAL targetValue.
    for (final section in node.cascadeSections) {
      // We need to manually handle each section type, forcing the target.
      if (section is MethodInvocation) {
        _executeCascadeMethodInvocation(targetValue, section);
      } else if (section is PropertyAccess) {
        // Evaluate property access for potential side effects (getters), but discard result.
        _executeCascadePropertyAccess(targetValue, section);
      } else if (section is AssignmentExpression) {
        _executeCascadeAssignment(targetValue, section);
      } else if (section is IndexExpression) {
        // Evaluate index expression for potential side effects (getters?), but discard result.
        _executeCascadeIndexAccess(targetValue, section);
      } else {
        // Should not happen with valid cascade sections
        throw UnimplementedError(
            'Cascade section type not handled: ${section.runtimeType}');
      }
    }

    // 3. The cascade expression evaluates to the original target value.
    return targetValue;
  }

  void _executeCascadeMethodInvocation(
      Object? targetValue, MethodInvocation node) {
    if (targetValue == null) {
      // Dart's .. operator throws on null, ?.. does nothing.
      // Since we can't easily distinguish here, we mimic ?.. and do nothing.
      Logger.debug(
          "[Cascade] Target is null, skipping method invocation section.");
      return;
    }

    final methodName = node.methodName.name;
    final (positionalArgs, namedArgs) = _evaluateArguments(node.argumentList);
    List<RuntimeType>? evaluatedTypeArguments;
    final typeArgsNode = node.typeArguments;
    if (typeArgsNode != null) {
      evaluatedTypeArguments = typeArgsNode.arguments
          .map((typeNode) => _resolveTypeAnnotation(typeNode))
          .toList();
    }

    // Resolve and call method ON targetValue
    if (targetValue is InterpretedInstance) {
      final callee = targetValue.get(methodName); // Gets bound method
      if (callee is Callable) {
        callee.call(this, positionalArgs, namedArgs, evaluatedTypeArguments);
      } else {
        throw RuntimeError(
            "Member '$methodName' on interpreted instance is not callable in cascade.");
      }
    } else if (toBridgedInstance(targetValue).$2) {
      final bridgedInstance = toBridgedInstance(targetValue).$1!;
      final adapter =
          bridgedInstance.bridgedClass.findInstanceMethodAdapter(methodName);
      if (adapter != null) {
        adapter(this, bridgedInstance.nativeObject, positionalArgs, namedArgs);
      } else {
        throw RuntimeError(
            "Bridged instance method '$methodName' not found in cascade.");
      }
    }
    // Ignore the return value of the method call in a cascade
  }

  Object? _executeCascadePropertyAccess(
      Object? targetValue, PropertyAccess node) {
    if (targetValue == null) {
      Logger.debug(
          "[Cascade] Target is null, skipping property access section.");
      return null;
    }

    final propertyName = node.propertyName.name;
    // Resolve property/getter ON targetValue
    if (targetValue is InterpretedInstance) {
      final member = targetValue.get(propertyName);
      if (member is InterpretedFunction && member.isGetter) {
        return member.call(this, [],
            {}); // Call getter, return its value (needed for assignment LHS)
      } else {
        return member; // Return field value (needed for assignment LHS)
      }
    } else if (toBridgedInstance(targetValue).$2) {
      final bridgedInstance = toBridgedInstance(targetValue).$1!;
      final getter =
          bridgedInstance.bridgedClass.findInstanceGetterAdapter(propertyName);
      if (getter != null) {
        return getter(this, bridgedInstance.nativeObject);
      }
      // If no getter, maybe it's a method to be used in assignment? Unlikely.
      throw RuntimeError(
          "Bridged instance property '$propertyName' (getter) not found in cascade.");
    } else {
      throw RuntimeError(
          "property '$propertyName' (getter) not found in cascade.");
    }
  }

  Object? _executeCascadeIndexAccess(
      Object? targetValue, IndexExpression node) {
    if (targetValue == null) {
      Logger.debug("[Cascade] Target is null, skipping index access section.");
      return null;
    }

    final indexValue = node.index.accept<Object?>(this);
    // Perform index access ON targetValue
    if (targetValue is List) {
      if (indexValue is int) {
        if (indexValue < 0 || indexValue >= targetValue.length) {
          throw RuntimeError('Index out of range in cascade: $indexValue');
        }
        return targetValue[indexValue];
      } else {
        throw RuntimeError('List index must be an integer in cascade.');
      }
    } else if (targetValue is Map) {
      return targetValue[indexValue];
    } else if (targetValue is String && indexValue is int) {
      return targetValue[indexValue];
    } else {
      throw RuntimeError(
          'Unsupported target for index access in cascade: ${targetValue.runtimeType}');
    }
    // Return the accessed value (needed for assignment LHS)
  }

  void _executeCascadeAssignment(
      Object? targetValue, AssignmentExpression node) {
    if (targetValue == null) {
      Logger.debug("[Cascade] Target is null, skipping assignment section.");
      return;
    }

    final rhsValue = node.rightHandSide.accept<Object?>(this);
    final operatorType = node.operator.type;
    final lhs = node.leftHandSide;

    if (lhs is SimpleIdentifier) {
      // Property assignment: targetValue.propertyName op= rhsValue
      final propertyName = lhs.name;
      Object? newValue;
      if (operatorType == TokenType.EQ) {
        newValue = rhsValue;
      } else {
        // Compound assignment
        Object? currentValue;
        // Need to get the current value from the target
        if (targetValue is InterpretedInstance) {
          currentValue = targetValue.get(propertyName);
        } else if (toBridgedInstance(targetValue).$2) {
          final bridgedInstance = toBridgedInstance(targetValue).$1!;
          final getter = bridgedInstance.bridgedClass
              .findInstanceGetterAdapter(propertyName);
          if (getter == null) {
            throw RuntimeError(
                "No getter '$propertyName' for compound assignment in cascade.");
          }
          currentValue = getter(this, bridgedInstance.nativeObject);
        } else {
          throw RuntimeError(
              "Cannot get property '$propertyName' for compound assignment on ${targetValue.runtimeType} in cascade.");
        }
        newValue = computeCompoundValue(currentValue, rhsValue, operatorType);
      }

      // Set the value on the target
      if (targetValue is InterpretedInstance) {
        final setter = targetValue.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(targetValue).call(this, [newValue], {});
        } else {
          targetValue.set(propertyName, newValue, this); // Direct field set
        }
      } else if (toBridgedInstance(targetValue).$2) {
        final bridgedInstance = toBridgedInstance(targetValue).$1!;
        final setter = bridgedInstance.bridgedClass
            .findInstanceSetterAdapter(propertyName);
        if (setter == null) {
          throw RuntimeError(
              "No setter '$propertyName' for assignment in cascade.");
        }
        setter(this, bridgedInstance.nativeObject, newValue);
      } else {
        throw RuntimeError(
            "Cannot set property '$propertyName' on ${targetValue.runtimeType} in cascade.");
      }
    } else if (lhs is IndexExpression) {
      // Index assignment: targetValue[index] op= rhsValue
      final indexValue = lhs.index.accept<Object?>(this);
      Object? newValue;

      if (operatorType == TokenType.EQ) {
        newValue = rhsValue;
      } else {
        // Compound assignment
        Object? currentValue;
        if (targetValue is List) {
          if (indexValue is! int) throw RuntimeError('List index must be int.');
          if (indexValue < 0 || indexValue >= targetValue.length) {
            throw RuntimeError('Index out of range.');
          }
          currentValue = targetValue[indexValue];
        } else if (targetValue is Map) {
          currentValue = targetValue[indexValue];
        } else {
          throw RuntimeError(
              "Compound index assignment target must be List or Map in cascade.");
        }
        newValue = computeCompoundValue(currentValue, rhsValue, operatorType);
      }

      // Set the value
      if (targetValue is List) {
        if (indexValue is! int) throw RuntimeError('List index must be int.');
        if (indexValue < 0 || indexValue >= targetValue.length) {
          throw RuntimeError('Index out of range.');
        }
        targetValue[indexValue] = newValue;
      } else if (targetValue is Map) {
        targetValue[indexValue] = newValue;
      } else {
        // Should have been caught earlier
        throw RuntimeError(
            "Index assignment target must be List or Map in cascade.");
      }
    } else if (lhs is PropertyAccess) {
      // Cascade assignment like: target..property = value or target..property += value
      // Note: targetValue is the original cascade target, NOT lhs.target
      final propertyName = lhs.propertyName.name;
      Object? newValue;

      if (operatorType == TokenType.EQ) {
        newValue = rhsValue; // Simple assignment
      } else {
        // Compound assignment
        Object? currentValue;
        // 1. Get current value from targetValue using propertyName
        if (targetValue is InterpretedInstance) {
          currentValue = targetValue.get(propertyName); // Handles field/getter
        } else if (toBridgedInstance(targetValue).$2) {
          final bridgedInstance = toBridgedInstance(targetValue).$1!;
          final getter = bridgedInstance.bridgedClass
              .findInstanceGetterAdapter(propertyName);
          if (getter == null) {
            throw RuntimeError(
                "No getter '$propertyName' for compound assignment in cascade.");
          }
          currentValue = getter(this, bridgedInstance.nativeObject);
        } else {
          throw RuntimeError(
              "Cannot get property '$propertyName' for compound assignment on ${targetValue.runtimeType} in cascade.");
        }
        // 2. Compute new value
        newValue = computeCompoundValue(currentValue, rhsValue, operatorType);
      }

      // 3. Set the new value on targetValue using propertyName
      if (targetValue is InterpretedInstance) {
        final setter = targetValue.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(targetValue).call(this, [newValue], {});
        } else {
          // Direct field assignment if no setter
          targetValue.set(propertyName, newValue, this);
        }
      } else if (toBridgedInstance(targetValue).$2) {
        final bridgedInstance = toBridgedInstance(targetValue).$1!;
        final setter = bridgedInstance.bridgedClass
            .findInstanceSetterAdapter(propertyName);
        if (setter == null) {
          throw RuntimeError(
              "No setter '$propertyName' for assignment in cascade.");
        }
        setter(this, bridgedInstance.nativeObject, newValue);
      } else {
        throw RuntimeError(
            "Cannot set property '$propertyName' on ${targetValue.runtimeType} in cascade.");
      }
    } else {
      throw UnimplementedError(
          'Unsupported assignment LHS in cascade: ${lhs.runtimeType}');
    }
    // Assignment in cascade doesn't produce a value to be used further.
  }

  void _processCollectionElement(CollectionElement element, Object collection,
      {required bool isMap}) {
    if (element is Expression) {
      final value = element.accept<Object?>(this);
      if (isMap) {
        throw RuntimeError(
            "Expected a MapLiteralEntry ('key: value') but got an expression in map literal.");
      } else if (collection is List) {
        collection.add(value);
      } else if (collection is Set) {
        collection.add(value);
      }
    } else if (element is MapLiteralEntry) {
      if (!isMap) {
        throw RuntimeError(
            "Unexpected MapLiteralEntry ('key: value') in a non-map literal.");
      }
      if (collection is Map) {
        final key = element.key.accept<Object?>(this);
        final value = element.value.accept<Object?>(this);
        collection[key] = value;
      } else {
        // Should not happen if isMap is true
        throw StateError("Internal error: Expected Map for map literal.");
      }
    } else if (element is SpreadElement) {
      final expressionValue = element.expression.accept<Object?>(this);
      if (element.isNullAware && expressionValue == null) {
        // Null-aware spread with null value, do nothing
        return;
      }
      if (isMap) {
        Map? mapToAdd;
        final bridgedInstance = toBridgedInstance(expressionValue);
        if (expressionValue is Map) {
          mapToAdd = expressionValue;
        } else if (bridgedInstance.$2 &&
            bridgedInstance.$1?.nativeObject is Map) {
          mapToAdd = bridgedInstance.$1!.nativeObject as Map;
        } else {
          throw RuntimeError(
              'Spread element in a Map literal requires a Map, but got ${expressionValue?.runtimeType}');
        }
        (collection as Map).addAll(mapToAdd);
      } else {
        // List or Set Spread Logic...
        Object? iterableToAdd;
        if (toBridgedInstance(expressionValue).$2) {
          final bridgedInstance = toBridgedInstance(expressionValue).$1!;
          if (bridgedInstance.nativeObject is Iterable) {
            iterableToAdd = bridgedInstance.nativeObject;
          } else {
            // BridgedInstance does not contain an Iterable
            throw RuntimeError(
                'Spread element in a ${collection is List ? 'List' : 'Set'} literal requires an Iterable, but got BridgedInstance containing ${bridgedInstance.nativeObject.runtimeType}');
          }
        } else if (expressionValue is Iterable) {
          // Original check: If not BridgedInstance, check if it's directly Iterable
          iterableToAdd = expressionValue;
        } else {
          // Neither BridgedInstance with Iterable nor direct Iterable
          throw RuntimeError(
              'Spread element in a ${collection is List ? 'List' : 'Set'} literal requires an Iterable, but got ${expressionValue?.runtimeType}');
        }

        // Now use iterableToAdd which is guaranteed to be Iterable
        // Should always be non-null if no error thrown
        if (collection is List) {
          collection.addAll(iterableToAdd as Iterable); // Cast is safe here
        } else if (collection is Set) {
          collection.addAll(iterableToAdd as Iterable); // Cast is safe here
        } else {
          throw StateError(
              "Internal error: Expected List or Set for non-map literal.");
        }
        // else case handled by error throws above
      }
    } else if (element is IfElement) {
      final conditionValue = element.expression.accept<Object?>(this);
      bool conditionResult;
      final bridgedInstance = toBridgedInstance(conditionValue);
      if (conditionValue is bool) {
        conditionResult = conditionValue;
      } else if (bridgedInstance.$2 &&
          bridgedInstance.$1?.nativeObject is bool) {
        conditionResult = bridgedInstance.$1!.nativeObject as bool;
      } else {
        throw RuntimeError(
            'Condition in collection \'if\' must be a boolean, but got ${conditionValue?.runtimeType}');
      }

      if (conditionResult) {
        _processCollectionElement(element.thenElement, collection,
            isMap: isMap);
      } else if (element.elseElement != null) {
        _processCollectionElement(element.elseElement!, collection,
            isMap: isMap);
      }
    } else if (element is ForElement) {
      final loopParts = element.forLoopParts;
      if (loopParts is ForEachPartsWithDeclaration ||
          loopParts is ForEachPartsWithIdentifier) {
        final iterableExpression = loopParts is ForEachPartsWithDeclaration
            ? loopParts.iterable
            : (loopParts as ForEachPartsWithIdentifier).iterable;
        final loopVariableNode = loopParts is ForEachPartsWithDeclaration
            ? loopParts.loopVariable
            : (loopParts as ForEachPartsWithIdentifier).identifier;

        final iterableValue = iterableExpression.accept<Object?>(this);

        if (iterableValue is Iterable) {
          final loopEnvironment = Environment(enclosing: environment);
          final previousEnvironment = environment;
          environment = loopEnvironment;

          try {
            String variableName;
            if (loopVariableNode is DeclaredIdentifier) {
              variableName = loopVariableNode.name.lexeme;
              environment.define(variableName, null); // Define before loop
            } else if (loopVariableNode is SimpleIdentifier) {
              variableName = loopVariableNode.name;
            } else {
              throw StateError(
                  'Unexpected for-in loop variable type: ${loopVariableNode.runtimeType}');
            }

            for (final item in iterableValue) {
              environment.assign(variableName, item);
              _processCollectionElement(element.body, collection, isMap: isMap);
            }
          } finally {
            environment = previousEnvironment;
          }
        } else {
          throw RuntimeError(
              'Value used in collection \'for-in\' must be an Iterable, but got ${iterableValue?.runtimeType}');
        }
      } else if (loopParts is ForPartsWithDeclarations ||
          loopParts is ForPartsWithExpression) {
        AstNode? initialization;
        Expression? condition;
        List<Expression>? updaters;
        if (loopParts is ForPartsWithDeclarations) {
          initialization = loopParts.variables;
          condition = loopParts.condition;
          updaters = loopParts.updaters;
        } else if (loopParts is ForPartsWithExpression) {
          initialization = loopParts.initialization;
          condition = loopParts.condition;
          updaters = loopParts.updaters;
        }
        final loopEnvironment = Environment(enclosing: environment);
        final previousEnvironment = environment;
        environment = loopEnvironment;
        try {
          // Initialisation
          if (initialization != null) {
            initialization.accept<Object?>(this);
          }
          // Boucle
          while (true) {
            bool conditionResult = true;
            if (condition != null) {
              final evalResult = condition.accept<Object?>(this);
              final bridgedInstance = toBridgedInstance(evalResult);
              if (evalResult is bool) {
                conditionResult = evalResult;
              } else if (bridgedInstance.$2 &&
                  bridgedInstance.$1?.nativeObject is bool) {
                conditionResult = bridgedInstance.$1!.nativeObject as bool;
              } else {
                throw RuntimeError(
                    "The condition of a 'for' loop must be a boolean, but was ${evalResult?.runtimeType}.");
              }
            }
            if (!conditionResult) break;
            _processCollectionElement(element.body, collection, isMap: isMap);
            if (updaters != null) {
              for (final updater in updaters) {
                updater.accept<Object?>(this);
              }
            }
          }
        } finally {
          environment = previousEnvironment;
        }
      } else {
        throw UnimplementedError(
            'Unsupported for-loop type in collection literal: ${loopParts.runtimeType}');
      }
    } else if (element is NullAwareElement) {
      // Use element.expression as per analyzer AST definition
      final value = element.value.accept<Object?>(this);
      if (value != null) {
        if (collection is List) {
          collection.add(value);
        } else if (collection is Set) {
          collection.add(value);
        } else {
          // Should not happen if isMap is false
          throw StateError(
              "Internal error: Expected List or Set for NullAwareElement.");
        }
      }
      // If value is null, do nothing.
    } else {
      throw UnimplementedError(
          'Collection element type not yet supported: ${element.runtimeType}');
    }
  }

  @override
  Object? visitFunctionDeclaration(FunctionDeclaration node) {
    // Create a function object that captures the current environment (closure)
    // Use the .declaration constructor
    final returnType = node.returnType;
    bool isAsync = node.functionExpression.body.isAsynchronous;
    bool isNullable = false;
    if (returnType is NamedType) {
      isNullable = returnType.toSource().contains('?');
    }

    // Handle type parameters for generic functions
    Environment? tempEnvironment;
    final typeParameters = node.functionExpression.typeParameters;

    if (typeParameters != null) {
      Logger.debug(
          "[InterpreterVisitor.visitFunctionDeclaration] Function '${node.name.lexeme}' has ${typeParameters.typeParameters.length} type parameters");

      // Create a temporary environment for type resolution
      tempEnvironment = Environment(enclosing: environment);

      // Create temporary type parameter placeholders
      for (final typeParam in typeParameters.typeParameters) {
        final paramName = typeParam.name.lexeme;

        // Create a simple TypeParameter placeholder in the temp environment
        final typeParamPlaceholder = TypeParameter(paramName);
        tempEnvironment.define(paramName, typeParamPlaceholder);

        Logger.debug(
            "[InterpreterVisitor.visitFunctionDeclaration]   Defined type parameter '$paramName' in temp environment");
      }
    }

    // Use the temp environment (if any) for type resolution, otherwise use the normal environment
    final resolveEnvironment = tempEnvironment ?? environment;

    final declaredReturnType = tempEnvironment != null
        ? _resolveTypeAnnotationWithEnvironment(
            node.returnType, resolveEnvironment,
            isAsync: isAsync)
        : _resolveTypeAnnotation(node.returnType, isAsync: isAsync);

    final function = InterpretedFunction.declaration(
        node, environment, declaredReturnType, isNullable);
    // Define the function in the current environment
    environment.define(node.name.lexeme, function);
    return null; // Declaration itself doesn't return a value
  }

  // Handle function expressions (anonymous functions)
  @override
  Object? visitFunctionExpression(FunctionExpression node) {
    // Create a function object capturing the current environment (closure)
    // Use the .expression constructor since it might be anonymous
    final function = InterpretedFunction.expression(node, environment);
    // Return the function object itself as the value of the expression
    return function;
  }

  @override
  Object? visitReturnStatement(ReturnStatement node) {
    AstNode? eDecl = node;
    while (eDecl != null) {
      if (eDecl is FunctionDeclaration) {
        break;
      }
      eDecl = eDecl.parent;
    }

    Object? returnValue;
    if (node.expression != null) {
      returnValue = node.expression!.accept<Object?>(this);
      if (returnValue is AsyncSuspensionRequest) {
        return returnValue;
      }
    } else {
      returnValue = null;
    }

    if (eDecl != null && eDecl is FunctionDeclaration) {
      bool isNullable = false;
      final functionName = eDecl.name.lexeme;
      RuntimeType? declaredType;
      RuntimeType? valueRuntimeType;

      try {
        final currentCallable = environment.get(functionName);

        if (currentCallable is InterpretedFunction) {
          declaredType = currentCallable.declaredReturnType;
          isNullable = currentCallable.isNullable;
        }
        valueRuntimeType = environment.getRuntimeType(returnValue);

        String declaredTypeDetails = "N/A";
        if (declaredType != null) {
          declaredTypeDetails =
              "Name: ${declaredType.name}, Hash: ${declaredType.hashCode}";
          if (declaredType is BridgedClass) {
            declaredTypeDetails +=
                ", NativeType: ${declaredType.nativeType}, NativeHash: ${declaredType.nativeType.hashCode}";
          }
        }

        String valueRuntimeTypeDetails = "N/A";
        if (valueRuntimeType != null) {
          valueRuntimeTypeDetails =
              "Name: ${valueRuntimeType.name}, Hash: ${valueRuntimeType.hashCode}";
          if (valueRuntimeType is BridgedClass) {
            valueRuntimeTypeDetails +=
                ", NativeType: ${valueRuntimeType.nativeType}, NativeHash: ${valueRuntimeType.nativeType.hashCode}";
          }
        }

        Logger.debug("[visitReturnStatement] Function: '$functionName'");
        Logger.debug(
            "[visitReturnStatement]   Declared Type: $declaredTypeDetails");
        Logger.debug(
            "[visitReturnStatement]   Value Runtime Type: $valueRuntimeTypeDetails");
        Logger.debug(
            "[visitReturnStatement]   Return Value: $returnValue (Type: ${returnValue?.runtimeType})");
        Logger.debug(
            "[visitReturnStatement]   Is Declared Type Nullable: $isNullable");

        if (declaredType != null && valueRuntimeType != null) {
          Logger.debug(
              "[visitReturnStatement]   declaredType.isSubtypeOf(valueRuntimeType) = ${declaredType.isSubtypeOf(valueRuntimeType)}");
        }

        if (valueRuntimeType != null) {
          if (declaredType != null) {
            if (declaredType.name != "dynamic" &&
                !declaredType.isSubtypeOf(valueRuntimeType,
                    value: returnValue)) {
              bool showError = true;
              if (isNullable && returnValue == null) {
                showError = false;
              }
              if (declaredType.name == "void" && returnValue == null) {
                showError = false;
              }
              if (declaredType.name == "Object" && returnValue != null) {
                showError = false;
              }

              if (showError) {
                throw RuntimeError(
                    "A value of type '${valueRuntimeType.name}' can't be returned from the function '$functionName' because it has a return type of '${eDecl.returnType}'.");
              }
            }
          }
        }
      } catch (e) {
        // Log before rethrow for more context in case of unexpected error here
        Logger.error(
            "[visitReturnStatement] Error during type check for function '$functionName': $e");
        if (e is Error) {
          Logger.error("Stack trace: ${e.stackTrace}");
        }
        rethrow;
      }
    }

    // For non-suspended results, throw the exception to unwind the stack.
    throw ReturnException(returnValue);
  }

  @override
  Object? visitConditionalExpression(ConditionalExpression node) {
    final conditionValue = node.condition.accept<Object?>(this);
    bool conditionResult;
    final bridgedInstance = toBridgedInstance(conditionValue);
    if (conditionValue is bool) {
      conditionResult = conditionValue;
    } else if (bridgedInstance.$2 && bridgedInstance.$1?.nativeObject is bool) {
      conditionResult = bridgedInstance.$1!.nativeObject as bool;
    } else {
      throw RuntimeError(
          "The condition of a conditional expression must be a boolean, but was ${conditionValue?.runtimeType}.");
    }

    if (conditionResult) {
      return node.thenExpression.accept<Object?>(this);
    } else {
      return node.elseExpression.accept<Object?>(this);
    }
  }

  @override
  Object? visitPrefixExpression(PrefixExpression node) {
    final operatorType = node.operator.type;
    final operandNode = node.operand;
    final operandValue = operandNode.accept<Object?>(this);
    final bridgedInstance = toBridgedInstance(operandValue);
    final operand =
        bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : operandValue;

    switch (operatorType) {
      case TokenType.BANG: // Logical NOT
        // Use unwrapped operand for check
        if (operand is bool) {
          return !operand;
        } else {
          // Error uses original value type
          throw RuntimeError(
              "Operand for '!' must be a boolean, but was ${operandValue?.runtimeType}.");
        }

      case TokenType.MINUS: // Unary minus (negation)
        // Use unwrapped operand for check
        if (operand is num) {
          return -operand;
        } else if (operand is BigInt) {
          return -operand;
        } else if (operandValue is InterpretedInstance) {
          // Check for class operator - method
          final operatorMethod = operandValue.findOperator('-');
          if (operatorMethod != null) {
            Logger.debug(
                "[PrefixExpr] Found class operator '-' on ${operandValue.klass.name}. Calling...");
            try {
              return operatorMethod.bind(operandValue).call(this, [], {});
            } on ReturnException catch (e) {
              return e.value;
            } catch (e) {
              throw RuntimeError("Error executing class operator '-': $e");
            }
          }
          // No class operator found, try extensions
        }

        const operatorName = '-';
        try {
          final extensionOperator =
              environment.findExtensionMember(operandValue, operatorName);
          if (extensionOperator is InterpretedExtensionMethod &&
              extensionOperator.isOperator) {
            Logger.debug(
                "[PrefixExpr] Found extension operator '-' for type ${operandValue?.runtimeType}. Calling...");
            // Args: receiver (operandValue)
            try {
              return extensionOperator.call(this, [operandValue], {});
            } on ReturnException catch (e) {
              return e.value;
            } catch (e) {
              throw RuntimeError("Error executing extension operator '-': $e");
            }
          }
        } on RuntimeError catch (findError) {
          Logger.debug(
              "[PrefixExpr] Extension operator '-' not found for type ${operandValue?.runtimeType}. Error: ${findError.message}");
          // Fall through
        }
        // Error uses original value type if extension not found/failed
        throw RuntimeError(
            "Operand for unary '-' must be a number or have an operator defined, but was ${operandValue?.runtimeType}.");

      case TokenType.TILDE: // Bitwise NOT (~)
        if (operand is int) {
          return ~operand;
        } else if (operand is BigInt) {
          // BigInt does not have a standard unary ~ operator in Dart
          // We rely solely on extensions for BigInt bitwise NOT
        } else if (operandValue is InterpretedInstance) {
          // Check for class operator ~ method
          final operatorMethod = operandValue.findOperator('~');
          if (operatorMethod != null) {
            Logger.debug(
                "[PrefixExpr] Found class operator '~' on ${operandValue.klass.name}. Calling...");
            try {
              return operatorMethod.bind(operandValue).call(this, [], {});
            } on ReturnException catch (e) {
              return e.value;
            } catch (e) {
              throw RuntimeError("Error executing class operator '~': $e");
            }
          }
          // No class operator found, try extensions
        }

        // Try Extension Operator '~' (for non-int or BigInt)
        const operatorNameTilde = '~';
        try {
          final extensionOperator =
              environment.findExtensionMember(operandValue, operatorNameTilde);
          if (extensionOperator is InterpretedExtensionMethod &&
              extensionOperator.isOperator) {
            Logger.debug(
                "[PrefixExpr] Found extension operator '~' for type ${operandValue?.runtimeType}. Calling...");
            // Args: receiver (operandValue)
            try {
              return extensionOperator.call(this, [operandValue], {});
            } on ReturnException catch (e) {
              return e.value;
            } catch (e) {
              throw RuntimeError("Error executing extension operator '~': $e");
            }
          }
        } on RuntimeError catch (findError) {
          Logger.debug(
              "[PrefixExpr] Extension operator '~' not found for type ${operandValue?.runtimeType}. Error: ${findError.message}");
          // Fall through
        }
        // Error if neither standard nor extension worked
        throw RuntimeError(
            "Operand for unary '~' must be an int or have an operator defined, but was ${operandValue?.runtimeType}.");

      case TokenType.PLUS_PLUS: // Prefix increment (++x)
      case TokenType.MINUS_MINUS: // Prefix decrement (--x)
        // Re-evaluate and unwrap operand specifically for ++/--
        final operandValue = operandNode.accept<Object?>(this);
        final bridgedInstance = toBridgedInstance(operandValue);
        final assignOperand = bridgedInstance.$2
            ? bridgedInstance.$1!.nativeObject
            : operandValue;

        // Check if AST node is assignable (SimpleIdentifier or PropertyAccess for now)
        if (operandNode is SimpleIdentifier) {
          final variableName = operandNode.name;
          // We need the current value (already got it as assignOperand)
          final currentValue = assignOperand;

          if (currentValue is num) {
            final newValue = operatorType == TokenType.PLUS_PLUS
                ? currentValue + 1
                : currentValue - 1;
            // Assign the new value back to the variable
            environment.assign(variableName, newValue);
            // Return the *new* value
            return newValue;
          } else if (operandValue is InterpretedInstance) {
            // Use custom + operator with literal 1
            final operatorMethod = operandValue.findOperator('+');
            if (operatorMethod != null) {
              try {
                // For ++x, we create appropriate operand and call x + operand
                final operand = _createIncrementOperand(
                    currentValue, operatorType == TokenType.PLUS_PLUS);
                // Note: For --, we could either call x + (-1) or x - 1
                // Let's use + with -1 for consistency
                final newValue =
                    operatorMethod.bind(operandValue).call(this, [operand], {});
                // Assign the new value back to the variable
                environment.assign(variableName, newValue);
                // Return the *new* value
                return newValue;
              } on ReturnException catch (e) {
                final newValue = e.value;
                // Assign the new value back to the variable
                environment.assign(variableName, newValue);
                // Return the *new* value
                return newValue;
              } catch (e) {
                throw RuntimeError(
                    "Error executing custom operator '+' for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
              }
            } else {
              throw RuntimeError(
                  "Cannot increment/decrement object of type '${operandValue.klass.name}': No operator '+' found.");
            }
          } else {
            // Requires finding operator +/-, then assigning back.
            // Complex, skip for now.
            // Error uses original value type
            throw RuntimeError(
                "Operand for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' must be a number, but was ${operandValue?.runtimeType}. Extension support TBD.");
          }
        } else if (operandNode is PropertyAccess) {
          // Handle property access like obj.field++
          final targetValue = operandNode.target?.accept<Object?>(this);
          final propertyName = operandNode.propertyName.name;

          if (targetValue is InterpretedInstance) {
            // Get current value via getter or field
            final currentValue = targetValue.get(propertyName);

            // Calculate new value
            Object? newValue;
            if (currentValue is num) {
              newValue = operatorType == TokenType.PLUS_PLUS
                  ? currentValue + 1
                  : currentValue - 1;
            } else if (currentValue is InterpretedInstance) {
              // Use custom + operator with literal 1
              final operatorMethod = currentValue.findOperator('+');
              if (operatorMethod != null) {
                try {
                  // For ++x, we create a literal 1 and call x + 1
                  operatorType == TokenType.PLUS_PLUS ? 1 : -1;
                  // Note: For --, we could either call x + (-1) or x - 1
                  // Let's use + with -1 for consistency
                  newValue = operatorMethod
                      .bind(currentValue)
                      .call(this, [operand], {});
                } on ReturnException catch (e) {
                  newValue = e.value;
                } catch (e) {
                  throw RuntimeError(
                      "Error executing custom operator '+' for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
                }
              } else {
                throw RuntimeError(
                    "Cannot increment/decrement object of type '${currentValue.klass.name}': No operator '+' found.");
              }
            } else {
              throw RuntimeError(
                  "Cannot increment/decrement property '$propertyName' of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
            }

            // Set new value via setter or field
            final setter = targetValue.klass.findInstanceSetter(propertyName);
            if (setter != null) {
              setter.bind(targetValue).call(this, [newValue], {});
            } else {
              targetValue.set(propertyName, newValue, this);
            }

            // Return the *new* value for prefix operators
            return newValue;
          } else {
            throw RuntimeError(
                "Cannot increment/decrement property on non-instance object of type '${targetValue?.runtimeType}'.");
          }
        } else if (operandNode is PrefixedIdentifier) {
          // Handle prefixed identifier like obj.field++ (parsed as PrefixedIdentifier)
          final targetValue = operandNode.prefix.accept<Object?>(this);
          final propertyName = operandNode.identifier.name;

          if (targetValue is InterpretedInstance) {
            // Get current value via getter or field
            final currentValue = targetValue.get(propertyName);

            // Calculate new value
            Object? newValue;
            if (currentValue is num) {
              newValue = operatorType == TokenType.PLUS_PLUS
                  ? currentValue + 1
                  : currentValue - 1;
            } else if (currentValue is InterpretedInstance) {
              // Use custom + operator with literal 1
              final operatorMethod = currentValue.findOperator('+');
              if (operatorMethod != null) {
                try {
                  // For ++x, we create a literal 1 and call x + 1
                  operatorType == TokenType.PLUS_PLUS ? 1 : -1;
                  // Note: For --, we could either call x + (-1) or x - 1
                  // Let's use + with -1 for consistency
                  newValue = operatorMethod
                      .bind(currentValue)
                      .call(this, [operand], {});
                } on ReturnException catch (e) {
                  newValue = e.value;
                } catch (e) {
                  throw RuntimeError(
                      "Error executing custom operator '+' for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
                }
              } else {
                throw RuntimeError(
                    "Cannot increment/decrement object of type '${currentValue.klass.name}': No operator '+' found.");
              }
            } else {
              throw RuntimeError(
                  "Cannot increment/decrement property '$propertyName' of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
            }

            // Set new value via setter or field
            final setter = targetValue.klass.findInstanceSetter(propertyName);
            if (setter != null) {
              setter.bind(targetValue).call(this, [newValue], {});
            } else {
              targetValue.set(propertyName, newValue, this);
            }

            // Return the *new* value for prefix operators
            return newValue;
          } else {
            throw RuntimeError(
                "Cannot increment/decrement property on non-instance object of type '${targetValue?.runtimeType}'.");
          }
        } else if (operandNode is IndexExpression) {
          // Handle index access like ++array[i]
          final targetValue = operandNode.target?.accept<Object?>(this);
          final indexValue = operandNode.index.accept<Object?>(this);

          // Get current value via [] operator or direct access
          Object? currentValue;
          if (targetValue is List) {
            final index = indexValue as int;
            currentValue = targetValue[index];
          } else if (targetValue is Map) {
            currentValue = targetValue[indexValue];
          } else if (targetValue is InterpretedInstance) {
            // Use class operator [] if available
            final operatorMethod = targetValue.findOperator('[]');
            if (operatorMethod != null) {
              try {
                currentValue = operatorMethod
                    .bind(targetValue)
                    .call(this, [indexValue], {});
              } on ReturnException catch (e) {
                currentValue = e.value;
              } catch (e) {
                throw RuntimeError(
                    "Error executing class operator '[]' for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
              }
            } else {
              throw RuntimeError(
                  "Cannot read index for prefix increment/decrement on ${targetValue.klass.name}: No operator '[]' found.");
            }
          } else {
            throw RuntimeError(
                "Cannot apply prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' to index of type '${targetValue?.runtimeType}'.");
          }

          // Calculate new value
          Object? newValue;
          if (currentValue is num) {
            newValue = operatorType == TokenType.PLUS_PLUS
                ? currentValue + 1
                : currentValue - 1;
          } else if (currentValue is InterpretedInstance) {
            // Use custom + operator with literal 1
            final operatorMethod = currentValue.findOperator('+');
            if (operatorMethod != null) {
              try {
                final operand = _createIncrementOperand(
                    currentValue, operatorType == TokenType.PLUS_PLUS);
                newValue =
                    operatorMethod.bind(currentValue).call(this, [operand], {});
              } on ReturnException catch (e) {
                newValue = e.value;
              } catch (e) {
                throw RuntimeError(
                    "Error executing custom operator '+' for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
              }
            } else {
              throw RuntimeError(
                  "Cannot increment/decrement object at index of type '${currentValue.klass.name}': No operator '+' found.");
            }
          } else {
            throw RuntimeError(
                "Cannot increment/decrement value at index of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
          }

          // Set new value via []= operator or direct access
          if (targetValue is List) {
            final index = indexValue as int;
            targetValue[index] = newValue;
          } else if (targetValue is Map) {
            targetValue[indexValue] = newValue;
          } else if (targetValue is InterpretedInstance) {
            // Use class operator []= if available
            final operatorMethod = targetValue.findOperator('[]=');
            if (operatorMethod != null) {
              try {
                operatorMethod
                    .bind(targetValue)
                    .call(this, [indexValue, newValue], {});
              } on ReturnException catch (_) {
                // []= should not return a value, but assignment expression returns assigned value
              } catch (e) {
                throw RuntimeError(
                    "Error executing class operator '[]=' for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
              }
            } else {
              throw RuntimeError(
                  "Cannot write index for prefix increment/decrement on ${targetValue.klass.name}: No operator '[]=' found.");
            }
          }

          // Return the *new* value for prefix operators
          return newValue;
        } else {
          Logger.debug("Operand type: ${operandNode.runtimeType}");
          throw RuntimeError(
              "Operand for prefix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' must be an assignable variable, property, or index.");
        }
      default:
        // Check for class operators first for any other unary operators
        final String operatorLexeme = node.operator.lexeme;
        if (operandValue is InterpretedInstance) {
          final operatorMethod = operandValue.findOperator(operatorLexeme);
          if (operatorMethod != null) {
            Logger.debug(
                "[PrefixExpr] Found class operator '$operatorLexeme' on ${operandValue.klass.name}. Calling...");
            try {
              return operatorMethod.bind(operandValue).call(this, [], {});
            } on ReturnException catch (e) {
              return e.value;
            } catch (e) {
              throw RuntimeError(
                  "Error executing class operator '$operatorLexeme': $e");
            }
          }
        }

        // Check for extension operators if no class operator found
        try {
          final extensionOperator =
              environment.findExtensionMember(operandValue, operatorLexeme);
          if (extensionOperator is InterpretedExtensionMethod &&
              extensionOperator.isOperator) {
            Logger.debug(
                "[PrefixExpr] Found generic extension operator '$operatorLexeme' for type ${operandValue?.runtimeType}. Calling...");
            try {
              return extensionOperator.call(this, [operandValue], {});
            } on ReturnException catch (e) {
              return e.value;
            } catch (e) {
              throw RuntimeError(
                  "Error executing extension operator '$operatorLexeme': $e");
            }
          }
        } on RuntimeError {
          // Fall through if no generic extension op found
        }
        throw UnimplementedError(
            'Unary prefix operator not handled: ${node.operator.lexeme} ($operatorType)');
    }
  }

  @override
  Object? visitPostfixExpression(PostfixExpression node) {
    final operatorType = node.operator.type;

    // Support for the non-null assertion operator (!)
    if (operatorType == TokenType.BANG) {
      final operandValue = node.operand.accept<Object?>(this);
      if (operandValue == null) {
        throw RuntimeError(
            "Null check operator used on a null value at ${node.toString()}");
      }
      return operandValue;
    }

    // Check if operand is assignable (SimpleIdentifier or PropertyAccess)
    if (node.operand is SimpleIdentifier) {
      final variableName = (node.operand as SimpleIdentifier).name;
      Object? operandValue;
      InterpretedInstance? thisInstance;
      bool isInstanceField = false;

      // Try lexical scope first, then implicit 'this'
      try {
        operandValue = environment.get(variableName); // Try lexical scope
      } on RuntimeError {
        // Not found lexically, try implicit 'this'
        try {
          final potentialThis = environment.get('this');
          if (potentialThis is InterpretedInstance) {
            thisInstance = potentialThis;
            operandValue = thisInstance.get(variableName); // Get from instance
            isInstanceField = true;
          } else {
            throw RuntimeError("Undefined variable: $variableName");
          }
        } on RuntimeError {
          throw RuntimeError("Undefined variable: $variableName");
        }
      }
      final bridgedInstance = toBridgedInstance(operandValue);
      final currentValue =
          bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : operandValue;

      if (currentValue is num) {
        final newValue = operatorType == TokenType.PLUS_PLUS
            ? currentValue + 1
            : currentValue - 1;

        // Assign back to correct target (lexical or instance)
        if (isInstanceField && thisInstance != null) {
          final setter = thisInstance.klass.findInstanceSetter(variableName);
          if (setter != null) {
            setter.bind(thisInstance).call(this, [newValue], {});
          } else {
            thisInstance.set(
                variableName, newValue, this); // Assign to instance field
          }
        } else {
          environment.assign(
              variableName, newValue); // Assign to lexical variable
        }

        return operandValue; // Return the original value
      } else if (operandValue is InterpretedInstance) {
        // Use custom + operator with literal 1
        final operatorMethod = operandValue.findOperator('+');
        if (operatorMethod != null) {
          try {
            // For x++, we create a literal 1 and call x + 1
            final operand = _createIncrementOperand(
                currentValue, operatorType == TokenType.PLUS_PLUS);
            Object? newValue =
                operatorMethod.bind(operandValue).call(this, [operand], {});

            // Assign back to correct target (lexical or instance)
            if (isInstanceField && thisInstance != null) {
              final setter =
                  thisInstance.klass.findInstanceSetter(variableName);
              if (setter != null) {
                setter.bind(thisInstance).call(this, [newValue], {});
              } else {
                thisInstance.set(
                    variableName, newValue, this); // Assign to instance field
              }
            } else {
              environment.assign(
                  variableName, newValue); // Assign to lexical variable
            }

            return operandValue; // Return the original value for postfix
          } on ReturnException catch (e) {
            final newValue = e.value;

            // Assign back to correct target (lexical or instance)
            if (isInstanceField && thisInstance != null) {
              final setter =
                  thisInstance.klass.findInstanceSetter(variableName);
              if (setter != null) {
                setter.bind(thisInstance).call(this, [newValue], {});
              } else {
                thisInstance.set(
                    variableName, newValue, this); // Assign to instance field
              }
            } else {
              environment.assign(
                  variableName, newValue); // Assign to lexical variable
            }

            return operandValue; // Return the original value for postfix
          } catch (e) {
            throw RuntimeError(
                "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
          }
        } else {
          throw RuntimeError(
              "Cannot increment/decrement object of type '${operandValue.klass.name}': No operator '+' found.");
        }
      } else {
        throw RuntimeError(
            "Operand for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' must be a number, but was ${operandValue?.runtimeType}.");
      }
    } else if (node.operand is PropertyAccess) {
      // Handle property access like obj.field++
      final propertyAccess = node.operand as PropertyAccess;
      final targetValue = propertyAccess.target?.accept<Object?>(this);
      final propertyName = propertyAccess.propertyName.name;

      if (targetValue is InterpretedInstance) {
        // Get current value via getter or field
        final currentValue = targetValue.get(propertyName);
        final originalValue = currentValue; // Save for return

        // Calculate new value
        Object? newValue;
        if (currentValue is num) {
          newValue = operatorType == TokenType.PLUS_PLUS
              ? currentValue + 1
              : currentValue - 1;
        } else if (currentValue is InterpretedInstance) {
          // Use custom + operator with literal 1
          final operatorMethod = currentValue.findOperator('+');
          if (operatorMethod != null) {
            try {
              // For x++, we create a literal 1 and call x + 1
              final operand = _createIncrementOperand(
                  currentValue, operatorType == TokenType.PLUS_PLUS);
              newValue =
                  operatorMethod.bind(currentValue).call(this, [operand], {});
            } on ReturnException catch (e) {
              newValue = e.value;
            } catch (e) {
              throw RuntimeError(
                  "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
            }
          } else {
            throw RuntimeError(
                "Cannot increment/decrement object of type '${currentValue.klass.name}': No operator '+' found.");
          }
        } else {
          throw RuntimeError(
              "Cannot increment/decrement property '$propertyName' of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
        }

        // Set new value via setter or field
        final setter = targetValue.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(targetValue).call(this, [newValue], {});
        } else {
          targetValue.set(propertyName, newValue, this);
        }

        // Return the *original* value for postfix operators
        return originalValue;
      } else {
        throw RuntimeError(
            "Cannot increment/decrement property on non-instance object of type '${targetValue?.runtimeType}'.");
      }
    } else if (node.operand is PrefixedIdentifier) {
      // Handle prefixed identifier like obj.field++ (parsed as PrefixedIdentifier)
      final prefixedIdentifier = node.operand as PrefixedIdentifier;
      final targetValue = prefixedIdentifier.prefix.accept<Object?>(this);
      final propertyName = prefixedIdentifier.identifier.name;

      if (targetValue is InterpretedInstance) {
        // Get current value via getter or field
        final currentValue = targetValue.get(propertyName);
        final originalValue = currentValue; // Save for return

        // Calculate new value
        Object? newValue;
        if (currentValue is num) {
          newValue = operatorType == TokenType.PLUS_PLUS
              ? currentValue + 1
              : currentValue - 1;
        } else if (currentValue is InterpretedInstance) {
          // Use custom + operator with literal 1
          final operatorMethod = currentValue.findOperator('+');
          if (operatorMethod != null) {
            try {
              // For x++, we create a literal 1 and call x + 1
              final operand = _createIncrementOperand(
                  currentValue, operatorType == TokenType.PLUS_PLUS);
              newValue =
                  operatorMethod.bind(currentValue).call(this, [operand], {});
            } on ReturnException catch (e) {
              newValue = e.value;
            } catch (e) {
              throw RuntimeError(
                  "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
            }
          } else {
            throw RuntimeError(
                "Cannot increment/decrement object of type '${currentValue.klass.name}': No operator '+' found.");
          }
        } else {
          throw RuntimeError(
              "Cannot increment/decrement property '$propertyName' of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
        }

        // Set new value via setter or field
        final setter = targetValue.klass.findInstanceSetter(propertyName);
        if (setter != null) {
          setter.bind(targetValue).call(this, [newValue], {});
        } else {
          targetValue.set(propertyName, newValue, this);
        }

        // Return the *original* value for postfix operators
        return originalValue;
      } else {
        throw RuntimeError(
            "Cannot increment/decrement property on non-instance object of type '${targetValue?.runtimeType}'.");
      }
    } else if (node.operand is IndexExpression) {
      // Handle index access like array[i]++
      final indexExpression = node.operand as IndexExpression;
      final targetValue = indexExpression.target?.accept<Object?>(this);
      final indexValue = indexExpression.index.accept<Object?>(this);

      // Get current value via [] operator or direct access
      Object? currentValue;
      if (targetValue is List) {
        final index = indexValue as int;
        currentValue = targetValue[index];
      } else if (targetValue is Map) {
        currentValue = targetValue[indexValue];
      } else if (targetValue is InterpretedInstance) {
        // Use class operator [] if available
        final operatorMethod = targetValue.findOperator('[]');
        if (operatorMethod != null) {
          try {
            currentValue =
                operatorMethod.bind(targetValue).call(this, [indexValue], {});
          } on ReturnException catch (e) {
            currentValue = e.value;
          } catch (e) {
            throw RuntimeError(
                "Error executing class operator '[]' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
          }
        } else {
          throw RuntimeError(
              "Cannot read index for postfix increment/decrement on ${targetValue.klass.name}: No operator '[]' found.");
        }
      } else {
        throw RuntimeError(
            "Cannot apply postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' to index of type '${targetValue?.runtimeType}'.");
      }

      final originalValue = currentValue; // Save for return

      // Calculate new value
      Object? newValue;
      if (currentValue is num) {
        newValue = operatorType == TokenType.PLUS_PLUS
            ? currentValue + 1
            : currentValue - 1;
      } else if (currentValue is InterpretedInstance) {
        // Use custom + operator with literal 1
        final operatorMethod = currentValue.findOperator('+');
        if (operatorMethod != null) {
          try {
            final operand = _createIncrementOperand(
                currentValue, operatorType == TokenType.PLUS_PLUS);
            newValue =
                operatorMethod.bind(currentValue).call(this, [operand], {});
          } on ReturnException catch (e) {
            newValue = e.value;
          } catch (e) {
            throw RuntimeError(
                "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
          }
        } else {
          throw RuntimeError(
              "Cannot increment/decrement object at index of type '${currentValue.klass.name}': No operator '+' found.");
        }
      } else {
        throw RuntimeError(
            "Cannot increment/decrement value at index of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
      }

      // Set new value via []= operator or direct access
      if (targetValue is List) {
        final index = indexValue as int;
        targetValue[index] = newValue;
      } else if (targetValue is Map) {
        targetValue[indexValue] = newValue;
      } else if (targetValue is InterpretedInstance) {
        // Use class operator []= if available
        final operatorMethod = targetValue.findOperator('[]=');
        if (operatorMethod != null) {
          try {
            operatorMethod
                .bind(targetValue)
                .call(this, [indexValue, newValue], {});
          } on ReturnException catch (_) {
            // []= should not return a value, but assignment expression returns assigned value
          } catch (e) {
            throw RuntimeError(
                "Error executing class operator '[]=' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
          }
        } else {
          throw RuntimeError(
              "Cannot write index for postfix increment/decrement on ${targetValue.klass.name}: No operator '[]=' found.");
        }
      }

      // Return the *original* value for postfix operators
      return originalValue;
    } else {
      throw RuntimeError(
          "Operand for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' must be an assignable variable or property.");
    }
  }

  // Handle String Interpolation: "Value is ${expr}"
  @override
  Object? visitStringInterpolation(StringInterpolation node) {
    final buffer = StringBuffer();
    for (final element in node.elements) {
      if (element is InterpolationString) {
        buffer.write(element.value);
      } else if (element is InterpolationExpression) {
        final value = element.expression.accept<Object?>(this);
        buffer.write(stringify(value)); // Use stringify helper
      } else {
        // Should not happen based on AST structure
        throw StateError(
            'Unknown interpolation element: ${element.runtimeType}');
      }
    }
    return buffer.toString();
  }

  @override
  Object? visitSuperExpression(SuperExpression node) {
    if (currentFunction == null || currentFunction?.ownerType == null) {
      // Use ownerType
      throw RuntimeError("'super' can only be used within an instance method.");
    }
    final ownerType = currentFunction!.ownerType!; // Use ownerType
    // Need to ensure ownerType is actually a class for super access
    if (ownerType is! InterpretedClass) {
      throw RuntimeError(
          "'super' used outside of a class context (found ${ownerType.runtimeType}).");
    }
    final definingClass =
        ownerType; // Can safely use ownerType as InterpretedClass now

    InterpretedClass? standardSuperclass = definingClass.superclass;
    BridgedClass? bridgedSuperclass = definingClass.bridgedSuperclass;

    if (standardSuperclass == null && bridgedSuperclass == null) {
      throw RuntimeError(
          "Class '${definingClass.name}' does not have a standard or bridged superclass, cannot use 'super'.");
    }

    // Get the current 'this' instance
    try {
      final thisInstance = environment.get('this');
      if (thisInstance is InterpretedInstance) {
        // Pass the correct superclass type to BoundSuper
        if (standardSuperclass != null) {
          return BoundSuper(thisInstance, standardSuperclass);
        } else {
          // bridgedSuperclass cannot be null here due to the check above
          return BoundBridgedSuper(thisInstance, bridgedSuperclass!);
        }
      } else {
        // Should not happen if currentFunction is set correctly
        throw RuntimeError("Cannot find 'this' instance when using 'super'.");
      }
    } on RuntimeError {
      throw RuntimeError("Cannot find 'this' instance when using 'super'.");
    }
  }

  @override
  Object? visitLabeledStatement(LabeledStatement node) {
    final labelNames = node.labels.map((l) => l.label.name).toSet();
    final oldLabels = _currentStatementLabels;
    _currentStatementLabels = labelNames;
    Logger.debug("[LabeledStatement] Entering with labels: $labelNames");

    try {
      return node.statement.accept<Object?>(this);
    } on BreakException catch (e) {
      Logger.debug(
          "[LabeledStatement] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
      if (e.label != null && _currentStatementLabels.contains(e.label)) {
        // This break was targeting this labeled statement.
        Logger.debug(
            "[LabeledStatement] Consuming labeled break: '${e.label}'.");
        return null; // Effectively breaks out of the labeled statement.
      } else {
        // Unlabeled break or break for an outer label, rethrow.
        Logger.debug("[LabeledStatement] Rethrowing break...");
        rethrow;
      }
    }
    // ContinueException with a label matching this statement is an error
    // (you can only continue loops/switch members), so we don't catch it here.
    // It should be caught by the loop/switch or propagate further up.
    finally {
      Logger.debug("[LabeledStatement] Exiting labels: $labelNames");
      _currentStatementLabels = oldLabels;
    }
  }

  @override
  Object? visitClassDeclaration(ClassDeclaration node) {
    final className = node.name.lexeme;
    Logger.debug(
        "[Visitor.visitClassDeclaration] START for '$className' in env: ${environment.hashCode}");

    // Retrieve the placeholder class object created in Pass 1
    Object? placeholder = environment.get(className);
    if (placeholder == null || placeholder is! InterpretedClass) {
      // This should not happen if Pass 1 worked correctly
      throw StateError(
          "Placeholder for class '$className' not found or invalid during Pass 2.");
    }
    final klass = placeholder;
    Logger.debug(
        "[Visitor.visitClassDeclaration] Retrieved placeholder for '$className' (hash: ${klass.hashCode})");

    // Resolve and populate relationships ON THE EXISTING klass object
    // Superclass lookup
    // InterpretedClass? superclass; // Keep this commented or remove
    if (node.extendsClause != null) {
      final superclassName = node.extendsClause!.superclass.name2.lexeme;
      Logger.debug(
          "[Visitor.visitClassDeclaration]   Trying to get superclass '$superclassName' from env: ${environment.hashCode}");
      Object? potentialSuperclass;
      try {
        potentialSuperclass = environment.get(superclassName);
      } on RuntimeError {
        throw RuntimeError(
            "Superclass '$superclassName' not found for class '$className'. Ensure it's defined.");
      }

      if (potentialSuperclass is InterpretedClass) {
        // Standard Dart Superclass
        final superclass = potentialSuperclass;

        // Add checks for final, interface, and sealed modifiers
        if (superclass.isFinal) {
          throw RuntimeError(
              "Class '$className' cannot extend final class '$superclassName'.");
        }
        if (superclass.isInterface) {
          throw RuntimeError(
              "Class '$className' cannot extend interface class '$superclassName'. Use 'implements'.");
        }
        if (superclass.isSealed) {
          throw RuntimeError(
              "Class '$className' cannot extend sealed class '$superclassName' outside of its library.");
        }

        // Set the superclass and clear bridged superclass
        klass.superclass = superclass;
        klass.bridgedSuperclass = null;
        Logger.debug(
            "[Visitor.visitClassDeclaration] Set standard superclass '$superclassName' for '$className'");
      } else if (potentialSuperclass is BridgedClass) {
        final bridgedSuperclass = potentialSuperclass;
        // No modifier checks needed for bridged classes (yet?)

        // Set the bridged superclass and clear standard superclass
        klass.bridgedSuperclass = bridgedSuperclass;
        klass.superclass = null;
        Logger.debug(
            "[Visitor.visitClassDeclaration] Set BRIDGED superclass '$superclassName' for '$className'");
      } else {
        throw RuntimeError(
            "Superclass '$superclassName' for class '$className' resolved to ${potentialSuperclass?.runtimeType}, which is not a class or bridged class.");
      }
    }

    // Interface lookup
    if (node.implementsClause != null) {
      Logger.debug(
          "[Visitor.visitClassDeclaration] Processing 'implements' clause for '$className' in env: ${environment.hashCode}");
      for (final interfaceType in node.implementsClause!.interfaces) {
        final interfaceName = interfaceType.name2.lexeme;
        Logger.debug(
            "[Visitor.visitClassDeclaration]   Trying to get interface '$interfaceName' from env: ${environment.hashCode}");
        try {
          final potentialInterface = environment.get(interfaceName);
          if (potentialInterface is InterpretedClass) {
            // Add checks for base and sealed modifiers
            if (potentialInterface.isBase) {
              throw RuntimeError(
                  "Class '$className' cannot implement base class '$interfaceName' outside of its library.");
            }
            if (potentialInterface.isSealed) {
              throw RuntimeError(
                  "Class '$className' cannot implement sealed class '$interfaceName' outside of its library.");
            }

            // Add to the interfaces list of the existing klass object
            klass.interfaces.add(potentialInterface);
            Logger.debug(
                "[Visitor.visitClassDeclaration] Added interface '$interfaceName' for '$className'");
          } else {
            throw RuntimeError(
                "Class '$className' cannot implement non-class '$interfaceName' (${potentialInterface?.runtimeType}).");
          }
        } on RuntimeError {
          throw RuntimeError(
              "Interface '$interfaceName' not found for class '$className'. Ensure it's defined.");
        }
      }
    }

    // Mixin application lookup
    if (node.withClause != null) {
      Logger.debug(
          "[Visitor.visitClassDeclaration] Processing 'with' clause for '$className' in env: ${environment.hashCode}");
      for (final mixinType in node.withClause!.mixinTypes) {
        final mixinName = mixinType.name2.lexeme;
        Logger.debug(
            "[Visitor.visitClassDeclaration]   Trying to get mixin '$mixinName' from env: ${environment.hashCode}");

        Object? mixin;
        try {
          mixin = environment.get(mixinName);
        } on RuntimeError {
          throw RuntimeError(
              "Mixin '$mixinName' not found during lookup for class '$className'. Ensure it's defined (as a mixin or class mixin).");
        }

        if (mixin is InterpretedClass) {
          if (!mixin.isMixin) {
            throw RuntimeError(
                "Class '$mixinName' cannot be used as a mixin because it's not declared with 'mixin' or 'class mixin'.");
          }

          // Add checks for base and sealed modifiers
          if (mixin.isBase) {
            throw RuntimeError(
                "Class '$className' cannot mix in base class '$mixinName' outside of its library.");
          }
          if (mixin.isSealed) {
            throw RuntimeError(
                "Class '$className' cannot mix in sealed class '$mixinName' outside of its library.");
          }

          // Add to the mixins list of the existing klass object
          klass.mixins.add(mixin);
          Logger.debug(
              "[Visitor.visitClassDeclaration] Applied interpreted mixin '$mixinName' to '$className'");
        } else if (mixin is BridgedClass) {
          // Support for bridged classes as mixins
          if (!mixin.canBeUsedAsMixin) {
            throw RuntimeError(
                "Bridged class '$mixinName' cannot be used as a mixin. Set canBeUsedAsMixin=true when registering the bridge.");
          }

          // Add to the bridged mixins list
          klass.bridgedMixins.add(mixin);
          Logger.debug(
              "[Visitor.visitClassDeclaration] Applied bridged mixin '$mixinName' to '$className'");
        } else {
          throw RuntimeError(
              "Identifier '$mixinName' resolved to ${mixin?.runtimeType}, which is not a class/mixin, for class '$className'.");
        }
      }
    }

    // Populate members ON THE EXISTING klass object
    final staticInitEnv = environment; // Statics use the class definition env
    final originalVisitorEnv = environment; // Backup visitor env

    Logger.debug(
        "[Visitor.visitClassDeclaration] Processing members for '$className' (hash: ${klass.hashCode})");

    for (final member in node.members) {
      if (member is MethodDeclaration) {
        final methodName = member.name.lexeme;
        // Pass the ALREADY RETRIEVED klass object
        final function =
            InterpretedFunction.method(member, staticInitEnv, klass);

        if (member.isStatic) {
          if (member.isGetter) {
            klass.staticGetters[methodName] = function;
          } else if (member.isSetter) {
            klass.staticSetters[methodName] = function;
          } else {
            klass.staticMethods[methodName] = function;
          }
        } else if (!member.isAbstract) {
          if (member.isGetter) {
            klass.getters[methodName] = function;
          } else if (member.isSetter) {
            klass.setters[methodName] = function;
          } else if (member.isOperator) {
            // Add operator methods to the operators map
            klass.operators[methodName] = function;
          } else {
            klass.methods[methodName] = function;
          }
        } else {
          // Abstract
          if (!klass.isAbstract) {
            throw RuntimeError(
                "Abstract methods can only be declared in abstract classes. Method '${klass.name}.$methodName'.");
          }
          if (member.body is! EmptyFunctionBody) {
            throw RuntimeError(
                "Abstract methods cannot have a body. Method '${klass.name}.$methodName'.");
          }
          if (member.isGetter) {
            klass.getters[methodName] = function;
          } else if (member.isSetter) {
            klass.setters[methodName] = function;
          } else if (member.isOperator) {
            // Add abstract operator methods to the operators map
            klass.operators[methodName] = function;
          } else {
            klass.methods[methodName] = function;
          }
        }
      } else if (member is ConstructorDeclaration) {
        final function =
            InterpretedFunction.constructor(member, staticInitEnv, klass);
        final constructorName = member.name?.lexeme ?? '';
        klass.constructors[constructorName] = function;
      } else if (member is FieldDeclaration) {
        if (member.isStatic) {
          try {
            environment = staticInitEnv;
            for (final variable in member.fields.variables) {
              final fieldName = variable.name.lexeme;
              Object? value;
              if (variable.initializer != null) {
                value = variable.initializer!.accept<Object?>(this);
              }
              klass.staticFields[fieldName] = value;
            }
          } finally {
            environment = originalVisitorEnv;
          }
        } else {
          klass.fieldDeclarations.add(member);
        }
      } else {
        throw StateError('Unknown member type: ${member.runtimeType}');
      }
    }
    Logger.debug(
        "[Visitor.visitClassDeclaration] Finished processing members for '$className'");

    // Final Checks (run on the populated klass object)
    // Check for unimplemented abstract members
    if (!klass.isAbstract) {
      final inheritedAbstract = klass.getAbstractInheritedMembers();
      final concreteMembers = klass.getConcreteMembers();
      for (final abstractName in inheritedAbstract.keys) {
        if (!concreteMembers.containsKey(abstractName)) {
          final abstractMember = inheritedAbstract[abstractName]!;
          String memberType = "method";
          if (abstractMember.isGetter) memberType = "getter";
          if (abstractMember.isSetter) memberType = "setter";
          throw RuntimeError(
              "Missing concrete implementation for inherited abstract $memberType '$abstractName' in class '${klass.name}'.");
        }
      }
    }

    // Check for unimplemented interface members
    if (!klass.isAbstract) {
      final requiredInterfaceMembers = klass.getAllInterfaceMembers();
      final availableConcreteMembers = klass.getAllConcreteMembers();
      for (final requiredName in requiredInterfaceMembers.keys) {
        if (!availableConcreteMembers.containsKey(requiredName)) {
          final memberType = requiredInterfaceMembers[requiredName]!;
          throw RuntimeError(
              "Missing concrete implementation for interface $memberType '$requiredName' in class '${klass.name}'.");
        }
      }
    }
    Logger.debug("[Visitor.visitClassDeclaration] END for '$className'");
    // No need to define/assign klass in the environment here, it was done in Pass 1.
    return null; // Class declaration statement doesn't return a value
  }

  @override
  Object? visitAssertStatement(AssertStatement node) {
    // Assertions are always enabled in this interpreter for now.
    final conditionValue = node.condition.accept<Object?>(this);

    final bridgedInstance = toBridgedInstance(conditionValue);
    bool conditionResult;
    if (conditionValue is bool) {
      conditionResult = conditionValue;
    } else if (bridgedInstance.$2 && bridgedInstance.$1?.nativeObject is bool) {
      conditionResult = bridgedInstance.$1!.nativeObject as bool;
    } else {
      throw RuntimeError(
        "Assertion condition must be a boolean, but was ${conditionValue?.runtimeType}.",
      );
    }

    if (!conditionResult) {
      // Condition is false, evaluate the message and throw.
      String assertionMessage = "Assertion failed";
      if (node.message != null) {
        final messageValue = node.message!.accept<Object?>(this);
        assertionMessage = "Assertion failed: ${stringify(messageValue)}";
      }
      // Mimic Dart's AssertionError by throwing a RuntimeError.
      throw RuntimeError(assertionMessage);
    }

    return null; // Assert statements don't produce a value.
  }

  // Visit Mixin Declaration (Adjusted for Two-Pass)
  @override
  Object? visitMixinDeclaration(MixinDeclaration node) {
    final mixinName = node.name.lexeme;
    Logger.debug(
        "[Visitor.visitMixinDeclaration] START for '$mixinName' in env: ${environment.hashCode}");

    // Retrieve the placeholder mixin object created in Pass 1
    Object? placeholder = environment.get(mixinName);
    if (placeholder == null ||
        placeholder is! InterpretedClass ||
        !placeholder.isMixin) {
      throw StateError(
          "Placeholder for mixin '$mixinName' not found or invalid during Pass 2.");
    }
    final mixinClass = placeholder;
    Logger.debug(
        "[Visitor.visitMixinDeclaration] Retrieved placeholder for mixin '$mixinName' (hash: ${mixinClass.hashCode})");

    // Resolve 'on' clause constraints ON THE EXISTING mixinClass object
    if (node.onClause != null) {
      mixinClass.onClauseTypes.clear(); // Clear existing before populating
      for (final typeNode in node.onClause!.superclassConstraints) {
        final typeName = typeNode.name2.lexeme;
        try {
          final potentialType = environment.get(typeName);
          if (potentialType is InterpretedClass) {
            // Add to the onClauseTypes list of the existing mixinClass object
            mixinClass.onClauseTypes.add(potentialType);
            Logger.debug(
                "[Visitor.visitMixinDeclaration] Added 'on' constraint '$typeName' for '$mixinName'");
          } else {
            throw RuntimeError(
                "Type '$typeName' in 'on' clause of mixin '$mixinName' is not a class (${potentialType?.runtimeType}).");
          }
        } on RuntimeError {
          throw RuntimeError(
              "Type '$typeName' in 'on' clause of mixin '$mixinName' not found. Ensure it's defined.");
        }
      }
    }

    // Populate members ON THE EXISTING mixinClass object
    final declarationEnv =
        environment; // Members use the mixin's declaration env
    final originalVisitorEnv = environment;

    Logger.debug(
        "[Visitor.visitMixinDeclaration] Processing members for mixin '$mixinName' (hash: ${mixinClass.hashCode})");

    try {
      environment = declarationEnv;
      for (final member in node.members) {
        if (member is MethodDeclaration) {
          final methodName = member.name.lexeme;
          // Methods capture the GLOBAL environment via the mixinClass
          final function =
              InterpretedFunction.method(member, globalEnvironment, mixinClass);
          if (member.isStatic) {
            // Static members belong to the mixin definition itself
            if (member.isGetter) {
              mixinClass.staticGetters[methodName] = function;
            } else if (member.isSetter) {
              mixinClass.staticSetters[methodName] = function;
            } else {
              mixinClass.staticMethods[methodName] = function;
            }
          } else {
            if (member.isGetter) {
              mixinClass.getters[methodName] = function;
            } else if (member.isSetter) {
              mixinClass.setters[methodName] = function;
            } else if (member.isOperator) {
              // Add operator methods to the operators map for mixins
              mixinClass.operators[methodName] = function;
            } else {
              mixinClass.methods[methodName] = function;
            }
          }
        } else if (member is FieldDeclaration) {
          if (member.isStatic) {
            for (final variable in member.fields.variables) {
              mixinClass.staticFields[variable.name.lexeme] =
                  variable.initializer?.accept<Object?>(this);
            }
          } else {
            mixinClass.fieldDeclarations.add(member);
          }
        } else if (member is ConstructorDeclaration) {
          throw RuntimeError(
              "Mixins cannot declare constructors ('$mixinName').");
        }
      }
    } finally {
      environment = originalVisitorEnv;
    }

    Logger.debug("[Visitor.visitMixinDeclaration] END for '$mixinName'");
    return null; // Mixin declaration doesn't return a value
  }

  @override
  Object? visitEnumDeclaration(EnumDeclaration node) {
    final enumName = node.name.lexeme;
    Logger.debug(
        "[Visitor.visitEnumDeclaration] START (Pass 2) for '$enumName'");

    // Retrieve the enum placeholder object created in Pass 1
    final enumObj = environment.get(enumName);
    if (enumObj == null || enumObj is! InterpretedEnum) {
      throw StateError(
          "Enum placeholder object for '$enumName' not found or invalid during Pass 2.");
    }

    // Process Members (Static and Instance)
    // Members defined in the enum body (methods, getters, fields, constructors)
    final originalVisitorEnv = environment; // Save original environment
    try {
      // Members are defined in the enum's declaration scope
      environment = enumObj.declarationEnvironment;
      for (final member in node.members) {
        if (member is MethodDeclaration) {
          final methodName = member.name.lexeme;
          // Methods capture the enum's declaration environment implicitly
          final function =
              InterpretedFunction.method(member, environment, enumObj);

          if (member.isStatic) {
            if (member.isGetter) {
              enumObj.staticGetters[methodName] = function;
            } else if (member.isSetter) {
              enumObj.staticSetters[methodName] = function;
            } else {
              enumObj.staticMethods[methodName] = function;
            }
            Logger.debug(
                "[Visitor.visitEnumDeclaration]   Processed static method/getter/setter: $methodName");
          } else {
            if (member.isAbstract) {
              throw RuntimeError(
                  "Enums cannot have abstract members ('$enumName.$methodName').");
            }
            if (member.isGetter) {
              enumObj.getters[methodName] = function;
            } else if (member.isSetter) {
              enumObj.setters[methodName] = function;
            } else {
              enumObj.methods[methodName] = function;
            }
            Logger.debug(
                "[Visitor.visitEnumDeclaration]   Processed instance method/getter/setter: $methodName");
          }
        } else if (member is ConstructorDeclaration) {
          if (member.factoryKeyword != null) {
            throw UnimplementedError(
                "Factory constructors in enums are not yet supported.");
          }
          if (member.redirectedConstructor != null) {
            throw UnimplementedError(
                "Redirecting constructors in enums are not yet supported.");
          }
          // Check if it's the default unnamed constructor or a named one
          final constructorName = member.name?.lexeme ?? '';
          // Constructors also capture the enum's declaration environment
          final function =
              InterpretedFunction.constructor(member, environment, enumObj);

          enumObj.constructors[constructorName] = function;
          Logger.debug(
              "[Visitor.visitEnumDeclaration]   Processed constructor: ${constructorName.isEmpty ? enumName : '$enumName.$constructorName'}");
        } else if (member is FieldDeclaration) {
          // Store field declarations for instance initialization
          // Only non-static fields are relevant for enum value instances
          if (!member.isStatic) {
            enumObj.fieldDeclarations.add(member);
            for (final variable in member.fields.variables) {
              Logger.debug(
                  "[Visitor.visitEnumDeclaration]   Stored instance field declaration: ${variable.name.lexeme}");
            }
          } else {
            // Evaluate static fields immediately
            for (final variable in member.fields.variables) {
              final fieldName = variable.name.lexeme;
              Object? value;
              if (variable.initializer != null) {
                value = variable.initializer!.accept<Object?>(this);
              }
              enumObj.staticFields[fieldName] = value;
              Logger.debug(
                  "[Visitor.visitEnumDeclaration]   Evaluated static field: $fieldName = $value");
            }
          }
        } else {
          Logger.warn(
              "[Visitor.visitEnumDeclaration]   Ignoring unknown member type: ${member.runtimeType}");
        }
      }
    } finally {
      environment = originalVisitorEnv; // Restore environment
    }

    // Instantiate Enum Values
    Logger.debug(
        "[Visitor.visitEnumDeclaration]   Instantiating enum values...");
    for (int i = 0; i < node.constants.length; i++) {
      final constantDecl = node.constants[i];
      final valueName = constantDecl.name.lexeme;

      if (enumObj.values.containsKey(valueName)) {
        Logger.warn(
            "[Visitor.visitEnumDeclaration] Enum value '$enumName.$valueName' already exists (should not happen).");
        continue;
      }

      // Create the runtime value instance (without initialized fields yet)
      final enumValueInstance = InterpretedEnumValue(enumObj, valueName, i);

      // Initialize Instance Fields using Constructor
      final constructorInvocation = constantDecl.arguments;
      final constructorName =
          constructorInvocation?.constructorSelector?.name.name ?? '';
      final constructorFunc = enumObj.constructors[constructorName];

      if (constructorFunc == null && constructorInvocation != null) {
        throw RuntimeError(
            "Enum '$enumName' does not have a constructor named '$constructorName' required by constant '$valueName'.");
      }
      if (constructorFunc == null &&
          enumObj.constructors.isNotEmpty &&
          enumObj.constructors.containsKey('')) {
        throw RuntimeError(
            "Enum '$enumName' has a default constructor but constant '$valueName' doesn't call it implicitly (requires explicit `()` if args are needed or constructor exists).");
      }
      // If there are NO constructors defined at all, and no args are passed, it's okay.
      if (constructorFunc != null && constructorInvocation != null) {
        Logger.debug(
            "[Visitor.visitEnumDeclaration]     Calling constructor '${constructorName.isEmpty ? enumName : '$enumName.$constructorName'}' for value '$valueName'");
        // Evaluate arguments for the constructor call
        final (positionalArgs, namedArgs) =
            _evaluateArguments(constructorInvocation.argumentList);

        // Call the constructor function, binding `this` to the enumValueInstance.
        // The constructor's call method needs to handle field initialization.
        try {
          // Use the _prepareExecutionEnvironment helper? Or call directly?
          // Need to ensure constructor initializers (: this.field = arg) run.
          // Let's assume constructorFunc.call handles this when isInitializer is true.
          final boundConstructor = constructorFunc.bind(enumValueInstance);
          boundConstructor.call(this, positionalArgs, namedArgs);
          Logger.debug(
              "[Visitor.visitEnumDeclaration]     Constructor call finished for '$valueName'. Fields: $enumValueInstance"); // Log instance directly for now
        } on RuntimeError catch (e) {
          throw RuntimeError(
              "Error executing constructor for enum value '$enumName.$valueName': ${e.message}");
        } catch (e) {
          throw RuntimeError(
              "Unexpected error executing constructor for enum value '$enumName.$valueName': $e");
        }
      } else if (constructorFunc == null &&
          constructorInvocation == null &&
          enumObj.constructors.isNotEmpty) {
        // Has constructors, but none called and no default exists implicitly.
        throw RuntimeError(
            "Enum constant '$enumName.$valueName' must call a constructor if the enum defines any.");
      } else {
        Logger.debug(
            "[Visitor.visitEnumDeclaration]     No constructor called for '$valueName' (enum has no explicit constructors or constant has no args).");
        // Initialize fields from declarations if no constructor called?
        // This might mirror class field initialization before constructor body.
        final fieldInitEnv =
            Environment(enclosing: enumObj.declarationEnvironment);
        fieldInitEnv.define('this', enumValueInstance);
        final originalVisitorEnvForFields = environment;
        try {
          environment = fieldInitEnv;
          for (final fieldDecl in enumObj.fieldDeclarations) {
            for (final variable in fieldDecl.fields.variables) {
              if (variable.initializer != null) {
                final fieldName = variable.name.lexeme;
                final value = variable.initializer!.accept<Object?>(this);
                enumValueInstance.setField(fieldName, value);
                Logger.debug(
                    "[Visitor.visitEnumDeclaration]     Initialized instance field '$fieldName'=$value for '$valueName' (default init).");
              }
            }
          }
        } finally {
          environment = originalVisitorEnvForFields;
        }
      }

      // Store the fully initialized instance in the enum object's map
      enumObj.values[valueName] = enumValueInstance;
      Logger.debug(
          "[Visitor.visitEnumDeclaration]   Created and initialized instance for '$enumName.$valueName' with index $i");
    }

    // Pre-cache the values list
    try {
      enumObj.valuesList; // Access the getter to trigger cache creation
      Logger.debug(
          "[Visitor.visitEnumDeclaration]   Cached 'values' list for '$enumName'.");
    } catch (e) {
      // Log error if caching fails (shouldn't happen ideally)
      Logger.error(
          "[Visitor.visitEnumDeclaration] Failed to cache 'values' for '$enumName': $e");
    }

    Logger.debug("[Visitor.visitEnumDeclaration] END (Pass 2) for '$enumName'");
    return null; // Declaration doesn't return a value
  }

  // Helper function to compute compound assignment values
  Object? computeCompoundValue(
      Object? currentValue, Object? rhsValue, TokenType operatorType) {
    // Unwrap BridgedInstance if necessary
    final bridgedInstance = toBridgedInstance(currentValue);
    final left =
        bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : currentValue;
    final right = rhsValue;

    if (operatorType == TokenType.PLUS_EQ) {
      // Use unwrapped left/right for calculation
      if (left is num && right is num) {
        return left + right;
      } else if (left is String && right != null) {
        return left + stringify(right);
      }
      // Fall through to extension check if standard types don't match
    } else if (operatorType == TokenType.MINUS_EQ) {
      // Use unwrapped left/right for calculation
      if (left is num && right is num) {
        return left - right;
      }
      // Fall through
    } else if (operatorType == TokenType.STAR_EQ) {
      // Use unwrapped left/right for calculation
      if (left is num && right is num) {
        return left * right;
      }
      // Fall through
    } else if (operatorType == TokenType.SLASH_EQ) {
      // Use unwrapped left/right for calculation
      if (left is num && right is num) {
        if (right == 0) throw RuntimeError("Division by zero in '/='.");
        return left.toDouble() / right.toDouble();
      }
      // Fall through
    } else if (operatorType == TokenType.TILDE_SLASH_EQ) {
      // Use unwrapped left/right for calculation
      if (left is num && right is num) {
        if (rhsValue == 0) {
          throw RuntimeError("Integer division by zero in '~/='.");
        }
        return left ~/ right;
      }
      // Fall through
    } else if (operatorType == TokenType.PERCENT_EQ) {
      // Use unwrapped left/right for calculation
      if (left is num && right is num) {
        if (right == 0) throw RuntimeError("Modulo by zero in '%='.");
        return left % right;
      }
      // Fall through
    } else if (operatorType == TokenType.QUESTION_QUESTION_EQ) {
      // Note: Uses original currentValue, not unwrapped 'left'
      if (currentValue == null) {
        return rhsValue; // If left is null, assign right
      } else {
        return currentValue; // If left is not null, keep left
      }
    } else if (operatorType == TokenType.AMPERSAND_EQ) {
      // Bitwise AND assignment (&=)
      if (left is int && right is int) {
        return left & right;
      } else if (left is BigInt && right is BigInt) {
        return left & right;
      }
      // Fall through to extension search
    } else if (operatorType == TokenType.BAR_EQ) {
      // Bitwise OR assignment (|=)
      if (left is int && right is int) {
        return left | right;
      } else if (left is BigInt && right is BigInt) {
        return left | right;
      }
      // Fall through to extension search
    } else if (operatorType == TokenType.CARET_EQ) {
      // Bitwise XOR assignment (^=)
      if (left is int && right is int) {
        return left ^ right;
      } else if (left is BigInt && right is BigInt) {
        return left ^ right;
      }
      // Fall through to extension search
    } else if (operatorType == TokenType.GT_GT_EQ) {
      // Right shift assignment (>>=)
      if (left is int && right is int) {
        return left >> right;
      } else if (left is BigInt && right is int) {
        return left >> right;
      }
      // Fall through to extension search
    } else if (operatorType == TokenType.LT_LT_EQ) {
      // Left shift assignment (<<=)
      if (left is int && right is int) {
        return left << right;
      } else if (left is BigInt && right is int) {
        return left << right;
      }
      // Fall through to extension search
    } else if (operatorType == TokenType.GT_GT_GT_EQ) {
      // Unsigned right shift assignment (>>>=)
      if (left is int && right is int) {
        return left >>> right;
      }
      // Note: BigInt doesn't support >>> operator
      // Fall through to extension search
    }

    Logger.debug(
        "[CompoundAssign] Standard op failed for $operatorType. Trying extension operator.");
    final String? operatorName = _mapCompoundToOperatorName(operatorType);

    if (operatorName != null) {
      try {
        final extensionOperator =
            environment.findExtensionMember(currentValue, operatorName);

        if (extensionOperator is InterpretedExtensionMethod &&
            extensionOperator.isOperator) {
          Logger.debug(
              "[CompoundAssign] Found extension operator '$operatorName' for type ${currentValue?.runtimeType}. Calling...");
          // Call the extension operator method
          // Args: receiver (currentValue), right-hand-side (rhsValue)
          final extensionPositionalArgs = [currentValue, rhsValue];
          try {
            return extensionOperator.call(this, extensionPositionalArgs, {});
          } on ReturnException catch (e) {
            return e.value; // Should not happen for operators, but handle
          } catch (e) {
            throw RuntimeError(
                "Error executing extension operator '$operatorName' for compound assignment: $e");
          }
        }
        Logger.debug(
            "[CompoundAssign] No suitable extension operator '$operatorName' found for type ${currentValue?.runtimeType}.");
      } on RuntimeError catch (findError) {
        Logger.debug(
            "[CompoundAssign] No extension member '$operatorName' found for type ${currentValue?.runtimeType}. Error: ${findError.message}");
        // Fall through to the final unimplemented error
      }
    }

    throw UnimplementedError(
        'Compound assignment operator $operatorType not handled for types ${currentValue?.runtimeType} and ${rhsValue?.runtimeType}');
  }

  // Map compound assignment token to operator method name
  String? _mapCompoundToOperatorName(TokenType compoundOp) {
    switch (compoundOp) {
      case TokenType.PLUS_EQ:
        return '+';
      case TokenType.MINUS_EQ:
        return '-';
      case TokenType.STAR_EQ:
        return '*';
      case TokenType.SLASH_EQ:
        return '/';
      case TokenType.PERCENT_EQ:
        return '%';
      case TokenType.TILDE_SLASH_EQ:
        return '~/';
      case TokenType.AMPERSAND_EQ:
        return '&';
      case TokenType.BAR_EQ:
        return '|';
      case TokenType.CARET_EQ:
        return '^';
      case TokenType.GT_GT_EQ:
        return '>>';
      case TokenType.LT_LT_EQ:
        return '<<';
      case TokenType.GT_GT_GT_EQ:
        return '>>>';
      // TokenType.QUESTION_QUESTION_EQ (??=) doesn't map to a binary operator method.
      default:
        return null;
    }
  }

  @override
  dynamic visitIdentifier(Identifier node) {
    final value = environment.get(node.name);
    if (value == null) {
      // Log environment ID on failure
      Logger.debug(
          "[visitIdentifier] Failed to find '${node.name}' in env: ${environment.hashCode}");
      throw RuntimeError("Undefined variable: ${node.name}");
    }
    return value;
  }

  bool _isInCatchBlock = false;

  InternalInterpreterException? _originalCaughtInternalExceptionForRethrow;

  @override
  Object? visitTryStatement(TryStatement node) {
    // Store the internal exception if caught
    InternalInterpreterException? caughtInternalException;
    StackTrace? caughtStackTrace;

    Object? tryResult;
    Object? returnValue; // Store either the try result or the catch result

    final originalEnv = environment; // Save to restore after catch/finally

    try {
      // 1. Execute the try block
      Logger.debug("[TryStatement] Entering try block");
      tryResult = node.body.accept<Object?>(this);
      returnValue = tryResult; // Default value if no exception
      Logger.debug("[TryStatement] Try block completed normally");
    } on ReturnException {
      // If the try returns, the finally must execute, but we propagate the return
      Logger.debug(
          "[TryStatement] Propagating ReturnException from try block.");
      rethrow;
    } on BreakException {
      // Propagate for outer loops/switch
      Logger.debug("[TryStatement] Propagating BreakException from try block");
      rethrow;
    } on ContinueException {
      // Propagate for outer loops
      Logger.debug(
          "[TryStatement] Propagating ContinueException from try block");
      rethrow;
    } on InternalInterpreterException catch (e, s) {
      // Catch ONLY the exceptions already encapsulated (coming from a 'throw')
      Logger.debug(
          "[TryStatement] Caught internal exception in try block: ${e.originalThrownValue}");
      caughtInternalException = e; // Store the internal exception
      caughtStackTrace = s;
      returnValue = null; // No normal try result
    } catch (userException, userStack) {
      // Catch any other exception (potentially native)
      Logger.debug(
          "[TryStatement] Caught unexpected non-InternalInterpreterException in TRY: $userException");
      // Encapsulate the user/native exception in our internal type
      caughtInternalException = InternalInterpreterException(userException);
      caughtStackTrace = userStack;
      returnValue = null;
    }

    // 2. Execute the catch blocks (if an internal exception was raised AND stored)
    if (caughtInternalException != null) {
      // Use the ORIGINAL value from the internal exception for checks
      final originalThrownValue = caughtInternalException.originalThrownValue;

      Logger.debug(
          "[TryStatement] Looking for catch clauses for thrown value: ${stringify(originalThrownValue)} (type: ${originalThrownValue?.runtimeType})");

      for (final clause in node.catchClauses) {
        bool typeMatch = false;
        String? targetCatchTypeName;

        // Type check (on Type)
        if (clause.exceptionType == null) {
          // No 'on Type' clause, matches anything
          typeMatch = true;
          Logger.debug("[TryStatement] Catch clause matches any type.");
        } else {
          final typeNode = clause.exceptionType!;
          if (typeNode is NamedType) {
            targetCatchTypeName = typeNode.name2.lexeme;
            Logger.debug(
                "[TryStatement] Checking catch clause for type: $targetCatchTypeName");

            // Use originalThrownValue for type checking
            switch (targetCatchTypeName) {
              case 'int':
                typeMatch = originalThrownValue is int;
                break;
              case 'double':
                typeMatch = originalThrownValue is double;
                break;
              case 'num':
                typeMatch = originalThrownValue is num;
                break;
              case 'String':
                typeMatch = originalThrownValue is String;
                break;
              case 'bool':
                typeMatch = originalThrownValue is bool;
                break;
              case 'List':
                typeMatch = originalThrownValue is List;
                break;
              case 'Null':
                // This is tricky. 'on Null' might not be common.
                // Check if the original value is null.
                typeMatch = originalThrownValue == null;
                break;
              case 'Object':
                // Everything non-null is an Object?
                // Dart's 'on Object' catches non-null exceptions.
                typeMatch = originalThrownValue != null;
                break;
              case 'dynamic': // 'on dynamic' catches everything, like no 'on' clause
                typeMatch = true;
                break;
              case 'void': // Cannot catch on void
                typeMatch = false;
                break;
              default:
                // User-defined type
                try {
                  final targetType = environment.get(targetCatchTypeName);
                  if (targetType is InterpretedClass) {
                    // Check if the ORIGINAL thrown value is an instance of the target type
                    if (originalThrownValue is InterpretedInstance) {
                      typeMatch =
                          originalThrownValue.klass.isSubtypeOf(targetType);
                      Logger.debug(
                          "[TryStatement]   Checking instance '${originalThrownValue.klass.name}' against class '$targetCatchTypeName'. Result: $typeMatch");
                    } else {
                      // Native value cannot be subtype of user-defined class
                      typeMatch = false;
                      Logger.debug(
                          "[TryStatement]   Thrown value is native (${originalThrownValue?.runtimeType}), cannot match user class '$targetCatchTypeName'.");
                    }
                  } else {
                    // Target type name resolved, but it's not an InterpretedClass
                    typeMatch = false;
                    Logger.warn(
                        "[TryStatement] Catch clause type '$targetCatchTypeName' not found or not a class/mixin.");
                  }
                } catch (e) {
                  // Error resolving targetCatchTypeName
                  Logger.warn(
                      "[TryStatement] Error resolving catch clause type '$targetCatchTypeName': $e");
                  typeMatch = false;
                }
            }
          } else {
            // Handle other type nodes like FunctionType if necessary
            Logger.warn(
                "[TryStatement] Unsupported catch clause type node: ${clause.exceptionType.runtimeType}");
            typeMatch = false;
          }
        }

        if (typeMatch) {
          Logger.debug(
              "[TryStatement] Found matching catch clause${targetCatchTypeName != null ? ' for type $targetCatchTypeName' : ''}.");
          final exceptionParameterName = clause.exceptionParameter?.name.lexeme;
          final stackTraceParameterName =
              clause.stackTraceParameter?.name.lexeme;

          // Create an environment for the catch block
          environment =
              originalEnv; // Restore the environment before creating the catch environment
          final catchEnv = Environment(enclosing: environment);
          if (exceptionParameterName != null) {
            // Define with the ORIGINAL thrown value
            catchEnv.define(exceptionParameterName, originalThrownValue);
            Logger.debug(
                "[TryStatement] Defined exception var '$exceptionParameterName' with original value: ${stringify(originalThrownValue)}");
          }
          if (stackTraceParameterName != null) {
            // Store the textual representation of the stack trace
            // Ensure caughtStackTrace is not null before calling toString()
            final stackTraceString =
                caughtStackTrace?.toString() ?? "Stack trace unavailable";
            catchEnv.define(stackTraceParameterName, stackTraceString);
            Logger.debug(
                "[TryStatement] Defined stacktrace var '$stackTraceParameterName'."); // Don't print full trace here
          }

          // Execute the catch block in its environment
          environment = catchEnv;
          _isInCatchBlock = true;
          _originalCaughtInternalExceptionForRethrow =
              caughtInternalException; // Store the internal exception for potential rethrow
          //
          try {
            Logger.debug("[TryStatement] Entering catch block body");
            returnValue = clause.body.accept<Object?>(this);
            Logger.debug("[TryStatement] Catch block completed normally");
            // The exception is handled, clear caughtInternalException to not rethrow it after finally
            caughtInternalException = null;
          } on ReturnException {
            // The catch made a return, we propagate it immediately but the finally must execute
            Logger.debug(
                "[TryStatement] Caught ReturnException in CATCH block");
            // IMPORTANT: Clean the rethrow state BEFORE rethrowing
            _isInCatchBlock = false;
            _originalCaughtInternalExceptionForRethrow = null;
            rethrow; // IMPORTANT: Ensure the return ends the function
          } on InternalInterpreterException catch (catchInternalError, catchStack) {
            if (identical(catchInternalError,
                _originalCaughtInternalExceptionForRethrow)) {
              // This is the exception rethrown by 'rethrow'. It must be allowed to propagate.
              Logger.debug(
                  "[TryStatement] Identified rethrown exception. Propagating.");
              // IMPORTANT: Clean the rethrow state BEFORE rethrowing
              _isInCatchBlock = false;
              _originalCaughtInternalExceptionForRethrow = null;
              rethrow; // Relaunch to let the outer mechanism handle it
            } else {
              // This is a NEW internal exception coming from the catch body.
              Logger.debug(
                  "[TryStatement] Caught NEW internal exception in CATCH block: ${catchInternalError.originalThrownValue}");
              caughtInternalException =
                  catchInternalError; // The new internal exception replaces the old one
              caughtStackTrace = catchStack; // Update stack trace too
              // The new exception is NOT handled by this try/catch
              returnValue = null;
            }
          } catch (nativeError, nativeStack) {
            // Catch other unexpected errors from catch block
            Logger.debug(
                "[TryStatement] Caught unexpected non-InternalInterpreterException in CATCH: $nativeError");
            // Wrap it as InternalInterpreterException to propagate
            caughtInternalException = InternalInterpreterException(nativeError);
            caughtStackTrace = nativeStack;
            returnValue = null;
          } finally {
            // IMPORTANT: Clean the rethrow state if we exit the catch
            _isInCatchBlock = false;
            _originalCaughtInternalExceptionForRethrow = null;
            environment =
                originalEnv; // Restore the environment after the catch
          }
          // Exit the for loop of catch clauses, because we found a match
          break;
        } else {
          Logger.debug(
              "[TryStatement] Skipping catch clause (type mismatch: needed $targetCatchTypeName, got ${originalThrownValue?.runtimeType})");
        }
      } // fin boucle for catchClauses
    } // fin if (caughtInternalException != null)

    // 3. Execute the finally block (always)
    // Store potential exception from finally block (must be internal type now)
    InternalInterpreterException? finallyInternalException;

    if (node.finallyBlock != null) {
      environment = originalEnv; // Ensure we are in the correct environment
      Logger.debug("[TryStatement] Entering finally block");
      try {
        node.finallyBlock!.accept<Object?>(this);
        Logger.debug("[TryStatement] Finally block completed normally");
      } on ReturnException {
        // If finally returns, it overrides everything
        Logger.debug("[TryStatement] Caught ReturnException in FINALLY block");
        rethrow; // The return of the finally is the final value
      } on InternalInterpreterException catch (e) {
        // Catch internal exceptions coming from finally (throw/rethrow in finally)
        Logger.debug(
            "[TryStatement] Caught internal exception in FINALLY block: ${e.originalThrownValue}");
        // The internal exception of the finally prevails
        finallyInternalException = e; // Store internal exception
        // We might want to store the stack trace too if needed later
      } catch (e) {
        // Catch other unexpected errors from finally block
        Logger.debug(
            "[TryStatement] Caught unexpected non-InternalInterpreterException in FINALLY: $e");
        // Wrap it as InternalInterpreterException
        finallyInternalException = InternalInterpreterException(e);
      }
    }

    // 4. Déterminer le résultat final
    if (finallyInternalException != null) {
      Logger.debug(
          "[TryStatement] Rethrowing internal exception from FINALLY: ${finallyInternalException.originalThrownValue}");
      throw finallyInternalException; // The internal exception of the Finally always prevails
    }

    // If there is an unhandled internal exception (either original, or from a catch) and no exception from the finally
    if (caughtInternalException != null /* && !exceptionHandled */) {
      // Note: If it was handled, caughtInternalException was set to null inside the matching catch block.
      // So, if caughtInternalException is still non-null here, it means it wasn't handled.
      Logger.debug(
          "[TryStatement] Rethrowing unhandled internal exception from TRY/CATCH: ${caughtInternalException.originalThrownValue}");
      throw caughtInternalException;
    }

    // Otherwise, return the value (either from the try, or from the catch that handled the exception)
    // Note: if a catch made a return, it was already propagated by the 'rethrow' above.
    Logger.debug("[TryStatement] Exiting normally, returning: $returnValue");
    return returnValue;
  }

  @override
  Object? visitThrowExpression(ThrowExpression node) {
    // 1. Evaluate the expression that is thrown
    final thrownValue = node.expression.accept<Object?>(this);

    // 2. Create and throw an InternalInterpreterException.
    final message = stringify(thrownValue); // Keep for debug log
    Logger.debug("[ThrowExpression] Throwing (original value): $message");
    // Throw the specific internal exception, wrapping the original value
    throw InternalInterpreterException(thrownValue);
    // We don't capture stack trace here, the 'catch' block does it.
  }

  @override
  Object? visitRethrowExpression(RethrowExpression node) {
    final asyncState = currentAsyncState;
    if (asyncState == null) {
      if (!_isInCatchBlock ||
          _originalCaughtInternalExceptionForRethrow == null) {
        throw RuntimeError("'rethrow' can only be used within a catch block.");
      }
      Logger.debug(
          "[Rethrow] Rethrowing original internal exception: ${_originalCaughtInternalExceptionForRethrow!.originalThrownValue}");
      // Re-launch the *original internal exception* that was caught by the enclosing catch block
      throw _originalCaughtInternalExceptionForRethrow!;
    }
    if (!asyncState.isHandlingErrorForRethrow) {
      throw RuntimeError("'rethrow' can only be used within a catch block.");
    }
    final originalError = asyncState.originalErrorForRethrow;
    if (originalError == null) {
      // Should not happen if isHandlingErrorForRethrow is true, but safety check
      throw StateError("Internal error: Inconsistent state for rethrow.");
    }
    Logger.debug(
        "[Rethrow] Rethrowing original internal exception: ${originalError.originalThrownValue}");
    // Relaunch the original exception stored in the async state
    throw originalError;
  }

  @override
  Object? visitIsExpression(IsExpression node) {
    final expressionValue = node.expression.accept<Object?>(this);
    final typeNode = node.type;
    bool result = false;

    if (typeNode is NamedType) {
      final typeName = typeNode.name2.lexeme;

      // Handle built-in types first
      switch (typeName) {
        case 'int':
          result = expressionValue is int;
          break;
        case 'double':
          result = expressionValue is double;
          break;
        case 'num':
          result = expressionValue is num;
          break;
        case 'String':
          result = expressionValue is String;
          break;
        case 'bool':
          result = expressionValue is bool;
          break;
        case 'List':
          result = expressionValue is List;
          break;
        case 'Map':
          result = expressionValue is Map;
          break;
        case 'Null':
          result = expressionValue == null;
          break;
        case 'Object':
          // Everything non-null is an Object?
          result = expressionValue != null;
          break;
        case 'dynamic': // 'is dynamic' is always true
        case 'void': // 'is void' is always false (or error?) - let's say false
          result = typeName == 'dynamic';
          break;
        default:
          try {
            final targetType = environment.get(typeName);

            if (targetType is BridgedClass) {
              if (expressionValue is BridgedInstance) {
                // Use the new helper method
                result = expressionValue.bridgedClass.isSubtypeOf(targetType);
              } else {
                // A non-instance value cannot be a subtype of a user-defined class
                result = false;
              }
            } else if (targetType is InterpretedClass) {
              if (expressionValue is InterpretedInstance) {
                // Use the new helper method
                result = expressionValue.klass.isSubtypeOf(targetType);
              } else {
                // A non-instance value cannot be a subtype of a user-defined class
                result = false;
              }
            } else if (targetType is NativeFunction &&
                targetType.call(this, []) is Type) {
              final object = targetType.call(this, []);

              return expressionValue.runtimeType == object;
            } else {
              throw RuntimeError(
                  "Type '$typeName' not found or is not a ${expressionValue.runtimeType}.");
            }
          } on RuntimeError catch (e) {
            // Propagate type lookup error
            // Wrap in InternalInterpreterException to be caught correctly
            throw InternalInterpreterException(
                RuntimeError("Type check failed: ${e.message}"));
          }
      }
    } else {
      // Handle FunctionType, etc., later if needed
      throw UnimplementedError(
          'Type check for ${typeNode.runtimeType} not implemented.');
    }

    // Handle negation (is!)
    if (node.notOperator != null) {
      return !result;
    } else {
      return result;
    }
  }

  @override
  Object? visitSetOrMapLiteral(SetOrMapLiteral node) {
    bool isMap;
    bool typeExplicit = false;

    // Check explicit type arguments first
    if (node.typeArguments != null &&
        node.typeArguments!.arguments.isNotEmpty) {
      isMap = node.typeArguments!.arguments.length == 2;
      typeExplicit = true;
      Logger.debug(
          "[SetOrMapLiteral] Determined type via explicit args: isMap = $isMap");
    } else {
      // No explicit types, infer from content
      isMap = false; // Default to Set if unsure initially
      bool onlySpreads = true;
      CollectionElement? firstEffectiveElement;

      for (final element in node.elements) {
        if (element is MapLiteralEntry) {
          isMap = true;
          onlySpreads = false;
          firstEffectiveElement = element;
          Logger.debug(
              "[SetOrMapLiteral] Determined isMap = true (found MapLiteralEntry).");
          break; // Found a map entry, definitely a map
        }
        if (element is! SpreadElement && firstEffectiveElement == null) {
          firstEffectiveElement = element;
          onlySpreads = false;
          // If it's an Expression, isMap remains false (it's a Set)
          if (element is Expression) {
            Logger.debug(
                "[SetOrMapLiteral] Determined isMap = false (found first non-spread Expression).");
          }
        }
        if (element is! SpreadElement) {
          onlySpreads = false;
        }
      }

      // Handle empty literal or spread-only literal
      if (!typeExplicit) {
        // Re-evaluate if type wasn't explicit
        if (node.elements.isEmpty) {
          isMap = true; // Empty literal defaults to Map
          Logger.debug(
              "[SetOrMapLiteral] Determined isMap = true (empty literal).");
        } else if (onlySpreads) {
          // Only spread elements, no explicit type args. Infer from first spread.
          Logger.debug(
              "[SetOrMapLiteral] Only spreads found. Inferring type from first spread element.");
          final firstSpread = node.elements.first as SpreadElement;
          final spreadValue = firstSpread.expression.accept<Object?>(this);
          // Check if the evaluated spread value is a Map or looks like one
          final bridgedInstance = toBridgedInstance(spreadValue);
          if (spreadValue is Map ||
              (bridgedInstance.$2 && bridgedInstance.$1?.nativeObject is Map)) {
            isMap = true;
            Logger.debug(
                "[SetOrMapLiteral]   First spread evaluated to Map. Setting isMap = true.");
          } else {
            isMap = false; // Assume Set otherwise
            Logger.debug(
                "[SetOrMapLiteral]   First spread did not evaluate to Map. Setting isMap = false.");
          }
        } else if (firstEffectiveElement is MapLiteralEntry) {
          isMap = true; // Confirmation if first non-spread was entry
        } else {
          isMap = false; // If first non-spread was Expression, it's a Set
        }
      }
    }

    // Create and populate the collection
    final Object collection = isMap ? <Object?, Object?>{} : <Object?>{};

    for (final element in node.elements) {
      try {
        _processCollectionElement(element, collection, isMap: isMap);
      } on RuntimeError catch (e) {
        final literalType = isMap ? "Map" : "Set";
        // Check if error already contains context to avoid duplication
        if (!e.message.contains('in $literalType literal')) {
          throw RuntimeError("${e.message} (in $literalType literal)");
        } else {
          rethrow; // Rethrow original error with context
        }
      }
    }

    return collection;
  }

  // Resolve TypeAnnotation AST node to RuntimeType
  RuntimeType _resolveTypeAnnotation(TypeAnnotation? typeNode,
      {bool isAsync = false}) {
    return _resolveTypeAnnotationWithEnvironment(typeNode, environment,
        isAsync: isAsync);
  }

  // Resolve TypeAnnotation AST node to RuntimeType using a specific environment
  RuntimeType _resolveTypeAnnotationWithEnvironment(
      TypeAnnotation? typeNode, Environment env,
      {bool isAsync = false}) {
    if (typeNode == null) {
      return BridgedClass(nativeType: dynamic, name: 'dynamic');
    }
    if (typeNode is NamedType) {
      final typeName = isAsync
          ? typeNode
              .toSource()
              .replaceAll('?', '')
              .substringAfter('<')
              .substringBeforeLast('>')
          : typeNode.name2.lexeme;
      if (typeName == "void") {
        return BridgedClass(nativeType: VoidType, name: 'void');
      }
      Logger.debug("[ResolveType] Resolving NamedType: $typeName");
      try {
        final resolved = env.get(typeName);
        if (resolved is RuntimeType) {
          Logger.debug(
              "[ResolveType]   Resolved to RuntimeType: ${resolved.name}");
          return resolved;
        } else {
          throw RuntimeError(
              "Symbol '$typeName' resolved to non-type value: $resolved");
        }
      } on RuntimeError {
        // Handle special case: 'dynamic' type doesn't exist in environment usually
        if (typeName == 'dynamic') {
          Logger.debug("[ResolveType]   Resolved to dynamic (special case)");
          // Need a representation for dynamic. Using a placeholder for now.
          // Ideally, have a predefined DynamicRuntimeType() instance.
          return BridgedClass(
              nativeType: Object, name: 'dynamic'); // Corrected placeholder
        }
        throw RuntimeError("Type '$typeName' not found.");
      }
    } else {
      Logger.error(
          "[ResolveType] Unsupported TypeAnnotation type: ${typeNode.runtimeType}");
      throw UnimplementedError(
          "Type resolution for ${typeNode.runtimeType} not implemented yet.");
    }
  }

  @override
  Object? visitInstanceCreationExpression(InstanceCreationExpression node) {
    final constructorNameNode = node.constructorName.type;
    final constructorName =
        constructorNameNode.name2.lexeme; // Name of the class
    final namedConstructorPart = node
        .constructorName.name?.name; // Name of the named constructor (or null)

    Logger.debug(
        "[InstanceCreation] Creating instance of '$constructorName'${namedConstructorPart != null ? '.$namedConstructorPart' : ''}");

    // Resolve the type
    Object? typeValue;
    try {
      typeValue = environment.get(constructorName);
    } on RuntimeError {
      throw RuntimeError(
          "Type '$constructorName' not found for instantiation.");
    }

    // Check the resolved type
    if (typeValue is InterpretedClass) {
      // CASE 1: InterpretedClass
      final klass = typeValue;
      Logger.debug(
          "[InstanceCreation]   Type resolved to InterpretedClass: '$constructorName'");

      // Check if the class is abstract
      if (klass.isAbstract) {
        throw RuntimeError(
            "Cannot instantiate abstract class '$constructorName'.");
      }

      // Evaluate the arguments
      final evaluationResult = _evaluateArgumentsAsync(node.argumentList);
      if (evaluationResult is AsyncSuspensionRequest) {
        return evaluationResult; // Propagate suspension
      }
      final (positionalArgs, namedArgs) =
          evaluationResult as (List<Object?>, Map<String, Object?>);

      // Find and call the constructor (interpreted)
      final constructorLookupName =
          namedConstructorPart ?? ''; // Use '' for default
      final constructor = klass.findConstructor(constructorLookupName);

      if (constructor == null) {
        throw RuntimeError(
            "Class '$constructorName' does not have a constructor named '$constructorLookupName'.");
      }

      try {
        // Evaluate the type arguments
        List<RuntimeType>? evaluatedTypeArguments;
        final typeArgsNode = node.constructorName.type.typeArguments;
        if (typeArgsNode != null) {
          evaluatedTypeArguments = typeArgsNode.arguments
              .map((typeNode) => _resolveTypeAnnotation(typeNode))
              .toList();
        }

        // Handle factory constructors differently from regular constructors
        if (constructor.isFactory) {
          // Factory constructors don't need a pre-created instance
          // They are responsible for creating and returning their own instance
          Logger.debug(
              "[InstanceCreation] Calling factory constructor '$constructorLookupName'");

          // Call the factory constructor directly without creating an instance first
          // The factory will create its own instance and return it
          final result = constructor.call(
              this, positionalArgs, namedArgs, evaluatedTypeArguments);

          // Factory constructors should return an instance of the expected type
          if (result is InterpretedInstance && result.klass == klass) {
            return result;
          } else if (result is InterpretedInstance) {
            throw RuntimeError(
                "Factory constructor '$constructorLookupName' returned an instance of '${result.klass.name}' but expected '$constructorName'.");
          } else {
            throw RuntimeError(
                "Factory constructor '$constructorLookupName' must return an instance, but returned ${result?.runtimeType}.");
          }
        } else {
          // Regular constructors: create instance first, then call constructor
          Logger.debug(
              "[InstanceCreation] Calling regular constructor '$constructorLookupName'");

          // Create and initialize the fields, passing the type arguments
          final instance =
              klass.createAndInitializeInstance(this, evaluatedTypeArguments);
          // Bind 'this' and call the constructor logic
          final boundConstructor = constructor.bind(instance);
          boundConstructor.call(
              this, positionalArgs, namedArgs, evaluatedTypeArguments);
          // The constructor call returns the instance
          return instance;
        }
      } on ReturnException catch (e) {
        // Handle return exceptions (applies to both factory and regular constructors)
        if (constructor.isFactory) {
          // For factory constructors, the return value is the actual result
          return e.value;
        } else {
          // For regular constructors, check if returned value is valid
          if (e.value != null && e.value is InterpretedInstance) {
            final instance = e.value as InterpretedInstance; // Explicit cast
            if (instance.klass == klass) {
              // Check on the casted instance
              return instance;
            }
          }
          // If the condition fails (null, not InterpretedInstance, or wrong class)
          throw RuntimeError(
              "Constructor return value error for '$constructorName'.");
        }
      } on RuntimeError catch (e) {
        // Simplified error message
        throw RuntimeError(
            "Constructor execution error for '$constructorName.': ${e.message}");
      }
    } else if (typeValue is BridgedClass) {
      // CASE 2: BridgedClass
      final bridgedClass = typeValue;
      Logger.debug(
          "[InstanceCreation]   Type resolved to BridgedClass: '$constructorName'");

      final (positionalArgs, namedArgs) = _evaluateArguments(node.argumentList);

      // Find the constructor adapter (bridged)
      final constructorLookupName =
          namedConstructorPart ?? ''; // Use '' if null
      final constructorAdapter =
          bridgedClass.findConstructorAdapter(constructorLookupName);

      if (constructorAdapter == null) {
        throw RuntimeError(
            "Bridged class '$constructorName' does not have a registered constructor named '$constructorLookupName'. Check bridge definition.");
      }

      // Call the constructor adapter
      try {
        // The adapter is responsible for:
        // 1. Converting the interpreted positionalArgs/namedArgs to native types.
        // 2. Calling the actual native constructor.
        // 3. Returning the created native object.
        final nativeObject =
            constructorAdapter(this, positionalArgs, namedArgs);

        // Check if the adapter returned a value (it should)
        if (nativeObject == null) {
          throw RuntimeError(
              "Bridged constructor adapter for '\$constructorName.$constructorLookupName' returned null unexpectedly.");
        }

        // Wrap the native object in BridgedInstance
        final bridgedInstance = BridgedInstance(bridgedClass, nativeObject);
        Logger.debug(
            "[InstanceCreation]   Successfully created BridgedInstance wrapping native object: \${nativeObject.runtimeType}");
        return bridgedInstance;
      } on RuntimeError catch (e) {
        // If the adapter itself raises a RuntimeError (e.g. conversion failure)
        throw RuntimeError(
            "Error during bridged constructor '$constructorLookupName' for class '$constructorName': ${e.message}");
      } catch (e) {
        // Catch potential native exceptions raised by the adapter or the native constructor
        Logger.error(
            "[InstanceCreation] Native exception during bridged constructor '$constructorName.$constructorLookupName': \$e\\n\$s");
        // Encapsulate the native error in a RuntimeError for propagation
        throw RuntimeError(
            "Native error during bridged constructor '$constructorLookupName' for class '$constructorName': \$e");
      }
    } else {
      // CASE 3: The resolved type is neither InterpretedClass nor BridgedClass
      throw RuntimeError(
          "Identifier '$constructorName' resolved to ${typeValue?.runtimeType}, which is not a class type that can be instantiated.");
    }
  }

  (List<Object?>, Map<String, Object?>) _evaluateArguments(
      ArgumentList argumentList) {
    List<Object?> positionalArgs = [];
    Map<String, Object?> namedArgs = {};
    bool namedArgsEncountered = false;

    for (final arg in argumentList.arguments) {
      if (arg is NamedExpression) {
        namedArgsEncountered = true;
        final name = arg.name.label.name;
        final value = arg.expression.accept<Object?>(this);

        // Check for async suspension in named arguments
        if (value is AsyncSuspensionRequest) {
          return value as dynamic; // Propagate suspension request
        }

        if (namedArgs.containsKey(name)) {
          throw RuntimeError("Named argument '$name' provided more than once.");
        }
        final bridgedInstance = toBridgedInstance(value);
        namedArgs[name] = _bridgeInterpreterValueToNative(
            bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : value);
      } else {
        if (namedArgsEncountered) {
          throw RuntimeError(
              "Positional arguments cannot follow named arguments.");
        }
        final a = arg.accept<Object?>(this);

        // Check for async suspension in positional arguments
        if (a is AsyncSuspensionRequest) {
          return a as dynamic; // Propagate suspension request
        }

        final bridgedInstance = toBridgedInstance(a);
        positionalArgs.add(_bridgeInterpreterValueToNative(
            bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : a));
      }
    }

    return (positionalArgs, namedArgs);
  }

  Object? _bridgeInterpreterValueToNative(Object? interpreterValue) {
    if (interpreterValue is BridgedInstance) {
      return interpreterValue.nativeObject;
    }

    if (interpreterValue is BridgedEnumValue) {
      return interpreterValue.nativeValue;
    }

    return interpreterValue;
  }

  /// Evaluates arguments for async function calls, handling await expressions.
  /// Returns either (List&lt;Object?&gt;, Map&lt;String, Object?&gt;) or AsyncSuspensionRequest.
  Object? _evaluateArgumentsAsync(ArgumentList argumentList) {
    List<Object?> positionalArgs = [];
    Map<String, Object?> namedArgs = {};
    bool namedArgsEncountered = false;

    for (final arg in argumentList.arguments) {
      if (arg is NamedExpression) {
        namedArgsEncountered = true;
        final name = arg.name.label.name;
        final value = arg.expression.accept<Object?>(this);

        // Check for async suspension in named arguments
        if (value is AsyncSuspensionRequest) {
          Logger.debug(
              "[_evaluateArgumentsAsync] Async suspension in named argument '$name'");
          return value; // Propagate suspension request
        }

        if (namedArgs.containsKey(name)) {
          throw RuntimeError("Named argument '$name' provided more than once.");
        }
        final bridgedInstance = toBridgedInstance(value);
        namedArgs[name] = _bridgeInterpreterValueToNative(
            bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : value);
      } else {
        if (namedArgsEncountered) {
          throw RuntimeError(
              "Positional arguments cannot follow named arguments.");
        }
        final a = arg.accept<Object?>(this);

        // Check for async suspension in positional arguments
        if (a is AsyncSuspensionRequest) {
          Logger.debug(
              "[_evaluateArgumentsAsync] Async suspension in positional argument");
          return a; // Propagate suspension request
        }

        final bridgedInstance = toBridgedInstance(a);
        positionalArgs.add(_bridgeInterpreterValueToNative(
            bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : a));
      }
    }

    Logger.debug(
        "[_evaluateArgumentsAsync] All arguments evaluated successfully: ${positionalArgs.length} positional, ${namedArgs.length} named");
    return (positionalArgs, namedArgs);
  }

  // Add FunctionExpressionInvocation handler
  @override
  Object? visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
    // 1. Evaluate the function expression itself.
    // This should result in a Callable (like InterpretedFunction or NativeFunction).
    final calleeValue = node.function.accept<Object?>(this);

    // 2. Evaluate arguments (shared logic).
    final (positionalArgs, namedArgs) = _evaluateArguments(node.argumentList);

    // 3. Evaluate type arguments (shared logic).
    List<RuntimeType>? evaluatedTypeArguments;
    final typeArgsNode = node.typeArguments;
    if (typeArgsNode != null) {
      evaluatedTypeArguments = typeArgsNode.arguments
          .map((typeNode) => _resolveTypeAnnotation(typeNode))
          .toList();
    }

    // 4. Check if it's actually callable (standard callable).
    if (calleeValue is Callable) {
      // 5. Perform the standard call.
      try {
        return calleeValue.call(
            this, positionalArgs, namedArgs, evaluatedTypeArguments);
      } on ReturnException catch (e) {
        return e.value;
      }
    } else {
      // Try Extension 'call' Method
      const methodName = 'call';
      try {
        final extensionMethod =
            environment.findExtensionMember(calleeValue, methodName);

        if (extensionMethod is InterpretedExtensionMethod &&
            !extensionMethod
                .isOperator && // Ensure it's a regular method named 'call'
            !extensionMethod.isGetter &&
            !extensionMethod.isSetter) {
          Logger.debug(
              "[FuncExprInvoke] Found extension method 'call' for type ${calleeValue?.runtimeType}. Calling...");

          // Prepare arguments for extension method:
          // First arg is the receiver (the object being called)
          final extensionPositionalArgs = [calleeValue, ...positionalArgs];

          try {
            // Call the extension method
            return extensionMethod.call(this, extensionPositionalArgs,
                namedArgs, evaluatedTypeArguments);
          } on ReturnException catch (e) {
            return e.value;
          } catch (e) {
            throw RuntimeError("Error executing extension method 'call': $e");
          }
        }
        Logger.debug(
            "[FuncExprInvoke] No suitable extension method 'call' found for type ${calleeValue?.runtimeType}.");
      } on RuntimeError catch (findError) {
        Logger.debug(
            "[FuncExprInvoke] No extension member 'call' found for type ${calleeValue?.runtimeType}. Error: ${findError.message}");
        // Fall through to the final standard error below.
      }

      // Original Error: The expression evaluated did not yield a callable function or an object with a callable 'call' extension.
      throw RuntimeError(
          "Attempted to call something that is not a function and has no 'call' extension method. Got type: ${calleeValue?.runtimeType}");
    }
  }

  @override
  Object? visitConstructorReference(ConstructorReference node) {
    final typeNode = node.constructorName.type;
    final constructorId = node.constructorName.name; // Null for default
    final constructorLookupName = constructorId?.name ?? '';

    // Resolve the class type
    final className = typeNode.name2.lexeme;
    Object? classValue;
    try {
      classValue = environment.get(className);
    } on RuntimeError {
      throw RuntimeError(
          "Type '$className' not found for constructor reference.");
    }

    if (classValue is InterpretedClass) {
      // Find the InterpretedFunction for the constructor
      final constructorFunction =
          classValue.findConstructor(constructorLookupName);
      if (constructorFunction == null) {
        throw RuntimeError(
            "Constructor '$constructorLookupName' not found for class '$className'.");
      }
      // Return the constructor function itself as the tear-off value
      return constructorFunction;
    } else if (classValue is BridgedClass) {
      // Find the adapter (just to check existence)
      final adapter = classValue.findConstructorAdapter(constructorLookupName);
      if (adapter == null) {
        throw RuntimeError(
            "Bridged constructor '$constructorLookupName' not found for class '$className'.");
      }

      throw UnimplementedError(
          "Tear-off for bridged constructors ('$className.$constructorLookupName') is not yet supported.");
    } else {
      throw RuntimeError(
          "Identifier '$className' did not resolve to a class type.");
    }
  }

  @override
  Object? visitFunctionReference(FunctionReference node) {
    // The actual logic is in visiting the underlying function expression
    // (Identifier, PropertyAccess, ConstructorReference, etc.)
    // which should return the Callable/Function object itself.
    return node.function.accept<Object?>(this);
  }

  @override
  Object? visitEmptyStatement(EmptyStatement node) {
    // An empty statement does nothing.
    return null;
  }

  @override
  Object? visitSwitchStatement(SwitchStatement node) {
    final switchValue = node.expression.accept<Object?>(this);
    final switchEnvironment = Environment(enclosing: environment);
    final previousEnvironment = environment;
    environment = switchEnvironment;

    bool matched = false; // Has any case matched the switchValue?
    bool execute =
        false; // Should we execute statements in the current/next section?

    try {
      for (final member in node.members) {
        List<Statement> statementsToExecute = [];

        if (member is SwitchCase) {
          if (!matched) {
            final caseValue = member.expression.accept<Object?>(this);
            Logger.debug(
                "[Switch] Checking legacy case value: $caseValue against $switchValue");
            if (switchValue == caseValue) {
              matched = true;
              execute = true;
              Logger.debug("[Switch] Matched legacy case: $caseValue");
            }
          }
          statementsToExecute = member.statements;
        } else if (member is SwitchPatternCase) {
          // Try explicit cast to potentially help the linter
          final pattern = member.guardedPattern.pattern;
          if (pattern is ConstantPattern) {
            // This handles 'case <constant>:'
            if (!matched) {
              // Access expression from the ConstantPattern
              final caseValue = pattern.expression.accept<Object?>(this);
              Logger.debug(
                  "[Switch] Checking pattern case value: $caseValue against $switchValue");
              if (switchValue == caseValue) {
                matched = true;
                execute = true; // Start executing
                Logger.debug("[Switch] Matched pattern case: $caseValue");
              }
            }
            statementsToExecute = member.statements;
          } else {
            // Handle other pattern types using our improved _matchAndBind function
            if (!matched) {
              // Create a temporary environment for pattern matching in switch
              final tempEnvironment = Environment(enclosing: environment);
              try {
                _matchAndBind(pattern, switchValue, tempEnvironment);
                // If we get here, the pattern matched
                matched = true;
                execute = true;
                // Copy any bound variables to the current environment
                // In a full implementation, we might want to handle variable scoping more carefully
                for (final name in tempEnvironment.values.keys) {
                  try {
                    final value = tempEnvironment.get(name);
                    environment.define(name, value);
                  } catch (e) {
                    // Variable might already exist or other issue, ignore for now
                  }
                }
                Logger.debug(
                    "[Switch] Matched pattern case: ${pattern.runtimeType}");
              } on PatternMatchException catch (e) {
                Logger.debug(
                    "[Switch] Pattern ${pattern.runtimeType} did not match: ${e.message}");
                // Pattern didn't match, continue to next case
              }
            }
            statementsToExecute = member.statements;
          }
        } else if (member is SwitchDefault) {
          Logger.debug("[Switch] Reached default case.");
          if (!matched || execute) {
            // Execute default if no match OR fallthrough
            execute = true;
          }
          statementsToExecute = member.statements;
        } else {
          throw StateError('Unknown switch member type: ${member.runtimeType}');
        }

        // Execute statements if needed (either matched this round or fell through)
        if (execute) {
          Logger.debug(
              "[Switch] Executing statements for matched/fallthrough/default...");
          try {
            for (final statement in statementsToExecute) {
              statement.accept<Object?>(this);
            }
            // Fall-through continues if no break
          } on BreakException catch (e) {
            Logger.debug(
                "[Switch] Caught BreakException (label: ${e.label}) with current labels: $_currentStatementLabels");
            if (e.label == null || _currentStatementLabels.contains(e.label)) {
              // Unlabeled break OR labeled break targeting this switch.
              Logger.debug("[Switch] Breaking switch.");
              execute = false; // Stop execution after this block
              break; // Exit the loop over members
            } else {
              // Labeled break targeting an outer construct.
              Logger.debug("[Switch] Rethrowing outer break...");
              rethrow;
            }
          } on ContinueException {
            // 'continue' without a label is invalid here according to Dart spec
            // Analyzer should catch 'continue L;' if L doesn't label a loop/switch member
            throw RuntimeError(
                "'continue' is not valid inside a switch case/default block without a loop target.");
          }
        }
      }
    } finally {
      environment = previousEnvironment;
    }

    return null; // Switch statements don't produce a value.
  }

  // Await Expression (Partial Support)
  @override
  Object? visitAwaitExpression(AwaitExpression node) {
    // Check for async state machine: do we have an async state?
    if (currentAsyncState == null) {
      // This shouldn't happen if the call is properly orchestrated
      throw StateError(
          "Internal error: 'await' encountered outside of a managed async execution state.");
    }

    // Initial check: Are we in an async function?
    if (!currentAsyncState!.function.isAsync) {
      throw RuntimeError("'await' can only be used inside an async function.");
    }

    // Check if we are in invocation resumption mode
    if (currentAsyncState!.isInvocationResumptionMode) {
      Logger.debug(
          "[AwaitExpression] In invocation resumption mode, returning last await result: ${currentAsyncState!.lastAwaitResult}");
      return currentAsyncState!.lastAwaitResult;
    }

    Logger.debug("[AwaitExpression] Evaluating expression for await...");
    final expressionValue = node.expression.accept<Object?>(this);

    // HANDLING NESTED SUSPENSIONS
    if (expressionValue is AsyncSuspensionRequest) {
      // If the awaited expression itself is an await, just propagate its suspension request.
      Logger.debug(
          "[AwaitExpression] Awaited expression itself suspended. Propagating AsyncSuspensionRequest.");
      return expressionValue;
    }

    if (expressionValue is Future) {
      Logger.debug(
          "[AwaitExpression] Expression evaluated to a Future. Returning AsyncSuspensionRequest.");
      final future = expressionValue as Future<Object?>;

      // CRUCIAL: Return the suspension request with the future and the current state.
      // The async state machine will use this information.
      // Note: currentAsyncState cannot be null here because of the previous check.
      return AsyncSuspensionRequest(future, currentAsyncState!);
    } else {
      // The argument to 'await' MUST be a Future.
      throw RuntimeError(
          "The argument to 'await' must be a Future, but received type: ${expressionValue?.runtimeType}");
    }
  }

  // =======================================================================
  // Pattern Matching Helper
  // =======================================================================

  /// Attempts to match the [pattern] against the [value].
  /// If successful, binds any variables declared in the pattern within the [environment].
  /// Throws [PatternMatchException] on failure.
  void _matchAndBind(
      DartPattern pattern, Object? value, Environment environment) {
    Logger.debug(
        "[_matchAndBind] Matching pattern ${pattern.runtimeType} against value ${value?.runtimeType}");

    if (pattern is DeclaredVariablePattern) {
      // Handles: var x, final T x, int x
      final name = pattern.name.lexeme;
      if (name == '_') {
        // Wildcard name in declaration: match succeeds, no binding
        Logger.debug("[_matchAndBind] Wildcard (declared) match success.");
        return;
      }
      environment.define(name, value);
      Logger.debug("[_matchAndBind] Bound variable '$name' = $value");
    } else if (pattern is WildcardPattern) {
      // Handles: _ when used as a standalone sub-pattern
      Logger.debug("[_matchAndBind] Wildcard (sub-pattern) match success.");
      return; // Match succeeds, no binding
    } else if (pattern is AssignedVariablePattern) {
      // Handles assignment patterns like: (a, _) = record;
      final name = pattern.name.lexeme;
      if (name == '_') {
        // Wildcard name in assignment: match succeeds, no binding
        Logger.debug("[_matchAndBind] Wildcard (assigned) match success.");
        return;
      }
      try {
        environment.assign(name, value);
        Logger.debug("[_matchAndBind] Assigned variable '$name' = $value");
      } on RuntimeError catch (e) {
        // Convert assignment errors (e.g., variable not defined) to PatternMatchException
        throw PatternMatchException(
            "Failed to assign pattern variable '$name': ${e.message}");
      }
    } else if (pattern is ConstantPattern) {
      // Handles: case 1:, case "abc":, case MyClass.constant:
      // Evaluate the constant expression within the pattern
      final patternValue = pattern.expression.accept<Object?>(this);
      // Compare the switch value against the evaluated pattern constant
      // Use simple equality check for now.
      if (value != patternValue) {
        throw PatternMatchException(
            "Constant pattern value $patternValue does not match switch value $value");
      }
      Logger.debug(
          "[_matchAndBind] Constant pattern matched: $value == $patternValue");
      // No binding needed for constant patterns
    } else if (pattern is ListPattern) {
      // Handles: [p1, p2, ...], including rest elements like [p1, ...rest]
      if (value is! List) {
        throw PatternMatchException(
            "Expected a List, but got ${value?.runtimeType}");
      }

      // Check if there's a rest element and handle accordingly
      final restElementIndex =
          pattern.elements.indexWhere((e) => e is RestPatternElement);

      if (restElementIndex == -1) {
        // No rest element: exact length match required
        if (pattern.elements.length != value.length) {
          throw PatternMatchException(
              "List pattern expected ${pattern.elements.length} elements, but List has ${value.length}.");
        }

        // Match subpatterns recursively
        for (int i = 0; i < pattern.elements.length; i++) {
          final element = pattern.elements[i];
          final subValue = value[i];

          // Extract the actual pattern from the element wrapper
          DartPattern subPattern;
          if (element is DartPattern) {
            subPattern = element;
          } else {
            throw StateError(
                "Unexpected ListPatternElement type: ${element.runtimeType}");
          }

          Logger.debug(
              "[_matchAndBind]   Matching list element $i: ${subPattern.runtimeType} against ${subValue?.runtimeType}");
          _matchAndBind(subPattern, subValue, environment);
        }
      } else {
        // Has rest element: more complex matching
        final RestPatternElement restElement =
            pattern.elements[restElementIndex] as RestPatternElement;
        final beforeRestCount = restElementIndex;
        final afterRestCount = pattern.elements.length - restElementIndex - 1;
        final minRequiredLength = beforeRestCount + afterRestCount;

        if (value.length < minRequiredLength) {
          throw PatternMatchException(
              "List pattern expected at least $minRequiredLength elements, but List has ${value.length}.");
        }

        // Match elements before rest
        for (int i = 0; i < beforeRestCount; i++) {
          final element = pattern.elements[i];
          final subValue = value[i];

          DartPattern subPattern;
          if (element is DartPattern) {
            subPattern = element;
          } else {
            throw StateError(
                "Unexpected ListPatternElement type before rest: ${element.runtimeType}");
          }

          Logger.debug(
              "[_matchAndBind]   Matching list element $i (before rest): ${subPattern.runtimeType} against ${subValue?.runtimeType}");
          _matchAndBind(subPattern, subValue, environment);
        }

        // Handle rest element
        final restStartIndex = beforeRestCount;
        final restEndIndex = value.length - afterRestCount;
        final restValues = value.sublist(restStartIndex, restEndIndex);

        if (restElement.pattern != null) {
          // Rest element has a pattern (e.g., ...rest), bind the sublist
          Logger.debug(
              "[_matchAndBind]   Matching rest element: ${restElement.pattern!.runtimeType} against List of ${restValues.length} elements");
          _matchAndBind(restElement.pattern!, restValues, environment);
        }
        // If restElement.pattern is null, it's just "..." (anonymous rest), no binding needed

        // Match elements after rest
        for (int i = 0; i < afterRestCount; i++) {
          final patternIndex = restElementIndex + 1 + i;
          final valueIndex = value.length - afterRestCount + i;
          final element = pattern.elements[patternIndex];
          final subValue = value[valueIndex];

          DartPattern subPattern;
          if (element is DartPattern) {
            subPattern = element;
          } else {
            throw StateError(
                "Unexpected ListPatternElement type after rest: ${element.runtimeType}");
          }

          Logger.debug(
              "[_matchAndBind]   Matching list element $valueIndex (after rest): ${subPattern.runtimeType} against ${subValue?.runtimeType}");
          _matchAndBind(subPattern, subValue, environment);
        }
      }
      Logger.debug("[_matchAndBind] List pattern matched successfully.");
    } else if (pattern is MapPattern) {
      // Handles: {'key': p1, 'key2': p2, ...}, including rest elements like {'key': p1, ...rest}
      if (value is! Map) {
        throw PatternMatchException(
            "Expected a Map, but got ${value?.runtimeType}");
      }

      final Set<Object?> matchedKeys = {};

      // Match regular entries first
      for (int i = 0; i < pattern.elements.length; i++) {
        final element = pattern.elements[i];

        if (element is MapPatternEntry) {
          final keyPatternExpr = element.key;
          final valuePattern = element.value;

          final keyToLookup = keyPatternExpr.accept<Object?>(this);

          if (!value.containsKey(keyToLookup)) {
            throw PatternMatchException(
                "Map pattern key '$keyToLookup' not found in the map.");
          }

          final subValue = value[keyToLookup];
          Logger.debug(
              "[_matchAndBind]   Matching map entry '$keyToLookup': ${valuePattern.runtimeType} against ${subValue?.runtimeType}");
          _matchAndBind(valuePattern, subValue, environment);
          matchedKeys.add(keyToLookup);
        } else if (element is RestPatternElement) {
          // Handle rest element
          final remainingEntries = <Object?, Object?>{};
          for (final entry in value.entries) {
            if (!matchedKeys.contains(entry.key)) {
              remainingEntries[entry.key] = entry.value;
            }
          }

          if (element.pattern != null) {
            // Rest element has a pattern (e.g., ...rest), bind the remaining map
            Logger.debug(
                "[_matchAndBind]   Matching rest element: ${element.pattern!.runtimeType} against Map of ${remainingEntries.length} entries");
            _matchAndBind(element.pattern!, remainingEntries, environment);
          }
          // If element.pattern is null, it's just "..." (anonymous rest), no binding needed
        } else {
          throw StateError(
              "Unexpected MapPatternElement type: ${element.runtimeType}");
        }
      }

      Logger.debug("[_matchAndBind] Map pattern matched successfully.");
    } else if (pattern is RecordPattern) {
      Logger.debug(
          '[_matchAndBind] Matching pattern ${pattern.runtimeType} against value ${value.runtimeType}');
      if (value is! InterpretedRecord) {
        Logger.debug(
            'DEBUG [_matchAndBind] Mismatch: Value is not an InterpretedRecord.');
        // Failure case handled by throwing or returning normally if not exhaustive
        // Depending on context (declaration vs refutable)
        // For now, let's assume declaration context (must match or throw)
        throw PatternMatchException(
            'Expected a Record, but got ${value?.runtimeType}'); // Corrected message
      }

      final positionalPatternFields =
          pattern.fields.where((f) => f.name == null).toList();
      final namedPatternFieldsNodes =
          pattern.fields.where((f) => f.name != null).toList();

      Logger.debug(
          '[_matchAndBind]   Pattern positional fields count: ${positionalPatternFields.length}');
      Logger.debug(
          '[_matchAndBind]   Value positional fields count: ${value.positionalFields.length}');
      Logger.debug(
          '[_matchAndBind]   Pattern named fields count: ${namedPatternFieldsNodes.length}');
      Logger.debug(
          '[_matchAndBind]   Value named fields count: ${value.namedFields.length}');

      // Check positional fields count FIRST
      if (positionalPatternFields.length > value.positionalFields.length) {
        // Adjusted error message to match test expectation
        throw RuntimeError(
            'Pattern match failed: Record pattern expected at least ${positionalPatternFields.length} positional fields, but Record only has ${value.positionalFields.length}.');
      }

      // Match positional fields
      for (int i = 0; i < positionalPatternFields.length; i++) {
        final fieldPatternNode = positionalPatternFields[i];
        final fieldValue = value.positionalFields[i];
        Logger.debug(
            'DEBUG [_matchAndBind]   Matching record positional field $i: ${fieldPatternNode.runtimeType} against ${fieldValue?.runtimeType ?? 'null'}');

        // Assume fieldPatternNode IS a RecordPatternField because it came from pattern.fields
        // We need the actual pattern nested within the field.
        // Directly access .pattern assuming the node type is correct (dynamic access if needed)
        final fieldPattern = (fieldPatternNode as dynamic).pattern;

        // Recursive call, rely on exceptions for failure
        _matchAndBind(fieldPattern, fieldValue, environment);
        Logger.debug(
            'DEBUG [_matchAndBind]     Positional field $i match success.');
      }

      // Match named fields
      for (final fieldNode in namedPatternFieldsNodes) {
        // We know fieldNode has name != null here
        // Assume fieldNode is RecordPatternField based on filtering
        final fieldPatternNode = fieldNode;

        // Use the corrected access path
        final fieldName = fieldPatternNode.name?.name?.lexeme;
        if (fieldName == null) {
          // This case implies fieldPatternNode.name was null, which contradicts the filter where((f) => f.name != null)
          // Or fieldPatternNode.name.name was null, which is unlikely for a SimpleIdentifier.
          throw StateError(
              'Internal error: Named field detected but name lexeme is null.');
        }

        Logger.debug(
            'DEBUG [_matchAndBind]   Matching record named field \'$fieldName\': ${fieldPatternNode.pattern.runtimeType} against value type ${value.namedFields[fieldName]?.runtimeType ?? 'null'}');

        if (!value.namedFields.containsKey(fieldName)) {
          throw PatternMatchException(
              'Record pattern named field \'$fieldName\' not found in the record.');
        }
        final fieldValue = value.namedFields[fieldName];

        // Recursive match on the pattern inside the field
        final fieldSubPattern = (fieldPatternNode as dynamic).pattern;
        _matchAndBind(fieldSubPattern, fieldValue, environment);
        Logger.debug(
            'DEBUG [_matchAndBind]     Named field \'$fieldName\' match success.');
      }

      Logger.debug('[_matchAndBind] Record pattern matched successfully.');
      // Success: function completes normally
    } else if (pattern is ObjectPattern) {
      // Handles: ClassName(field1: pattern1, field2: pattern2)
      Logger.debug(
          '[_matchAndBind] Matching object pattern ${pattern.type.name2.lexeme} against value ${value?.runtimeType}');

      // Get the expected type name
      final expectedTypeName = pattern.type.name2.lexeme;

      // Check if the value is of the expected type
      // For now, we'll do basic runtime type checking
      // In a full implementation, this would involve more sophisticated type checking

      // Try to match common Dart types and interpreted classes
      bool typeMatches = false;
      String actualTypeName = value?.runtimeType.toString() ?? 'null';

      // Handle common type aliases and matches
      if (expectedTypeName == 'int' && value is int) {
        typeMatches = true;
      } else if (expectedTypeName == 'double' && value is double) {
        typeMatches = true;
      } else if (expectedTypeName == 'num' && value is num) {
        typeMatches = true;
      } else if (expectedTypeName == 'String' && value is String) {
        typeMatches = true;
      } else if (expectedTypeName == 'bool' && value is bool) {
        typeMatches = true;
      } else if (expectedTypeName == 'List' && value is List) {
        typeMatches = true;
      } else if (expectedTypeName == 'Map' && value is Map) {
        typeMatches = true;
      } else if (expectedTypeName == 'Set' && value is Set) {
        typeMatches = true;
      } else if (value != null && actualTypeName.endsWith(expectedTypeName)) {
        // Basic heuristic: if the actual type name ends with expected type name
        typeMatches = true;
      } else {
        // Check if the value has an interpreted class that matches
        // This is a simplified check - a full implementation would be more robust
        try {
          // Try to look up the expected type in the environment
          final expectedType = environment.get(expectedTypeName);
          if (expectedType is RuntimeType) {
            // In a full implementation, we'd check if value is an instance of expectedType
            typeMatches =
                true; // For now, assume it matches if we found the type
          }
        } catch (e) {
          // Type not found in environment, use basic matching
        }
      }

      if (!typeMatches) {
        throw PatternMatchException(
            "Object pattern expected type '$expectedTypeName', but got '${value?.runtimeType}'");
      }

      // Match each field pattern
      for (final field in pattern.fields) {
        final PatternFieldName? fieldName = field.name;
        final DartPattern fieldPattern = field.pattern;

        if (fieldName == null) {
          throw PatternMatchException("Object pattern field must have a name");
        }

        // Get the field name string
        String fieldNameStr;
        if (fieldName.name != null) {
          fieldNameStr = fieldName.name!.lexeme;
        } else {
          throw PatternMatchException(
              "Object pattern field name is not a simple identifier");
        }

        // Extract the field value from the object
        Object? fieldValue;

        // For basic types, we can't really access fields, so this is limited
        // In a full implementation, this would use reflection or interpreter class instances
        if (value is Map && value.containsKey(fieldNameStr)) {
          // If it's a map, treat field access as key access (common pattern)
          fieldValue = value[fieldNameStr];
        } else {
          // For now, we'll assume field access is not supported for most types
          // A full implementation would use reflection or interpreter-managed objects
          throw PatternMatchException(
              "Object pattern field access '$fieldNameStr' is not yet fully supported for type '${value?.runtimeType}'");
        }

        Logger.debug(
            "[_matchAndBind]   Matching object field '$fieldNameStr': ${fieldPattern.runtimeType} against ${fieldValue?.runtimeType}");
        _matchAndBind(fieldPattern, fieldValue, environment);
      }

      Logger.debug("[_matchAndBind] Object pattern matched successfully.");
    } else {
      throw UnimplementedError(
          "Pattern type not yet supported in _matchAndBind: ${pattern.runtimeType}");
    }
  }

  @override
  Object? visitRecordLiteral(RecordLiteral node) {
    final positional = <Object?>[];
    final named = <String, Object?>{};

    for (final field in node.fields) {
      if (field is NamedExpression) {
        final name = field.name.label.name;
        final value = field.expression.accept<Object?>(this);
        if (named.containsKey(name)) {
          throw RuntimeError(
              "Record literal field '$name' specified more than once.");
        }
        named[name] = value;
      } else {
        // Positional field: expression
        if (named.isNotEmpty) {
          // As per Dart spec, positional fields must come before named fields
          throw RuntimeError(
              "Positional fields must come before named fields in record literal.");
        }
        positional.add(field.accept<Object?>(this));
      }
    }
    Logger.debug("[visitRecordLiteral] Created record: ($positional, $named)");
    return InterpretedRecord(positional, named);
  }

  @override
  Object? visitPatternAssignment(PatternAssignment node) {
    // 1. Evaluate the right-hand side expression
    final rhsValue = node.expression.accept<Object?>(this);

    // 2. Match the pattern against the value and bind variables
    try {
      _matchAndBind(node.pattern, rhsValue, environment);
    } on PatternMatchException catch (e) {
      // Convert pattern match failures into standard RuntimeErrors for assignment expressions
      throw RuntimeError("Pattern assignment failed: ${e.message}");
    } catch (e) {
      // Catch other potential errors during binding
      throw RuntimeError("Error during pattern assignment: $e");
    }

    // 3. Pattern assignment expression evaluates to the RHS value
    return rhsValue;
  }

  @override
  Object? visitSwitchExpression(SwitchExpression node) {
    final switchValue = node.expression.accept<Object?>(this);
    final originalEnvironment = environment; // Backup current environment

    for (final caseExpr in node.cases) {
      final pattern = caseExpr.guardedPattern.pattern;
      final guard = caseExpr.guardedPattern.whenClause?.expression;
      final body = caseExpr.expression;

      // Create a temporary environment for this case's pattern variables
      // Enclose the *original* environment where the switch expression is evaluated
      final caseEnvironment = Environment(enclosing: originalEnvironment);

      try {
        // Attempt to match and bind variables in the temporary environment
        _matchAndBind(pattern, switchValue, caseEnvironment);
        Logger.debug(
            "[SwitchExpr] Pattern ${pattern.runtimeType} matched value ${switchValue?.runtimeType}");

        // Pattern matched, now check the guard (if it exists)
        bool guardPassed = true;
        if (guard != null) {
          final previousVisitorEnv = environment; // Backup
          try {
            environment = caseEnvironment; // Evaluate guard in case scope
            final guardResult = guard.accept<Object?>(this);
            if (guardResult is! bool) {
              throw RuntimeError(
                  "Switch expression 'when' clause must evaluate to a boolean.");
            }
            guardPassed = guardResult;
            Logger.debug("[SwitchExpr] Guard evaluated to: $guardPassed");
          } finally {
            environment = previousVisitorEnv; // Restore
          }
        }

        // If guard passed (or no guard), evaluate and return the body result
        if (guardPassed) {
          Logger.debug("[SwitchExpr] Guard passed or absent. Evaluating body.");

          final previousVisitorEnv = environment; // Backup
          try {
            environment = caseEnvironment; // Evaluate body in case scope
            final result = body.accept<Object?>(this);
            Logger.debug("[SwitchExpr] Body evaluated to: $result. Returning.");
            return result; // Return the result of the matching case's body
          } finally {
            environment = previousVisitorEnv; // Restore
          }
        }
      } on PatternMatchException catch (e) {
        // Pattern didn't match, try the next case
        Logger.debug(
            "[SwitchExpr] Pattern ${pattern.runtimeType} did not match: ${e.message}. Trying next case.");
        continue;
      }
      // If we reach here, it means the pattern matched but the guard failed.
      Logger.debug(
          "[SwitchExpr] Pattern matched but guard failed. Trying next case.");
    } // End of loop through cases

    // If no case matched and returned a value
    throw RuntimeError(
        "Switch expression was not exhaustive for value: $switchValue (${switchValue?.runtimeType})");
  }

  @override
  Object? visitExtensionDeclaration(ExtensionDeclaration node) {
    final extensionName = node.name?.lexeme;
    Logger.debug(
        "[visitExtensionDeclaration] Declaring extension: ${extensionName ?? '<unnamed>'}");

    // 1. Resolve the 'on' type
    final onTypeNode = node.onClause?.extendedType;
    if (onTypeNode == null) {
      // This might happen for extension types, which are different.
      // For now, assume classic extensions always have an 'on' clause.
      Logger.warn(
          "[visitExtensionDeclaration] Extension '${extensionName ?? '<unnamed>'}' has no 'on' clause, skipping.");
      return null;
    }

    // Resolve the type name from the AST node
    String onTypeName;
    if (onTypeNode is NamedType) {
      onTypeName = onTypeNode.name2.lexeme;
    } else {
      Logger.warn(
          "[visitExtensionDeclaration] Unsupported 'on' type node for resolution: ${onTypeNode.runtimeType}. Skipping extension.");
      return null;
    }

    // Look up the RuntimeType in the environment
    RuntimeType onRuntimeType;
    try {
      final typeValue = environment.get(onTypeName);

      // Handle Resolution of Native/Bridged Types
      if (typeValue is RuntimeType) {
        // Standard case: Found an InterpretedClass/Mixin or existing BridgedClass
        onRuntimeType = typeValue;
        Logger.debug(
            "[visitExtensionDeclaration] Resolved 'on' type '$onTypeName' to RuntimeType: ${onRuntimeType.name}");
      } else if (typeValue is NativeFunction) {
        // Heuristic: If environment.get returns a NativeFunction for a common type name,
        // assume it represents the native type and find/create a corresponding BridgedClass.
        // This relies on the global environment being populated correctly with type bridges.
        BridgedClass? bridgedType = _getBridgedClassForNativeType(onTypeName);
        if (bridgedType != null) {
          onRuntimeType = bridgedType;
          Logger.debug(
              "[visitExtensionDeclaration] Resolved native 'on' type '$onTypeName' to BridgedClass: ${onRuntimeType.name}");
        } else {
          // We found something, but couldn't map it to a known bridged type
          throw RuntimeError(
              "Symbol '$onTypeName' resolved to NativeFunction but could not map to a known BridgedClass.");
        }
      } else {
        // Resolved to something unexpected (e.g., an instance, null, etc.)
        throw RuntimeError(
            "Symbol '$onTypeName' resolved to non-type: ${typeValue?.runtimeType}");
      }
    } on RuntimeError catch (e) {
      // Check if the error is specifically "Undefined variable"which means the type wasn't found at all.
      if (e.message.contains("Undefined variable: $onTypeName")) {
        // Special handling for core types that might not be explicitly defined if stdlib wasn't fully loaded?
        // Or maybe they are always NativeFunctions?
        BridgedClass? coreBridgedType =
            _getBridgedClassForNativeType(onTypeName);
        if (coreBridgedType != null) {
          onRuntimeType = coreBridgedType;
          Logger.debug(
              "[visitExtensionDeclaration] Resolved unfound core 'on' type '$onTypeName' to BridgedClass: ${onRuntimeType.name}");
        } else {
          // Type genuinely not found or not a recognized core type
          throw RuntimeError(
              "Could not resolve 'on' type '$onTypeName' for extension '${extensionName ?? '<unnamed>'}': Type not found or not a recognized core type.");
        }
      } else {
        // Propagate other RuntimeErrors (like the non-type error from above)
        throw RuntimeError(
            "Could not resolve 'on' type '$onTypeName' for extension '${extensionName ?? '<unnamed>'}': ${e.message}");
      }
    }

    // 2. Process members (methods, getters, setters, operators)
    final members = <String, Callable>{};
    for (final member in node.members) {
      if (member is MethodDeclaration) {
        final methodName = member
            .name.lexeme; // Operator names like '+', '[]' are also lexemes
        // Create InterpretedExtensionMethod for all method-like declarations
        final function =
            InterpretedExtensionMethod(member, environment, onRuntimeType);
        members[methodName] = function;
        String memberType = "method";
        if (member.isGetter) memberType = "getter";
        if (member.isSetter) memberType = "setter";
        if (member.isOperator) memberType = "operator";
        Logger.debug(
            "[visitExtensionDeclaration]   Added extension $memberType: $methodName");
      } else if (member is FieldDeclaration) {
        // Only static fields are allowed in extensions.
        if (!member.isStatic) {
          Logger.warn(
              "[visitExtensionDeclaration] Instance fields are not allowed in extensions. Skipping field '$member'.");
          continue; // Skip instance fields
        }
        // Handle static fields
        for (final variable in member.fields.variables) {
          final fieldName = variable.name.lexeme;
          Object? value;
          if (variable.initializer != null) {
            // Evaluate static initializer immediately in the current environment
            // (Environment where the extension is declared)
            try {
              value = variable.initializer!.accept<Object?>(this);
            } catch (e) {
              throw RuntimeError(
                  "Error evaluating static initializer for extension field '$fieldName': $e");
            }
          }
          // Define static field directly in the environment, potentially prefixed?
          // Option 1: Prefix with extension name (if named)
          // Option 2: Store directly on InterpretedExtension object?
          // Let's use Option 1 for now for simplicity.
          final staticFieldName =
              "${extensionName ?? '_'}.$fieldName"; // Use '_' if unnamed
          environment.define(staticFieldName, value);
          Logger.debug(
              "[visitExtensionDeclaration]   Defined static field '$staticFieldName' in environment.");
        }
      } else {
        Logger.warn(
            "[visitExtensionDeclaration] Unsupported extension member type: ${member.runtimeType}. Skipping.");
      }
    }

    // 3. Create and store the InterpretedExtension
    final interpretedExtension = InterpretedExtension(
      name: extensionName,
      onType: onRuntimeType,
      members: members,
    );

    // How to store it? In the environment associated with its name?
    // Or in a separate list in the environment?
    // Let's try defining it by name if it has one, otherwise maybe a special list.
    if (extensionName != null) {
      environment.define(extensionName, interpretedExtension);
      Logger.debug(
          "[visitExtensionDeclaration] Defined named extension '$extensionName' in environment.");
    } else {
      // Store unnamed extensions in a special list in the environment
      environment.addUnnamedExtension(interpretedExtension);
      Logger.debug(
          "[visitExtensionDeclaration] Added unnamed extension to environment list.");
    }

    return null; // Declarations typically return null
  }

  BridgedClass? _getBridgedClassForNativeType(String typeName) {
    // This function maps core Dart type names (as strings) to their
    // corresponding BridgedClass representations used by the interpreter.
    // This relies on these BridgedClass instances being available (e.g., created during stdlib setup).
    // We get them from the global environment.
    Object? typeValue;
    try {
      // Use globalEnvironment which should have stdlib types defined.
      typeValue = globalEnvironment.get(typeName);
    } on RuntimeError {
      // Type not found in global environment
      Logger.warn(
          "[_getBridgedClassForNativeType] Type '$typeName' not found in global environment.");
      return null;
    }

    if (typeValue is BridgedClass) {
      // Successfully found the corresponding BridgedClass
      return typeValue;
    } else {
      // Found the name, but it wasn't a BridgedClass (e.g., maybe the NativeFunction constructor)
      Logger.warn(
          "[_getBridgedClassForNativeType] Symbol '$typeName' found in global env but is not a BridgedClass (type: ${typeValue?.runtimeType}).");
      // Special case: Maybe it maps to a fundamental type like Object?
      if (typeName == 'dynamic') {
        try {
          final objectType = globalEnvironment.get('Object');
          if (objectType is BridgedClass) return objectType;
          // ignore: empty_catches
        } on RuntimeError {}
      }
      return null;
    }
  }

  @override
  Object? visitSuperConstructorInvocation(SuperConstructorInvocation node) {
    // 1. Retrieve the 'this' instance currently being created.
    InterpretedInstance? thisInstance;
    try {
      final thisValue = environment.get('this');
      if (thisValue is InterpretedInstance) {
        thisInstance = thisValue;
      } else {
        throw RuntimeError(
            "Internal error: 'this' is not an InterpretedInstance during super constructor call.");
      }
    } on RuntimeError {
      throw RuntimeError(
          "Internal error: Could not find 'this' during super constructor call.");
    }

    // 2. Check that the class has a bridged superclass.
    final currentClass = thisInstance.klass;
    final bridgedSuper = currentClass.bridgedSuperclass;
    if (bridgedSuper == null) {
      // If the superclass is interpreted, this call should be handled differently
      // (standard inherited constructor call).
      // For now, assume that if we get here, it's because of a bridged superclass.
      throw RuntimeError(
          "Cannot call super() constructor: Class '${currentClass.name}' does not have a registered bridged superclass.");
    }

    // 3. Determine the name of the super constructor to call.
    final constructorName =
        node.constructorName?.name ?? ''; // '' for the default

    // 4. Find the constructor adapter for the bridged superclass.
    final constructorAdapter =
        bridgedSuper.findConstructorAdapter(constructorName);
    if (constructorAdapter == null) {
      throw RuntimeError(
          "Bridged superclass '${bridgedSuper.name}' has no constructor named '$constructorName'.");
    }

    // 5. Evaluate the arguments passed to super(...).
    final (positionalArgs, namedArgs) = _evaluateArguments(node.argumentList);

    // 6. Call the constructor adapter.
    Object? nativeSuperObject;
    try {
      nativeSuperObject = constructorAdapter(this, positionalArgs, namedArgs);
      if (nativeSuperObject == null) {
        throw RuntimeError(
            "Bridged super constructor adapter for '${bridgedSuper.name}.$constructorName' returned null.");
      }
    } catch (e, s) {
      Logger.error(
          "Native exception during super constructor call to '${bridgedSuper.name}.$constructorName': $e\n$s");
      throw RuntimeError(
          "Native error during super constructor call '$constructorName': $e");
    }

    // 7. Store the returned native object on the 'this' instance.
    thisInstance.bridgedSuperObject = nativeSuperObject;
    Logger.debug(
        "[SuperConstructorInvocation] Stored native super object (${nativeSuperObject.runtimeType}) on instance of ${currentClass.name}.");

    return null; // The super() call itself doesn't return a value.
  }

  @override
  Object? visitImportDirective(ImportDirective node) {
    final importUriString = node.uri.stringValue;
    if (importUriString == null) {
      Logger.warn(
          "[visitImportDirective] Import directive with null URI string.");
      return null; // Return null if the URI is null
    }

    Logger.debug(
        "[InterpreterVisitor.visitImportDirective] START processing import: $importUriString");

    Uri resolvedUri;
    final importUri = Uri.parse(importUriString);

    if (importUri.isScheme('dart') || importUri.isScheme('package')) {
      resolvedUri = importUri;
      Logger.debug(
          "[visitImportDirective] Using absolute/unresolvable URI: $resolvedUri");
    } else {
      final baseUri = moduleLoader.currentlibrary;
      if (baseUri == null) {
        throw RuntimeError(
            "Unable to resolve relative import '$importUriString': Base URI not defined in ModuleLoader.");
      }
      Logger.debug(
          "[visitImportDirective] Attempting to resolve relative URI '$importUriString' relative to '$baseUri'");
      resolvedUri = baseUri.resolveUri(importUri);
      Logger.debug(
          "[visitImportDirective] Resolved relative URI: $resolvedUri");
    }

    final prefixIdentifier = node.prefix; // Get the prefix identifier
    final prefixName = prefixIdentifier?.name;
    Logger.debug(
        "[visitImportDirective] Loading module for resolved URI: $resolvedUri (prefix: $prefixName)");

    LoadedModule loadedModule = moduleLoader.loadModule(resolvedUri);

    // Extract the show/hide combinators from the import directive
    Set<String>? showNames;
    Set<String>? hideNames;

    for (final combinator in node.combinators) {
      if (combinator is ShowCombinator) {
        showNames ??= {};
        showNames.addAll(combinator.shownNames.map((id) => id.name));
        Logger.debug(
            "[visitImportDirective] Combinator: show ${combinator.shownNames.map((id) => id.name).join(', ')}");
      } else if (combinator is HideCombinator) {
        hideNames ??= {};
        hideNames.addAll(combinator.hiddenNames.map((id) => id.name));
        Logger.debug(
            "[visitImportDirective] Combinator: hide ${combinator.hiddenNames.map((id) => id.name).join(', ')}");
      }
    }

    if (prefixName != null) {
      Logger.debug(
          "[visitImportDirective] Importation du module '${resolvedUri.toString()}' avec le préfixe '$prefixName'. Show: $showNames, Hide: $hideNames");

      Environment envForPrefix;
      if (showNames != null || hideNames != null) {
        // Apply show/hide to the exported environment of the loaded module BEFORE defining it for the prefix
        envForPrefix = loadedModule.exportedEnvironment
            .shallowCopyFiltered(showNames: showNames, hideNames: hideNames);
      } else {
        envForPrefix = loadedModule.exportedEnvironment;
      }
      environment.definePrefixedImport(prefixName, envForPrefix);
    } else {
      Logger.debug(
          "[visitImportDirective] Direct import of module '${resolvedUri.toString()}' into the current environment. Show: $showNames, Hide: $hideNames");
      // Apply show/hide directly during import into the current environment
      environment.importEnvironment(loadedModule.exportedEnvironment,
          show: showNames, hide: hideNames);
    }
    return null; // Import directives do not produce a value.
  }

  /// Helper method to create the appropriate operand for ++ and -- operators.
  /// For numeric types, returns 1 or -1 directly.
  /// For custom classes, attempts to create an instance with value 1 or -1.
  Object? _createIncrementOperand(Object? targetValue, bool isIncrement) {
    if (targetValue is! InterpretedInstance) {
      // For primitive types (num, int, double), return the literal value
      return isIncrement ? 1 : -1;
    }

    // For custom class instances, try to create an instance of the same class
    // with the value 1 or -1. This handles cases like CustomNumber(1).
    try {
      final klass = targetValue.klass;
      final operandValue = isIncrement ? 1 : -1;

      // Try to create an instance with the operand value
      final newInstance = klass.call(this, [operandValue], {});
      return newInstance;
    } catch (e) {
      // If we can't create an instance, fall back to the literal value
      // This allows the operator to handle the error appropriately
      return isIncrement ? 1 : -1;
    }
  }
} // End of InterpreterVisitor class
