///|
// Immutable root-script facts consumed by the compiled-script runtime entry.
// The constructor accepts source AST only at the lowering boundary; the value
// retained by a compiled program contains declaration facts and a settled
// early-error result, never executable source nodes.
pub struct CompiledScriptLexicalBinding {
  priv kind : BindingKind
  priv name : String
}

///|
pub fn CompiledScriptLexicalBinding::CompiledScriptLexicalBinding(
  kind~ : BindingKind,
  name~ : String,
) -> CompiledScriptLexicalBinding {
  { kind, name, }
}

///|
// The settlement token is created only by source validation. Its reference
// identity binds the retained error (including its typed location data) to
// the lowering operation that produced the preparation; callers cannot forge
// a matching token by supplying an arbitrary Error value.
pub struct CompiledScriptEarlyErrorSettlement {
  priv token : Ref[Unit]
  priv provenance_token : Ref[Unit]
  priv error : Error?
}

///|
pub struct CompiledScriptPreparation {
  priv strict : Bool
  priv var_names : Array[String]
  priv function_names : Array[String]
  priv lexical_bindings : Array[CompiledScriptLexicalBinding]
  priv early_error_settlement : CompiledScriptEarlyErrorSettlement?
}

///|
pub fn CompiledScriptPreparation::CompiledScriptPreparation(
  strict~ : Bool,
  var_names~ : Array[String],
  function_names~ : Array[String],
  lexical_bindings~ : Array[CompiledScriptLexicalBinding],
) -> CompiledScriptPreparation {
  {
    strict,
    var_names: var_names.copy(),
    function_names: function_names.copy(),
    lexical_bindings: lexical_bindings.copy(),
    early_error_settlement: None,
  }
}

///|
// Attach the source-validation result through its opaque settlement
// capability. A general facts constructor intentionally cannot accept Error.
pub fn CompiledScriptPreparation::with_early_error_settlement(
  strict~ : Bool,
  var_names~ : Array[String],
  function_names~ : Array[String],
  lexical_bindings~ : Array[CompiledScriptLexicalBinding],
  settlement~ : CompiledScriptEarlyErrorSettlement,
) -> CompiledScriptPreparation {
  {
    strict,
    var_names: var_names.copy(),
    function_names: function_names.copy(),
    lexical_bindings: lexical_bindings.copy(),
    early_error_settlement: Some(settlement),
  }
}

///|
// Verify the immutable root setup envelope against compiler-derived facts
// without exposing its backing arrays or the runtime Binding representation.
// Early-error construction is intentionally not repeated here; it is settled
// once at lowering and carried as part of the same envelope.
pub fn compiled_script_preparation_matches(
  preparation : CompiledScriptPreparation,
  strict : Bool,
  var_names : Array[String],
  function_names : Array[String],
  lexical_bindings : Array[CompiledScriptLexicalBinding],
  expected_settlement : CompiledScriptEarlyErrorSettlement,
  provenance_token? : Ref[Unit]? = None,
) -> Bool {
  let settlement_matches = match preparation.early_error_settlement {
    Some(actual) => {
      let provenance_matches = match provenance_token {
        Some(expected) => physical_equal(actual.provenance_token, expected)
        None => true
      }
      physical_equal(actual.token, expected_settlement.token) &&
      provenance_matches
    }
    None => false
  }
  if preparation.strict != strict ||
    !Array::equal(preparation.var_names, var_names) ||
    !Array::equal(preparation.function_names, function_names) ||
    preparation.lexical_bindings.length() != lexical_bindings.length() ||
    !settlement_matches {
    return false
  }
  for index in 0.. CompiledScriptEarlyErrorSettlement {
  let error : Error? = try {
    validate_compiled_script_early_errors(stmts, strict)
    None
  } catch {
    error => Some(error)
  }
  {
    token: { val: (), },
    provenance_token: match provenance_token {
      Some(token) => token
      None => { val: (), }
    },
    error,
  }
}

///|
fn Interpreter::apply_compiled_script_preparation(
  self : Interpreter,
  preparation : CompiledScriptPreparation,
) -> Unit raise Error {
  match preparation.early_error_settlement {
    Some(settlement) =>
      match settlement.error {
        Some(error) => raise error
        None => ()
      }
    None =>
      raise @errors.InternalError(
        message="unsettled CompiledScriptPreparation cannot be applied",
      )
  }
  check_global_function_declarations(self, preparation.function_names)
  check_global_var_declarations(
    self,
    preparation.function_names,
    preparation.var_names,
  )
  for name in preparation.var_names {
    self.define_compiled_binding(
      self.global,
      @ast.VarKind::VarKind,
      name,
      Value::Undefined,
      false,
    )
  }
  for binding in preparation.lexical_bindings {
    match binding.kind {
      LetBinding => self.global.def_let_tdz_if_absent(binding.name)
      ConstBinding => self.global.def_const_tdz_if_absent(binding.name)
      _ =>
        raise @errors.InternalError(
          message="compiled script preparation contains a non-lexical binding",
        )
    }
  }
}