use oxc::ast::ast::Str;
use oxc::{
  allocator::{self, Allocator, Box, IntoIn, TakeIn},
  ast::{
    AstBuilder, NONE,
    ast::{
      self, Argument, ClassElement, Declaration, Expression, ImportOrExportKind, NumberBase,
      ObjectPropertyKind, PropertyKind, Statement, VariableDeclarationKind,
    },
  },
  span::{GetSpanMut, SPAN, Span},
  syntax::identifier,
};
use rolldown_common::{EcmaModuleAstUsage, Interop, MemberExprProp};
use rolldown_utils::ecmascript::is_validate_identifier_name;

type PassedStr<'a> = &'a str;

// `AstBuilder` is more suitable name, but it's already used in oxc.
pub struct AstSnippet<'ast> {
  pub builder: AstBuilder<'ast>,
}

impl<'ast> AstSnippet<'ast> {
  pub fn new(alloc: &'ast Allocator) -> Self {
    Self { builder: AstBuilder::new(alloc) }
  }

  #[inline]
  pub fn alloc(&self) -> &'ast Allocator {
    self.builder.allocator
  }

  #[inline]
  pub fn atom(&self, value: &str) -> Str<'ast> {
    self.builder.str(value)
  }

  #[inline]
  pub fn id(&self, name: PassedStr, span: Span) -> ast::BindingIdentifier<'ast> {
    self.builder.binding_identifier(span, self.builder.str(name))
  }

  #[inline]
  pub fn alloc_id_ref(
    &self,
    name: PassedStr,
    span: Span,
  ) -> Box<'ast, ast::IdentifierReference<'ast>> {
    self.builder.alloc_identifier_reference(span, self.builder.str(name))
  }

  #[inline]
  pub fn id_name(&self, name: PassedStr, span: Span) -> ast::IdentifierName<'ast> {
    self.builder.identifier_name(span, self.builder.str(name))
  }

  #[inline]
  pub fn id_ref_expr(&self, name: PassedStr, span: Span) -> ast::Expression<'ast> {
    self.builder.expression_identifier(span, self.builder.str(name))
  }

  /// Helper function to create a `__reExport` call expression with optional symbol arguments
  pub fn re_export_call_expr(
    &self,
    re_export_fn_ref: Expression<'ast>,
    first_arg: Expression<'ast>,
    second_arg: Expression<'ast>,
  ) -> ast::CallExpression<'ast> {
    let args = self.builder.vec_from_iter([first_arg.into(), second_arg.into()]);
    self.builder.call_expression(SPAN, re_export_fn_ref, NONE, args, false)
  }

  pub fn member_expr_or_ident_ref(
    &self,
    object: ast::Expression<'ast>,
    props: &[MemberExprProp],
    span: Span,
  ) -> ast::Expression<'ast> {
    let mut cur = object;
    for prop in props {
      cur = if identifier::is_identifier_name(&prop.name) {
        ast::Expression::from(self.builder.member_expression_static(
          SPAN,
          cur,
          self.id_name(&prop.name, prop.span),
          prop.optional,
        ))
      } else {
        ast::Expression::from(self.builder.member_expression_computed(
          SPAN,
          cur,
          self.builder.expression_string_literal(prop.span, self.builder.str(&prop.name), None),
          prop.optional,
        ))
      };
    }
    *cur.span_mut() = span;
    cur
  }

  /// The props of `foo_exports.value.a` is `["value", "a"]`, here convert it to `(void 0).a`
  #[inline]
  pub fn member_expr_with_void_zero_object(
    &self,
    props: &[MemberExprProp],
    span: Span,
  ) -> ast::Expression<'ast> {
    if props.is_empty() {
      self.void_zero()
    } else {
      self.member_expr_or_ident_ref(self.void_zero(), &props[1..], span)
    }
  }

  /// `[object].[property]`
  pub fn literal_prop_access_member_expr(
    &self,
    object: PassedStr,
    property: PassedStr,
  ) -> ast::MemberExpression<'ast> {
    ast::MemberExpression::StaticMemberExpression(self.builder.alloc_static_member_expression(
      SPAN,
      self.id_ref_expr(object, SPAN),
      self.builder.identifier_name(SPAN, self.builder.str(property)),
      false,
    ))
  }

  /// `[object].[property]`
  #[inline]
  pub fn literal_prop_access_member_expr_expr(
    &self,
    object: PassedStr,
    property: PassedStr,
  ) -> ast::Expression<'ast> {
    ast::Expression::from(self.literal_prop_access_member_expr(object, property))
  }

  /// `name()`
  pub fn call_expr_expr(&self, name: PassedStr) -> ast::Expression<'ast> {
    self.builder.expression_call(
      SPAN,
      self.builder.expression_identifier(SPAN, self.builder.str(name)),
      NONE,
      self.builder.vec(),
      false,
    )
  }

  /// `name(arg)`
  pub fn call_expr_with_arg_expr(
    &self,
    name: ast::Expression<'ast>,
    arg: ast::Expression<'ast>,
    pure: bool,
  ) -> ast::Expression<'ast> {
    let mut call_expr = self.simple_call_expr(name);
    call_expr.pure = pure;
    call_expr.arguments.push(arg.into());
    ast::Expression::CallExpression(call_expr.into_in(self.alloc()))
  }

  /// `name(arg)`
  pub fn call_expr_with_arg_expr_expr(
    &self,
    name: PassedStr,
    arg: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    let arg = ast::Argument::from(arg);
    let mut call_expr = self.builder.call_expression(
      SPAN,
      self.builder.expression_identifier(SPAN, self.builder.str(name)),
      NONE,
      self.builder.vec(),
      false,
    );
    call_expr.arguments.push(arg);
    ast::Expression::CallExpression(call_expr.into_in(self.alloc()))
  }

  /// `name(arg1, arg2)`
  pub fn call_expr_with_2arg_expr(
    &self,
    name: ast::Expression<'ast>,
    arg1: ast::Expression<'ast>,
    arg2: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    let mut call_expr = self.builder.call_expression(SPAN, name, NONE, self.builder.vec(), false);
    call_expr.arguments.push(arg1.into());
    call_expr.arguments.push(arg2.into());
    ast::Expression::CallExpression(call_expr.into_in(self.alloc()))
  }

  /// `name(arg1, arg2)`
  pub fn alloc_call_expr_with_2arg_expr_expr(
    &self,
    name: PassedStr,
    arg1: ast::Expression<'ast>,
    arg2: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    self.builder.expression_call(
      SPAN,
      self.builder.expression_identifier(SPAN, self.builder.str(name)),
      NONE,
      self.builder.vec_from_iter([Argument::from(arg1), Argument::from(arg2)]),
      false,
    )
  }

  /// `name(arg1, arg2)`
  pub fn call_expr_with_2arg_expr_expr(
    &self,
    name: ast::Expression<'ast>,
    arg1: ast::Expression<'ast>,
    arg2: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    self.builder.expression_call(
      SPAN,
      name,
      NONE,
      self.builder.vec_from_iter([Argument::from(arg1), Argument::from(arg2)]),
      false,
    )
  }

  /// `name()`
  #[inline]
  pub fn call_expr_stmt(&self, name: PassedStr) -> ast::Statement<'ast> {
    self.builder.statement_expression(SPAN, self.call_expr_expr(name))
  }

  /// `var [name] = [init]`
  pub fn var_decl_stmt(
    &self,
    name: PassedStr,
    init: ast::Expression<'ast>,
  ) -> ast::Statement<'ast> {
    let declarations = self.builder.vec1(self.builder.variable_declarator(
      SPAN,
      ast::VariableDeclarationKind::Var,
      self.builder.binding_pattern_binding_identifier(SPAN, self.builder.str(name)),
      NONE,
      Some(init),
      false,
    ));

    ast::Statement::from(ast::Declaration::VariableDeclaration(
      self.builder.alloc_variable_declaration(
        SPAN,
        ast::VariableDeclarationKind::Var,
        declarations,
        false,
      ),
    ))
  }

  /// ```js
  ///  var require_foo = __commonJS(((exports, module) => {
  ///    ...
  ///  }));
  ///  or
  ///  __commonJSMin when `options.profiler_names` is false
  /// ```
  #[expect(clippy::too_many_arguments)]
  pub fn commonjs_wrapper_stmt(
    &self,
    binding_name: PassedStr,
    commonjs_expr: ast::Expression<'ast>,
    statements: allocator::Vec<'ast, Statement<'ast>>,
    ast_usage: EcmaModuleAstUsage,
    profiler_names: bool,
    stable_id: &str,
    is_async: bool,
  ) -> ast::Statement<'ast> {
    // (exports, module) => {}

    let mut params = self.builder.formal_parameters(
      SPAN,
      ast::FormalParameterKind::Signature,
      self.builder.vec_with_capacity(1),
      NONE,
    );
    let body = self.builder.function_body(SPAN, self.builder.vec(), statements);
    if ast_usage.intersects(EcmaModuleAstUsage::ModuleOrExports) {
      params.items.push(self.builder.formal_parameter(
        SPAN,
        self.builder.vec(),
        self.builder.binding_pattern_binding_identifier(SPAN, "exports"),
        NONE,
        NONE,
        false,
        None,
        false,
        false,
      ));
    }

    if ast_usage.contains(EcmaModuleAstUsage::ModuleRef) {
      params.items.push(self.builder.formal_parameter(
        SPAN,
        self.builder.vec(),
        self.builder.binding_pattern_binding_identifier(SPAN, "module"),
        NONE,
        NONE,
        false,
        None,
        false,
        false,
      ));
    }

    //  __commonJS(...)
    let mut commonjs_call_expr = self.builder.call_expression_with_pure(
      SPAN,
      commonjs_expr,
      NONE,
      self.builder.vec(),
      false,
      true,
    );

    // the callback is marked as PIFE because most require calls are evaluated in the initial load
    let mut arrow_expr =
      self.builder.alloc_arrow_function_expression(SPAN, false, is_async, NONE, params, NONE, body);
    arrow_expr.pife = true;

    if profiler_names {
      let obj_expr = self.builder.alloc_object_expression(
        SPAN,
        self.builder.vec1(self.builder.object_property_kind_object_property(
          SPAN,
          PropertyKind::Init,
          ast::PropertyKey::from(self.builder.expression_string_literal(
            SPAN,
            self.builder.str(stable_id),
            None,
          )),
          Expression::ArrowFunctionExpression(arrow_expr),
          true,
          false,
          false,
        )),
      );
      commonjs_call_expr.arguments.push(ast::Argument::ObjectExpression(obj_expr));
    } else {
      commonjs_call_expr.arguments.push(ast::Argument::ArrowFunctionExpression(arrow_expr));
    }

    // var require_foo = ...
    self.var_decl_stmt(
      binding_name,
      ast::Expression::CallExpression(commonjs_call_expr.into_in(self.alloc())),
    )
  }

  /// ```js
  /// var init_foo = __esm((() => { ... }));
  /// ```
  #[expect(clippy::too_many_arguments)]
  pub fn esm_wrapper_stmt(
    &self,
    binding_name: PassedStr,
    esm_fn_expr: ast::Expression<'ast>,
    statements: allocator::Vec<'ast, Statement<'ast>>,
    profiler_names: bool,
    use_pife: bool,
    stable_id: &str,
    is_async: bool,
  ) -> ast::Statement<'ast> {
    // () => { ... }
    let params = self.builder.formal_parameters(
      SPAN,
      ast::FormalParameterKind::Signature,
      self.builder.vec(),
      NONE,
    );
    let body = self.builder.function_body(SPAN, self.builder.vec(), statements);

    //  __esm(...)
    let mut esm_call_expr =
      self.builder.call_expression(SPAN, esm_fn_expr, NONE, self.builder.vec(), false);

    // the callback is marked as PIFE because dynamically imported modules are split into a separate chunk
    // and the statically imported modules are evaluated in the initial load
    let mut arrow_expr =
      self.builder.alloc_arrow_function_expression(SPAN, false, is_async, NONE, params, NONE, body);
    arrow_expr.pife = use_pife;

    if profiler_names {
      let obj_expr = self.builder.alloc_object_expression(
        SPAN,
        self.builder.vec1(self.builder.object_property_kind_object_property(
          SPAN,
          PropertyKind::Init,
          ast::PropertyKey::from(self.builder.expression_string_literal(
            SPAN,
            self.builder.str(stable_id),
            None,
          )),
          Expression::ArrowFunctionExpression(arrow_expr),
          false,
          false,
          false,
        )),
      );
      esm_call_expr.arguments.push(ast::Argument::ObjectExpression(obj_expr));
    } else {
      esm_call_expr.arguments.push(ast::Argument::ArrowFunctionExpression(arrow_expr));
    }

    // var init_foo = __esm(...)
    self.var_decl_stmt(
      binding_name,
      ast::Expression::CallExpression(esm_call_expr.into_in(self.alloc())),
    )
  }

  /// ```js
  /// (a, b)
  /// ```
  pub fn seq2_in_paren_expr(
    &self,
    a: ast::Expression<'ast>,
    b: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    let mut expressions = self.builder.vec_with_capacity(2);
    expressions.push(a);
    expressions.push(b);
    let seq_expr = ast::Expression::SequenceExpression(
      self.builder.alloc_sequence_expression(SPAN, expressions),
    );
    ast::Expression::ParenthesizedExpression(
      self.builder.alloc_parenthesized_expression(SPAN, seq_expr),
    )
  }

  pub fn number_expr(&self, value: f64, raw: &'ast str) -> ast::Expression<'ast> {
    ast::Expression::NumericLiteral(self.builder.alloc_numeric_literal(
      SPAN,
      value,
      Some(Str::from(raw)),
      oxc::syntax::number::NumberBase::Decimal,
    ))
  }

  /// ```js
  ///  id = ...
  /// ￣￣ AssignmentTarget
  /// ```
  pub fn simple_id_assignment_target(
    &self,
    id: PassedStr,
    span: Span,
  ) -> ast::AssignmentTarget<'ast> {
    ast::AssignmentTarget::AssignmentTargetIdentifier(self.alloc_id_ref(id, span))
  }

  /// ```js
  /// () => xx
  /// ```
  pub fn only_return_arrow_expr(&self, expr: ast::Expression<'ast>) -> ast::Expression<'ast> {
    let statements = self.builder.vec1(ast::Statement::ExpressionStatement(
      self.builder.alloc_expression_statement(SPAN, expr),
    ));
    ast::Expression::ArrowFunctionExpression(self.builder.alloc_arrow_function_expression(
      SPAN,
      true,
      false,
      NONE,
      self.builder.formal_parameters(
        SPAN,
        ast::FormalParameterKind::Signature,
        self.builder.vec(),
        NONE,
      ),
      NONE,
      self.builder.function_body(SPAN, self.builder.vec(), statements),
    ))
  }

  #[inline]
  /// `undefined` is acting like identifier, it might be shadowed by user code.
  pub fn void_zero(&self) -> ast::Expression<'ast> {
    self.builder.void_0(SPAN)
  }

  pub fn alloc_string_literal(
    &self,
    value: PassedStr,
    span: Span,
  ) -> Box<'ast, ast::StringLiteral<'ast>> {
    self.builder.alloc_string_literal(span, self.builder.str(value), None)
  }

  pub fn string_literal_expr(&self, value: PassedStr, span: Span) -> ast::Expression<'ast> {
    ast::Expression::StringLiteral(self.alloc_string_literal(value, span))
  }

  pub fn import_star_stmt(&self, source: PassedStr, as_name: PassedStr) -> ast::Statement<'ast> {
    let specifiers = self.builder.vec1(ast::ImportDeclarationSpecifier::ImportNamespaceSpecifier(
      self.builder.alloc_import_namespace_specifier(SPAN, self.id(as_name, SPAN)),
    ));
    ast::Statement::ImportDeclaration(self.builder.alloc_import_declaration(
      SPAN,
      Some(specifiers),
      self.builder.string_literal(SPAN, self.builder.str(source), None),
      None,
      NONE,
      ImportOrExportKind::Value,
    ))
  }

  /// var [assignee] = require([source]);
  pub fn variable_declarator_require_call_stmt(
    &self,
    assignee: &str,
    init: ast::Expression<'ast>,
    span: Span,
  ) -> Statement<'ast> {
    Statement::from(self.builder.declaration_variable(
      span,
      VariableDeclarationKind::Var,
      self.builder.vec1(self.builder.variable_declarator(
        SPAN,
        VariableDeclarationKind::Var,
        self.builder.binding_pattern_binding_identifier(SPAN, self.builder.str(assignee)),
        NONE,
        Some(init),
        false,
      )),
      false,
    ))
  }

  /// Promise.resolve().then(() => expr))
  pub fn promise_resolve_then_call_expr(
    &self,
    expr: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    ast::Expression::CallExpression(self.builder.alloc_call_expression(
      SPAN,
      ast::Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
        SPAN,
        ast::Expression::CallExpression(self.builder.alloc_call_expression(
          SPAN,
          ast::Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
            SPAN,
            self.id_ref_expr("Promise", SPAN),
            self.id_name("resolve", SPAN),
            false,
          )),
          NONE,
          self.builder.vec(),
          false,
        )),
        self.id_name("then", SPAN),
        false,
      )),
      NONE,
      self.builder.vec1(Argument::from(self.only_return_arrow_expr(expr))),
      false,
    ))
  }

  pub fn callee_then_call_expr(
    &self,
    call_expr: ast::Expression<'ast>,
    return_expr: ast::Expression<'ast>,
  ) -> ast::Expression<'ast> {
    ast::Expression::CallExpression(self.builder.alloc_call_expression(
      SPAN,
      ast::Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
        SPAN,
        call_expr,
        self.id_name("then", SPAN),
        false,
      )),
      NONE,
      self.builder.vec1(Argument::from(self.only_return_arrow_expr(return_expr))),
      false,
    ))
  }

  // create `a: () => expr` for  `{ a: () => expr }``
  pub fn object_property_kind_object_property(
    &self,
    key: PassedStr,
    expr: ast::Expression<'ast>,
    computed: bool,
  ) -> ObjectPropertyKind<'ast> {
    self.builder.object_property_kind_object_property(
      SPAN,
      PropertyKind::Init,
      if computed {
        ast::PropertyKey::from(self.builder.expression_string_literal(
          SPAN,
          self.builder.str(key),
          None,
        ))
      } else {
        self.builder.property_key_static_identifier(SPAN, self.builder.str(key))
      },
      self.only_return_arrow_expr(expr),
      true,
      false,
      computed,
    )
  }

  // If interop is None, using `require_foo()`
  // If interop is babel, using __toESM(require_foo())
  // If interop is node, using __toESM(require_foo(), 1)
  #[expect(clippy::needless_pass_by_value)]
  pub fn to_esm_call_with_interop(
    &self,
    to_esm_fn_name: PassedStr,
    call_expr: Expression<'ast>,
    interop: Option<Interop>,
  ) -> Expression<'ast> {
    match interop {
      None => call_expr,
      Some(Interop::Babel) => self.call_expr_with_arg_expr_expr(to_esm_fn_name, call_expr),
      Some(Interop::Node) => self.alloc_call_expr_with_2arg_expr_expr(
        to_esm_fn_name,
        call_expr,
        self.builder.expression_numeric_literal(SPAN, 1.0, None, NumberBase::Decimal),
      ),
    }
  }

  // If `node_mode` is true, using `__toESM(expr, 1)`
  // If `node_mode` is false, using `__toESM(expr)`
  pub fn wrap_with_to_esm(
    &self,
    to_esm_fn_expr: Expression<'ast>,
    expr: Expression<'ast>,
    node_mode: bool,
  ) -> Expression<'ast> {
    let args = if node_mode {
      self.builder.vec_from_iter([
        Argument::from(expr),
        Argument::from(self.builder.expression_numeric_literal(
          SPAN,
          1.0,
          None,
          NumberBase::Decimal,
        )),
      ])
    } else {
      self.builder.vec1(Argument::from(expr))
    };
    ast::Expression::CallExpression(self.builder.alloc_call_expression_with_pure(
      SPAN,
      to_esm_fn_expr,
      NONE,
      args,
      false,
      true,
    ))
  }

  /// convert `Expression` to
  /// export default ${Expression}
  pub fn export_default_expr_stmt(&self, expr: Expression<'ast>) -> Statement<'ast> {
    let ast_builder = &self.builder;
    Statement::from(ast_builder.module_declaration_export_default_declaration(
      SPAN,
      ast::ExportDefaultDeclarationKind::from(expr),
    ))
  }

  /// convert `Expression` to
  /// module.exports = ${Expression}
  pub fn module_exports_expr_stmt(&self, expr: Expression<'ast>) -> Statement<'ast> {
    let ast_builder = &self.builder;
    ast_builder.statement_expression(
      SPAN,
      ast_builder.expression_assignment(
        SPAN,
        ast::AssignmentOperator::Assign,
        ast::AssignmentTarget::from(ast::SimpleAssignmentTarget::from(
          ast_builder.member_expression_static(
            SPAN,
            ast_builder.expression_identifier(SPAN, "module"),
            ast_builder.identifier_name(SPAN, "exports"),
            false,
          ),
        )),
        expr,
      ),
    )
  }

  pub fn expr_without_parentheses(&self, mut expr: Expression<'ast>) -> Expression<'ast> {
    while let Expression::ParenthesizedExpression(mut paren_expr) = expr {
      expr = paren_expr.expression.take_in(self.builder.allocator);
    }
    expr
  }

  #[inline]
  pub fn statement_module_declaration_export_named_declaration<'a, T: AsRef<str> + 'a, I>(
    &self,
    declaration: Option<Declaration<'ast>>,
    specifiers: I,
  ) -> Statement<'ast>
  where
    I: Iterator<Item = (&'a T, &'a (T, bool))>,
  {
    Statement::from(self.builder.module_declaration_export_named_declaration(
      SPAN,
      declaration,
      self.builder.vec_from_iter(specifiers.into_iter().map(|(local, (exported, legal_ident))| {
        self.builder.export_specifier(
          SPAN,
          self
            .builder
            .module_export_name_identifier_reference(SPAN, self.builder.str(local.as_ref())),
          if *legal_ident {
            self
              .builder
              .module_export_name_identifier_name(SPAN, self.builder.str(exported.as_ref()))
          } else {
            self.builder.module_export_name_string_literal(
              SPAN,
              self.builder.str(exported.as_ref()),
              None,
            )
          },
          ImportOrExportKind::Value,
        )
      })),
      None,
      ImportOrExportKind::Value,
      NONE,
    ))
  }

  pub fn keep_name_call_expr(
    &self,
    original_name: PassedStr,
    target: Expression<'ast>,
    callee: Expression<'ast>,
    pure: bool,
  ) -> Expression<'ast> {
    self.builder.expression_call_with_pure(
      SPAN,
      callee,
      NONE,
      {
        let mut items = self.builder.vec_with_capacity(2);
        items.push(target.into());
        items.push(
          self.builder.expression_string_literal(SPAN, self.atom(original_name), None).into(),
        );
        items
      },
      false,
      pure,
    )
  }

  pub fn static_block_keep_name_helper(
    &self,
    name: PassedStr,
    callee: Expression<'ast>,
  ) -> ClassElement<'ast> {
    self.builder.class_element_static_block(
      SPAN,
      self.builder.vec1(self.builder.statement_expression(
        SPAN,
        self.builder.expression_call(
          SPAN,
          callee,
          NONE,
          {
            let mut items = self.builder.vec_with_capacity(2);
            items.push(self.builder.expression_this(SPAN).into());
            items.push(
              self.builder.expression_string_literal(SPAN, self.builder.str(name), None).into(),
            );
            items
          },
          false,
        ),
      )),
    )
  }

  pub fn simple_call_expr(&self, callee: Expression<'ast>) -> ast::CallExpression<'ast> {
    self.builder.call_expression(SPAN, callee, NONE, self.builder.vec(), false)
  }

  pub fn alloc_simple_call_expr(
    &self,
    callee: Expression<'ast>,
  ) -> allocator::Box<'ast, ast::CallExpression<'ast>> {
    self.builder.alloc_call_expression(SPAN, callee, NONE, self.builder.vec(), false)
  }

  pub fn object_freeze_dynamic_import_polyfill(&self) -> Expression<'ast> {
    let proto = self.builder.object_property_kind_object_property(
      SPAN,
      PropertyKind::Init,
      self.builder.property_key_static_identifier(SPAN, "__proto__"),
      ast::Expression::NullLiteral(self.builder.alloc_null_literal(SPAN)),
      false,
      false,
      false,
    );

    self.call_expr_with_arg_expr(
      self.literal_prop_access_member_expr_expr("Object", "freeze"),
      ast::Expression::ObjectExpression(
        self.builder.alloc_object_expression(SPAN, self.builder.vec_from_iter([proto])),
      ),
      true,
    )
  }

  /// Creates an arrow function that extracts a property from a namespace object.
  /// Generates: `n => n.property_name`
  /// Used for transforming dynamic imports like `import('./module').then(n => n.exportedName)`
  pub fn arrow_function_extract_property(
    &self,
    property_name: &str,
  ) -> allocator::Box<'ast, ast::ArrowFunctionExpression<'ast>> {
    debug_assert!(is_validate_identifier_name(property_name));
    self.builder.alloc_arrow_function_expression(
      SPAN,
      true,  // expression
      false, // async
      NONE,
      self.builder.formal_parameters(
        SPAN,
        ast::FormalParameterKind::ArrowFormalParameters,
        self.builder.vec1(self.builder.formal_parameter(
          SPAN,
          self.builder.vec(),
          self.builder.binding_pattern_binding_identifier(SPAN, self.builder.str("n")),
          NONE,
          NONE,
          false,
          None,
          false,
          false,
        )),
        NONE,
      ),
      NONE,
      self.builder.function_body(
        SPAN,
        self.builder.vec(),
        self.builder.vec1(ast::Statement::ExpressionStatement(
          self.builder.alloc_expression_statement(
            SPAN,
            Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
              SPAN,
              self.builder.expression_identifier(SPAN, "n"),
              self.builder.identifier_name(SPAN, self.builder.str(property_name)),
              false,
            )),
          ),
        )),
      ),
    )
  }

  /// Creates a call expression that transforms a dynamic import to extract a specific property.
  /// Generates: `import_expr.then(n => n.property_name)`
  /// This is used to transform dynamic imports to extract a specific export from the module namespace.
  #[inline]
  pub fn import_then_extract_property(
    &self,
    import_expr: allocator::Box<'ast, ast::ImportExpression<'ast>>,
    property_name: &str,
  ) -> allocator::Box<'ast, ast::CallExpression<'ast>> {
    self.then_extract_property(Expression::ImportExpression(import_expr), property_name)
  }

  /// Creates a call expression that chains `.then(n => n.property_name)` to any expression.
  /// Generates: `expr.then(n => n.property_name)`
  /// This is used to extract a specific export from a promise that resolves to a module namespace.
  pub fn then_extract_property(
    &self,
    expr: Expression<'ast>,
    property_name: &str,
  ) -> allocator::Box<'ast, ast::CallExpression<'ast>> {
    let callee = self.builder.alloc_static_member_expression(
      SPAN,
      expr,
      self.builder.identifier_name(SPAN, "then"),
      false,
    );

    self.builder.alloc_call_expression(
      SPAN,
      Expression::StaticMemberExpression(callee),
      NONE,
      self.builder.vec1(
        ast::Expression::ArrowFunctionExpression(
          self.arrow_function_extract_property(property_name),
        )
        .into(),
      ),
      false,
    )
  }

  /// Creates a call expression that chains `.then(n => return_expr)` to any expression.
  /// Generates: `expr.then(n => return_expr)`
  /// The `build_return_expr` callback receives the parameter name "n" and should return the expression to use as the arrow function body.
  fn then_with_arrow_callback(
    &self,
    expr: Expression<'ast>,
    return_expr: Expression<'ast>,
  ) -> allocator::Box<'ast, ast::CallExpression<'ast>> {
    // Create arrow function: n => return_expr
    let arrow_fn = self.builder.alloc_arrow_function_expression(
      SPAN,
      true,  // expression
      false, // async
      NONE,
      self.builder.formal_parameters(
        SPAN,
        ast::FormalParameterKind::ArrowFormalParameters,
        self.builder.vec1(self.builder.formal_parameter(
          SPAN,
          self.builder.vec(),
          self.builder.binding_pattern_binding_identifier(SPAN, self.builder.str("n")),
          NONE,
          NONE,
          false,
          None,
          false,
          false,
        )),
        NONE,
      ),
      NONE,
      self.builder.function_body(
        SPAN,
        self.builder.vec(),
        self.builder.vec1(ast::Statement::ExpressionStatement(
          self.builder.alloc_expression_statement(SPAN, return_expr),
        )),
      ),
    );

    // expr.then(n => return_expr)
    let callee = self.builder.alloc_static_member_expression(
      SPAN,
      expr,
      self.builder.identifier_name(SPAN, "then"),
      false,
    );
    self.builder.alloc_call_expression(
      SPAN,
      Expression::StaticMemberExpression(callee),
      NONE,
      self.builder.vec1(ast::Expression::ArrowFunctionExpression(arrow_fn).into()),
      false,
    )
  }

  /// Creates a call expression that chains `.then(n => __toESM(n.property_name()))` to any expression.
  /// Generates: `expr.then(n => __toESM(n.property_name()))`
  /// This is used for CJS modules merged into common chunks where we need to call the wrapper and wrap the result.
  pub fn then_call_cjs_wrapper_with_to_esm(
    &self,
    expr: Expression<'ast>,
    property_name: &str,
    to_esm_fn_expr: Expression<'ast>,
    node_mode: bool,
  ) -> allocator::Box<'ast, ast::CallExpression<'ast>> {
    // n.property_name
    let member_expr =
      Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
        SPAN,
        self.builder.expression_identifier(SPAN, "n"),
        self.builder.identifier_name(SPAN, self.builder.str(property_name)),
        false,
      ));
    // n.property_name()
    let wrapper_call =
      self.builder.expression_call(SPAN, member_expr, NONE, self.builder.vec(), false);

    // __toESM(n.property_name()) or __toESM(n.property_name(), 1)
    let to_esm_call = self.wrap_with_to_esm(to_esm_fn_expr, wrapper_call, node_mode);

    self.then_with_arrow_callback(expr, to_esm_call)
  }

  /// Creates a call expression that chains `.then(n => (n.wrapper_name(), n.namespace_name))` to any expression.
  /// Generates: `expr.then(n => (n.init_xxx(), n.namespace))`
  /// This is used for ESM modules merged into common chunks where we need to call the wrapper and return the namespace.
  pub fn then_call_esm_wrapper_with_namespace(
    &self,
    expr: Expression<'ast>,
    wrapper_name: &str,
    namespace_name: &str,
  ) -> allocator::Box<'ast, ast::CallExpression<'ast>> {
    // n.wrapper_name
    let wrapper_member =
      Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
        SPAN,
        self.builder.expression_identifier(SPAN, "n"),
        self.builder.identifier_name(SPAN, self.builder.str(wrapper_name)),
        false,
      ));
    // n.wrapper_name()
    let wrapper_call =
      self.builder.expression_call(SPAN, wrapper_member, NONE, self.builder.vec(), false);

    // n.namespace_name
    let namespace_member =
      Expression::StaticMemberExpression(self.builder.alloc_static_member_expression(
        SPAN,
        self.builder.expression_identifier(SPAN, "n"),
        self.builder.identifier_name(SPAN, self.builder.str(namespace_name)),
        false,
      ));

    // (n.wrapper_name(), n.namespace_name)
    let seq_expr = self.seq2_in_paren_expr(wrapper_call, namespace_member);

    self.then_with_arrow_callback(expr, seq_expr)
  }
}
