///|
/// Runtime package qualification for generated consumer code.
/// Generated files rely on `moon.pkg` import alias; no source-level import.
let current_runtime_alias : Ref[String] = Ref::{ val: "@runtime" }

///|
/// Default runtime import alias used when plugin option is unset.
pub fn default_runtime_alias() -> String {
  "@runtime"
}

///|
/// Set the runtime import alias for the current codegen pass (from `PluginOptions.runtime_alias`).
pub fn set_runtime_alias(runtime_alias : String) -> Unit {
  current_runtime_alias.val = runtime_alias
}

///|
fn runtime_alias_str() -> String {
  current_runtime_alias.val
}

///|
fn is_runtime_type_name(name : String) -> Bool {
  match name {
    "Row"
    | "DBError"
    | "DB"
    | "Transaction"
    | "ExecResult"
    | "RowIter"
    | "Value"
    | "Date"
    | "DateTime"
    | "Time"
    | "TimeTZ"
    | "Decimal"
    | "Uuid"
    | "Duration"
    | "IpAddr" => true
    _ => false
  }
}

///|
/// Qualify a runtime type name for cross-package consumer compilation.
pub fn qual_runtime_name(name : String) -> String {
  if is_runtime_type_name(name) {
    runtime_alias_str() + "." + name
  } else {
    name
  }
}

///|
/// Recursively qualify runtime types in a TypeExpr AST node.
pub fn qual_runtime_type(ty : TypeExpr) -> TypeExpr {
  match ty {
    Named(name) => Named(qual_runtime_name(name))
    Parameterized(name, args) =>
      Parameterized(name, args.map(qual_runtime_type))
    Option(inner) => Option(qual_runtime_type(inner))
    Wildcard => Wildcard
  }
}

///|
/// Map Value constructor label to a cross-package `Value::from_*` helper.
pub fn value_ctor_to_from_fn(ctor : String) -> String {
  let rt = runtime_alias_str()
  match ctor {
    "Int64" => rt + ".Value::from_int64"
    "String" => rt + ".Value::from_string"
    "Bool" => rt + ".Value::from_bool"
    "Double" => rt + ".Value::from_double"
    "Bytes" => rt + ".Value::from_bytes"
    "Date" => rt + ".Value::from_date"
    "DateTime" => rt + ".Value::from_datetime"
    "JsonValue" => rt + ".Value::from_json_value"
    "Decimal" => rt + ".Value::from_decimal"
    "Uuid" => rt + ".Value::from_uuid"
    "Duration" => rt + ".Value::from_duration"
    "Time" => rt + ".Value::from_time"
    "TimeTZ" => rt + ".Value::from_timetz"
    "IpAddr" => rt + ".Value::from_ip_addr"
    _ => rt + ".Value::from_string"
  }
}

///|
pub fn runtime_null_value_fn() -> String {
  runtime_alias_str() + ".Value::null"
}

///|
pub fn runtime_db_err_no_rows_fn() -> String {
  runtime_alias_str() + ".db_err_no_rows"
}

///|
pub fn runtime_db_err_too_many_rows_fn() -> String {
  runtime_alias_str() + ".db_err_too_many_rows"
}

///|
pub fn runtime_exec_result_new_fn() -> String {
  runtime_alias_str() + ".ExecResult::new"
}

///|
/// Strip runtime alias prefix from a qualified type name for internal lookups.
pub fn unqual_runtime_name(name : String) -> String {
  let prefix = runtime_alias_str() + "."
  if name.has_prefix(prefix) {
    name[prefix.length():].to_owned()
  } else {
    name
  }
}

///|
test "qual_runtime_name: Row gets @runtime prefix" {
  set_runtime_alias("@runtime")
  inspect(qual_runtime_name("Row"), content="@runtime.Row")
}

///|
test "qual_runtime_name: Int64 unchanged" {
  set_runtime_alias("@runtime")
  inspect(qual_runtime_name("Int64"), content="Int64")
}

///|
test "value_ctor_to_from_fn: int64 maps to from_int64" {
  set_runtime_alias("@runtime")
  inspect(value_ctor_to_from_fn("Int64"), content="@runtime.Value::from_int64")
}

///|
test "qual_runtime_name: custom alias @myrt" {
  set_runtime_alias("@myrt")
  inspect(qual_runtime_name("DB"), content="@myrt.DB")
  inspect(value_ctor_to_from_fn("Int64"), content="@myrt.Value::from_int64")
  set_runtime_alias("@runtime")
}