///|
priv struct ConstraintIntRange {
  lower : Double
  upper : Double
}

///|
priv struct ConstraintIntCall {
  name : String
  argument : Double
}

///|
// PKL-112: threshold encoding lifted from Int to Double so that
// `Float(isBetween(0.5, 1.5))` parses and runs without losing precision.
// Int-side comparisons widen the value to Double before applying the
// operator (see pkl_constraint_predicate_accepts), so existing Int-only
// constraint annotations keep working unchanged.
priv enum ConstraintIntPredicate {
  IsBetween(Double, Double)
  IsPositive
  IsGreaterThan(Double)
  IsLessThan(Double)
  NotIsBetween(Double, Double)
  NotIsPositive
  NotIsGreaterThan(Double)
  NotIsLessThan(Double)
  CustomGreaterThan(String, Double)
  CustomLessThan(String, Double)
  CustomGreaterOrEqual(String, Double)
  CustomLessOrEqual(String, Double)
  NotCustomGreaterThan(String, Double)
  NotCustomLessThan(String, Double)
  NotCustomGreaterOrEqual(String, Double)
  NotCustomLessOrEqual(String, Double)
  // PKL-148b: `Int(this > 0)` / `Int(0 < this)`-style bare comparison
  // constraints. The constraint name renders as the literal source
  // ("this > 0") to match Apple Pkl's diagnostic wording, and the
  // accepts side runs the comparison with the candidate substituted
  // for `this`.
  ThisCompare(ConstraintCompareOp, Double, Bool)
  NotThisCompare(ConstraintCompareOp, Double, Bool)
}

///|
priv enum ConstraintCompareOp {
  CmpGreaterThan
  CmpGreaterOrEqual
  CmpLessThan
  CmpLessOrEqual
  CmpEqual
  CmpNotEqual
}

///|
fn pkl_parse_constraint_int_text(text : String) -> Int? {
  if text.length() == 0 {
    return None
  }
  let mut index = 0
  let mut sign = 1
  if text[0].to_int().unsafe_to_char() == '-' {
    sign = -1
    index = 1
  }
  if index == text.length() {
    return None
  }
  let mut value = 0
  while index < text.length() {
    let char = text[index].to_int().unsafe_to_char()
    if char < '0' || char > '9' {
      return None
    }
    value = value * 10 + char.to_int() - '0'.to_int()
    index += 1
  }
  Some(value * sign)
}

///|
// PKL-112: parser for Int or Float threshold literals appearing inside
// constraint predicates (`isBetween(0.5, 1.5)`, `isGreaterThan(-3)`).
// Accepts an optional leading `-`, a digit-run integer part, and an
// optional `.` fractional part. Exponents are not handled — the
// parser surface (PKL-128) covers `1e10` separately and constraint text
// never contains scientific literals today.
fn pkl_parse_constraint_double_text(text : String) -> Double? {
  let trimmed = pkl_constraint_trim(text)
  if trimmed.length() == 0 {
    return None
  }
  let mut index = 0
  let mut sign = 1.0
  if trimmed[0].to_int().unsafe_to_char() == '-' {
    sign = -1.0
    index = 1
  }
  if index == trimmed.length() {
    return None
  }
  let mut int_part = 0.0
  let mut saw_digit = false
  while index < trimmed.length() {
    let char = trimmed[index].to_int().unsafe_to_char()
    if char >= '0' && char <= '9' {
      int_part = int_part * 10.0 + (char.to_int() - '0'.to_int()).to_double()
      saw_digit = true
      index += 1
    } else {
      break
    }
  }
  let mut frac_part = 0.0
  let mut frac_scale = 1.0
  let mut saw_dot = false
  if index < trimmed.length() && trimmed[index].to_int().unsafe_to_char() == '.' {
    saw_dot = true
    index += 1
    while index < trimmed.length() {
      let char = trimmed[index].to_int().unsafe_to_char()
      if char >= '0' && char <= '9' {
        frac_scale = frac_scale * 10.0
        frac_part = frac_part +
          (char.to_int() - '0'.to_int()).to_double() / frac_scale
        saw_digit = true
        index += 1
      } else {
        break
      }
    }
  }
  if index != trimmed.length() || !saw_digit {
    return None
  }
  let _ = saw_dot
  Some(sign * (int_part + frac_part))
}

///|
fn pkl_split_constraint_arguments(text : String) -> Array[String] {
  // Split a comma-separated constraint argument list at the top
  // level (parens / brackets / angles balanced). Each segment is
  // trimmed so the predicate dispatcher doesn't have to defend
  // against leading / trailing whitespace introduced by the
  // operator spacing in `Int(isPositive, isLessThan(10))`.
  //
  // Fast path: a flat argument-free call like `isPositive` has no
  // commas; return the trimmed text without entering the per-char
  // StringBuilder loop. Also use `write_char` instead of allocating a
  // transient one-char String per iteration.
  if !string_contains_char(text, ',') {
    return [trim_spaces(text)]
  }
  let parts : Array[String] = []
  let buf = StringBuilder::new()
  let mut parens = 0
  let mut brackets = 0
  let mut angles = 0
  for char in text {
    if char == ',' && parens == 0 && brackets == 0 && angles == 0 {
      parts.push(trim_spaces(buf.to_string()))
      buf.reset()
    } else {
      if char == '(' {
        parens += 1
      } else if char == ')' && parens > 0 {
        parens -= 1
      } else if char == '[' {
        brackets += 1
      } else if char == ']' && brackets > 0 {
        brackets -= 1
      } else if char == '<' {
        angles += 1
      } else if char == '>' && angles > 0 {
        angles -= 1
      }
      buf.write_char(char)
    }
  }
  let last = trim_spaces(buf.to_string())
  if last != "" || parts.length() > 0 {
    parts.push(last)
  }
  parts
}

///|
/// Tight byte-loop single-char index search. Returns `-1` when not
/// found. MoonBit's `String.find(pattern)` routes through
/// `brute_force_find` / `boyer_moore_horspool_find` which dominate
/// the reflect / type-name profile when used with single-char
/// patterns; this avoids that dispatch.
fn string_index_of_char(s : String, c : Char) -> Int {
  let target = c.to_int()
  for i = 0; i < s.length(); i = i + 1 {
    if s[i].to_int() == target {
      return i
    }
  }
  -1
}

///|
fn string_contains_char(s : String, c : Char) -> Bool {
  string_index_of_char(s, c) >= 0
}

///|
/// Cheap "starts with this one character" check that avoids
/// `String.has_prefix("(")`'s boyer-moore dispatch on the hot reflect
/// / type-name paths.
fn string_starts_with_char(s : String, c : Char) -> Bool {
  s.length() > 0 && s[0].to_int() == c.to_int()
}

///|
fn string_ends_with_char(s : String, c : Char) -> Bool {
  s.length() > 0 && s[s.length() - 1].to_int() == c.to_int()
}

///|
fn trim_spaces(text : String) -> String {
  let mut start = 0
  while start < text.length() {
    let c = text[start].to_int().unsafe_to_char()
    if c == ' ' || c == '\t' {
      start = start + 1
    } else {
      break
    }
  }
  let mut end = text.length()
  while end > start {
    let c = text[end - 1].to_int().unsafe_to_char()
    if c == ' ' || c == '\t' {
      end = end - 1
    } else {
      break
    }
  }
  String::unsafe_substring(text, start~, end~)
}

///|
fn pkl_constrained_type_paren_index(name : String) -> Int? {
  // Fast path: a constrained-type name (`Int(isBetween(...))`) must
  // contain at least one `(`. Type names without a paren — `Int`,
  // `Listing`, `String` — are by far the common case on the
  // reflect / synthesize-default hot paths, and they all bail here
  // without entering the per-char balanced-bracket walk. Use a tight
  // byte-loop instead of `String.find("(")` so the common-case bail
  // doesn't itself go through MoonBit's general pattern matcher.
  let first_paren = string_index_of_char(name, '(')
  match first_paren {
    -1 => return None
    0 => return None
    _ => ()
  }
  let mut parens = 0
  let mut brackets = 0
  let mut angles = 0
  for i = 0; i < name.length(); i = i + 1 {
    let char = name[i].to_int().unsafe_to_char()
    if char == '(' && parens == 0 && brackets == 0 && angles == 0 {
      return Some(i)
    }
    if char == '(' {
      parens += 1
    } else if char == ')' && parens > 0 {
      parens -= 1
    } else if char == '[' {
      brackets += 1
    } else if char == ']' && brackets > 0 {
      brackets -= 1
    } else if char == '<' && parens == 0 && brackets == 0 {
      angles += 1
    } else if char == '>' && parens == 0 && brackets == 0 && angles > 0 {
      angles -= 1
    }
  }
  None
}

///|
fn pkl_constrained_type_suffix_is_balanced(name : String, start : Int) -> Bool {
  let mut parens = 0
  let mut brackets = 0
  let mut angles = 0
  for i = start; i < name.length(); i = i + 1 {
    let char = name[i].to_int().unsafe_to_char()
    if char == '(' {
      parens += 1
    } else if char == ')' {
      parens -= 1
      if parens < 0 {
        return false
      }
    } else if char == '[' {
      brackets += 1
    } else if char == ']' {
      brackets -= 1
      if brackets < 0 {
        return false
      }
    } else if char == '<' && parens == 0 && brackets == 0 {
      angles += 1
    } else if char == '>' && parens == 0 && brackets == 0 {
      angles -= 1
      if angles < 0 {
        return false
      }
    }
  }
  parens == 0 && brackets == 0 && angles == 0
}

///|
fn pkl_constrained_type_base_name(name : String) -> String? {
  match pkl_constrained_type_paren_index(name) {
    Some(index) =>
      if string_ends_with_char(name, ')') &&
        pkl_constrained_type_suffix_is_balanced(name, index) {
        Some(String::unsafe_substring(name, start=0, end=index))
      } else {
        None
      }
    None => None
  }
}

///|
fn pkl_constrained_type_constraint_text(name : String) -> String? {
  match pkl_constrained_type_paren_index(name) {
    Some(index) =>
      if string_ends_with_char(name, ')') &&
        pkl_constrained_type_suffix_is_balanced(name, index) {
        Some(
          String::unsafe_substring(name, start=index + 1, end=name.length() - 1),
        )
      } else {
        None
      }
    None => None
  }
}

///|
fn pkl_builtin_type_alias_target(name : String) -> String? {
  match name {
    "NonNull" => Some("Any(!(this is Null))")
    "UInt" => Some("Int(isPositive)")
    "UInt8" => Some("Int(isBetween(0, 255))")
    "UInt16" => Some("Int(isBetween(0, 65535))")
    "UInt32" => Some("Int(isBetween(0, 4294967295))")
    "Int8" => Some("Int(isBetween(-128, 127))")
    "Int16" => Some("Int(isBetween(-32768, 32767))")
    "Int32" => Some("Int(isBetween(-2147483648, 2147483647))")
    "Uri" => Some("String")
    _ => None
  }
}

///|
fn pkl_constrained_any_not_null_constraint_name(type_name : String) -> String? {
  match pkl_constrained_type_base_name(type_name) {
    Some("Any") =>
      match pkl_constrained_type_constraint_text(type_name) {
        Some(text) => {
          let parts = pkl_split_constraint_arguments(text)
          for part in parts {
            if pkl_constraint_trim(part) == "!(this is Null)" {
              return Some("!(this is Null)")
            }
          }
          None
        }
        None => None
      }
    _ => None
  }
}

///|
fn pkl_constraint_call_inner(text : String, prefix : String) -> String? {
  if text.has_prefix(prefix) &&
    text.has_suffix(")") &&
    text.length() >= prefix.length() + 1 {
    Some(
      String::unsafe_substring(
        text,
        start=prefix.length(),
        end=text.length() - 1,
      ),
    )
  } else {
    None
  }
}

///|
fn pkl_is_between_inner(text : String) -> String? {
  match pkl_constraint_call_inner(text, "isBetween(") {
    Some(inner) => Some(inner)
    None => pkl_constraint_call_inner(text, "this.isBetween(")
  }
}

///|
fn pkl_is_between_range(text : String) -> ConstraintIntRange? {
  match pkl_is_between_inner(text) {
    Some(inner) => {
      let parts = pkl_split_constraint_arguments(inner)
      if parts.length() != 2 {
        return None
      }
      match
        (
          pkl_parse_constraint_double_text(parts[0]),
          pkl_parse_constraint_double_text(parts[1]),
        ) {
        (Some(lower), Some(upper)) => Some({ lower, upper })
        _ => None
      }
    }
    None => None
  }
}

///|
fn pkl_constraint_property_matches(text : String, name : String) -> Bool {
  text == name || text == "this." + name
}

///|
fn pkl_single_int_constraint_argument(text : String, name : String) -> Double? {
  match pkl_constraint_call_inner(text, name + "(") {
    Some(inner) => pkl_parse_constraint_double_text(inner)
    None =>
      match pkl_constraint_call_inner(text, "this." + name + "(") {
        Some(inner) => pkl_parse_constraint_double_text(inner)
        None => None
      }
  }
}

///|
fn pkl_single_int_constraint_call(text : String) -> ConstraintIntCall? {
  let call_text = if text.has_prefix("this.") && text.length() > 5 {
    String::unsafe_substring(text, start=5, end=text.length())
  } else {
    text
  }
  match call_text.find("(") {
    Some(index) =>
      if index == 0 || !call_text.has_suffix(")") {
        None
      } else {
        let name = String::unsafe_substring(call_text, start=0, end=index)
        let inner = String::unsafe_substring(
          call_text,
          start=index + 1,
          end=call_text.length() - 1,
        )
        match pkl_parse_constraint_double_text(inner) {
          Some(argument) => Some({ name, argument })
          None => None
        }
      }
    None => None
  }
}

///|
fn pkl_int_constraint_predicate(text : String) -> ConstraintIntPredicate? {
  if text.has_prefix("!") && text.length() > 1 {
    let inner = String::unsafe_substring(text, start=1, end=text.length())
    match pkl_int_constraint_predicate(inner) {
      Some(IsBetween(lower, upper)) => return Some(NotIsBetween(lower, upper))
      Some(IsPositive) => return Some(NotIsPositive)
      Some(IsGreaterThan(threshold)) => return Some(NotIsGreaterThan(threshold))
      Some(IsLessThan(threshold)) => return Some(NotIsLessThan(threshold))
      Some(ThisCompare(op, t, leftish)) =>
        return Some(NotThisCompare(op, t, leftish))
      _ => return None
    }
  }
  match pkl_is_between_range(text) {
    Some(range) => return Some(IsBetween(range.lower, range.upper))
    None => ()
  }
  if pkl_constraint_property_matches(text, "isPositive") {
    return Some(IsPositive)
  }
  match pkl_single_int_constraint_argument(text, "isGreaterThan") {
    Some(threshold) => return Some(IsGreaterThan(threshold))
    None => ()
  }
  match pkl_single_int_constraint_argument(text, "isLessThan") {
    Some(threshold) => return Some(IsLessThan(threshold))
    None => ()
  }
  // PKL-148b: `this  N` bare comparison falls through last so it
  // doesn't shadow the named-predicate forms above.
  match pkl_parse_this_comparison(text) {
    Some(predicate) => return Some(predicate)
    None => ()
  }
  None
}

///|
fn pkl_user_defined_int_constraint_from_order(
  function_name : String,
  threshold : Double,
  value_on_left : Bool,
  op : BinaryOp,
) -> ConstraintIntPredicate? {
  match op {
    GreaterThan =>
      if value_on_left {
        Some(CustomGreaterThan(function_name, threshold))
      } else {
        Some(CustomLessThan(function_name, threshold))
      }
    LessThan =>
      if value_on_left {
        Some(CustomLessThan(function_name, threshold))
      } else {
        Some(CustomGreaterThan(function_name, threshold))
      }
    GreaterOrEqual =>
      if value_on_left {
        Some(CustomGreaterOrEqual(function_name, threshold))
      } else {
        Some(CustomLessOrEqual(function_name, threshold))
      }
    LessOrEqual =>
      if value_on_left {
        Some(CustomLessOrEqual(function_name, threshold))
      } else {
        Some(CustomGreaterOrEqual(function_name, threshold))
      }
    _ => None
  }
}

///|
fn pkl_user_defined_int_constraint_from_comparison(
  function_name : String,
  factory_parameter : String,
  threshold : Double,
  lambda_parameter : String,
  op : BinaryOp,
  left : Expr,
  right : Expr,
) -> ConstraintIntPredicate? {
  match (left, right) {
    (Identifier(left_name), Identifier(right_name)) =>
      if left_name == lambda_parameter && right_name == factory_parameter {
        pkl_user_defined_int_constraint_from_order(
          function_name, threshold, true, op,
        )
      } else if left_name == factory_parameter && right_name == lambda_parameter {
        pkl_user_defined_int_constraint_from_order(
          function_name, threshold, false, op,
        )
      } else {
        None
      }
    _ => None
  }
}

///|
fn pkl_user_defined_int_constraint_from_function_decl(
  function_decl : FunctionDecl,
  threshold : Double,
) -> ConstraintIntPredicate? {
  if function_decl.parameters.length() != 1 {
    return None
  }
  let factory_parameter = function_decl.parameters[0].name
  match function_decl.body {
    Some(LambdaExpr(lambda_parameters, BinaryExpr(op, left, right), _)) =>
      if lambda_parameters.length() == 1 {
        pkl_user_defined_int_constraint_from_comparison(
          function_decl.name,
          factory_parameter,
          threshold,
          lambda_parameters[0].name,
          op,
          left,
          right,
        )
      } else {
        None
      }
    _ => None
  }
}

///|
fn pkl_negate_user_defined_int_constraint(
  predicate : ConstraintIntPredicate,
) -> ConstraintIntPredicate? {
  match predicate {
    CustomGreaterThan(name, threshold) =>
      Some(NotCustomGreaterThan(name, threshold))
    CustomLessThan(name, threshold) => Some(NotCustomLessThan(name, threshold))
    CustomGreaterOrEqual(name, threshold) =>
      Some(NotCustomGreaterOrEqual(name, threshold))
    CustomLessOrEqual(name, threshold) =>
      Some(NotCustomLessOrEqual(name, threshold))
    _ => None
  }
}

///|
fn pkl_user_defined_int_constraint_predicate(
  text : String,
  declarations : Array[Declaration],
) -> ConstraintIntPredicate? {
  if text.has_prefix("!") && text.length() > 1 {
    let inner = String::unsafe_substring(text, start=1, end=text.length())
    match pkl_user_defined_int_constraint_predicate(inner, declarations) {
      Some(predicate) =>
        return pkl_negate_user_defined_int_constraint(predicate)
      None => return None
    }
  }
  match pkl_single_int_constraint_call(text) {
    Some(call) => {
      for declaration in declarations {
        match declaration {
          FunctionDeclaration(function_decl) =>
            if function_decl.name == call.name {
              return pkl_user_defined_int_constraint_from_function_decl(
                function_decl,
                call.argument,
              )
            }
          ClassDeclaration(_) | TypeAliasDeclaration(_) => ()
        }
      }
      None
    }
    None => None
  }
}

///|
fn pkl_constrained_int_predicates(
  type_name : String,
) -> Array[ConstraintIntPredicate] {
  let predicates : Array[ConstraintIntPredicate] = []
  // PKL-092: accept `Int(...)`, `Float(...)`, and `Number(...)` as the
  // numeric constraint host. The predicate-side encoding (Int thresholds)
  // is kept — Float values are widened from those thresholds when the
  // accepts-float helper runs the comparison.
  match pkl_constrained_type_base_name(type_name) {
    Some("Int") | Some("Float") | Some("Number") =>
      match pkl_constrained_type_constraint_text(type_name) {
        Some(text) => {
          let parts = pkl_split_constraint_arguments(text)
          for part in parts {
            match pkl_int_constraint_predicate(part) {
              Some(predicate) => predicates.push(predicate)
              None => ()
            }
          }
        }
        None => ()
      }
    _ => ()
  }
  predicates
}

///|
fn pkl_user_defined_constrained_int_predicates(
  type_name : String,
  declarations : Array[Declaration],
) -> Array[ConstraintIntPredicate] {
  let predicates : Array[ConstraintIntPredicate] = []
  match pkl_constrained_type_base_name(type_name) {
    Some("Int") =>
      match pkl_constrained_type_constraint_text(type_name) {
        Some(text) => {
          let parts = pkl_split_constraint_arguments(text)
          for part in parts {
            match
              pkl_user_defined_int_constraint_predicate(part, declarations) {
              Some(predicate) => predicates.push(predicate)
              None => ()
            }
          }
        }
        None => ()
      }
    _ => ()
  }
  predicates
}

///|
fn pkl_lookup_type_alias_target(
  declarations : Array[Declaration],
  name : String,
) -> String? {
  let mut found : String? = None
  for declaration in declarations {
    match declaration {
      TypeAliasDeclaration(type_alias) =>
        if type_alias.name == name {
          found = Some(type_alias.target)
        }
      ClassDeclaration(_) | FunctionDeclaration(_) => ()
    }
  }
  found
}

///|
fn pkl_user_defined_constrained_type_source_name_with_depth(
  name : String,
  declarations : Array[Declaration],
  depth : Int,
) -> String? {
  if depth > 8 {
    return None
  }
  if pkl_user_defined_constrained_int_predicates(name, declarations).length() >
    0 {
    return Some(name)
  }
  match pkl_lookup_type_alias_target(declarations, name) {
    Some(target) =>
      pkl_user_defined_constrained_type_source_name_with_depth(
        target,
        declarations,
        depth + 1,
      )
    None => None
  }
}

///|
fn pkl_user_defined_constrained_type_source_name(
  name : String,
  declarations : Array[Declaration],
) -> String? {
  pkl_user_defined_constrained_type_source_name_with_depth(
    name, declarations, 0,
  )
}

///|
fn pkl_constrained_type_annotation_has_supported_constraint(
  type_name : String,
) -> Bool {
  pkl_constrained_type_annotation_has_supported_constraint_with_depth(
    type_name, 0,
  )
}

///|
/// Recurse into collection wrappers so `Listing` and
/// `Mapping 0), Int>` register as supported. Depth is
/// capped to keep mutually-recursive aliases from looping the cascade.
fn pkl_constrained_type_annotation_has_supported_constraint_with_depth(
  type_name : String,
  depth : Int,
) -> Bool {
  if depth > 8 {
    return false
  }
  match pkl_builtin_type_alias_target(type_name) {
    Some(target) =>
      if pkl_constrained_type_annotation_has_supported_constraint_with_depth(
          target,
          depth + 1,
        ) {
        return true
      }
    None => ()
  }
  if pkl_constrained_int_predicates(type_name).length() > 0 ||
    pkl_constrained_string_predicates(type_name).length() > 0 ||
    pkl_constrained_any_not_null_constraint_name(type_name) is Some(_) {
    return true
  }
  match generic_argument_text(type_name, "Listing") {
    Some(inner_text) =>
      return pkl_constrained_type_annotation_has_supported_constraint_with_depth(
        inner_text,
        depth + 1,
      )
    None => ()
  }
  match generic_argument_text(type_name, "Mapping") {
    Some(inner_text) => {
      let parts = split_top_level_generic_arguments(inner_text)
      if parts.length() == 2 {
        if pkl_constrained_type_annotation_has_supported_constraint_with_depth(
            parts[0],
            depth + 1,
          ) ||
          pkl_constrained_type_annotation_has_supported_constraint_with_depth(
            parts[1],
            depth + 1,
          ) {
          return true
        }
      }
      return false
    }
    None => ()
  }
  false
}

///|
fn pkl_constraint_name(predicate : ConstraintIntPredicate) -> String {
  // PKL-148: include the argument list verbatim so the diagnostic
  // wording matches Apple Pkl's exact format (e.g. `isBetween(10, 20)`
  // rather than the bare `isBetween`). The threshold values are
  // formatted through `format_constraint_arg` so Int-magnitude
  // doubles render without a trailing `.0`.
  match predicate {
    IsBetween(lo, hi) =>
      "isBetween(\{format_constraint_arg(lo)}, \{format_constraint_arg(hi)})"
    IsPositive => "isPositive"
    IsGreaterThan(t) => "isGreaterThan(\{format_constraint_arg(t)})"
    IsLessThan(t) => "isLessThan(\{format_constraint_arg(t)})"
    NotIsBetween(lo, hi) =>
      "!isBetween(\{format_constraint_arg(lo)}, \{format_constraint_arg(hi)})"
    NotIsPositive => "!isPositive"
    NotIsGreaterThan(t) => "!isGreaterThan(\{format_constraint_arg(t)})"
    NotIsLessThan(t) => "!isLessThan(\{format_constraint_arg(t)})"
    CustomGreaterThan(name, t) => "\{name}(\{format_constraint_arg(t)})"
    CustomLessThan(name, t) => "\{name}(\{format_constraint_arg(t)})"
    CustomGreaterOrEqual(name, t) => "\{name}(\{format_constraint_arg(t)})"
    CustomLessOrEqual(name, t) => "\{name}(\{format_constraint_arg(t)})"
    NotCustomGreaterThan(name, t) =>
      "!" + name + "(\{format_constraint_arg(t)})"
    NotCustomLessThan(name, t) => "!" + name + "(\{format_constraint_arg(t)})"
    NotCustomGreaterOrEqual(name, t) =>
      "!" + name + "(\{format_constraint_arg(t)})"
    NotCustomLessOrEqual(name, t) =>
      "!" + name + "(\{format_constraint_arg(t)})"
    ThisCompare(op, t, this_on_left) =>
      if this_on_left {
        "this \{constraint_compare_op_text(op)} \{format_constraint_arg(t)}"
      } else {
        "\{format_constraint_arg(t)} \{constraint_compare_op_text(op)} this"
      }
    NotThisCompare(op, t, this_on_left) =>
      if this_on_left {
        "!(this \{constraint_compare_op_text(op)} \{format_constraint_arg(t)})"
      } else {
        "!(\{format_constraint_arg(t)} \{constraint_compare_op_text(op)} this)"
      }
  }
}

///|
fn constraint_compare_op_text(op : ConstraintCompareOp) -> String {
  match op {
    CmpGreaterThan => ">"
    CmpGreaterOrEqual => ">="
    CmpLessThan => "<"
    CmpLessOrEqual => "<="
    CmpEqual => "=="
    CmpNotEqual => "!="
  }
}

///|
fn _pkl_binary_op_to_compare(op : BinaryOp) -> ConstraintCompareOp? {
  match op {
    GreaterThan => Some(CmpGreaterThan)
    GreaterOrEqual => Some(CmpGreaterOrEqual)
    LessThan => Some(CmpLessThan)
    LessOrEqual => Some(CmpLessOrEqual)
    Equal => Some(CmpEqual)
    NotEqual => Some(CmpNotEqual)
    _ => None
  }
}

///|
/// PKL-148b: parse a `this  N` or `N  this` bare comparison
/// from the constraint text into a `ThisCompare` predicate. Whitespace
/// is normalised; the supported ops are >, >=, <, <=, ==, !=.
fn pkl_parse_this_comparison(text : String) -> ConstraintIntPredicate? {
  let trimmed = pkl_constraint_trim(text)
  let ops : Array[(String, ConstraintCompareOp)] = [
    (">=", CmpGreaterOrEqual),
    ("<=", CmpLessOrEqual),
    ("==", CmpEqual),
    ("!=", CmpNotEqual),
    (">", CmpGreaterThan),
    ("<", CmpLessThan),
  ]
  for op_pair in ops {
    let op_text = op_pair.0
    let op = op_pair.1
    match pkl_split_on_op(trimmed, op_text) {
      Some((left, right)) => {
        let left_trim = pkl_constraint_trim(left)
        let right_trim = pkl_constraint_trim(right)
        if left_trim == "this" {
          match pkl_parse_constraint_double_text(right_trim) {
            Some(n) => return Some(ThisCompare(op, n, true))
            None => ()
          }
        } else if right_trim == "this" {
          match pkl_parse_constraint_double_text(left_trim) {
            Some(n) => return Some(ThisCompare(op, n, false))
            None => ()
          }
        }
      }
      None => ()
    }
  }
  None
}

///|
fn pkl_split_on_op(text : String, op : String) -> (String, String)? {
  let n = text.length()
  let m = op.length()
  let mut i = 0
  while i + m <= n {
    let mut equal = true
    for j = 0; j < m; j = j + 1 {
      if text[i + j] != op[j] {
        equal = false
        break
      }
    }
    if equal {
      // Reject if the op text appears inside a longer operator (e.g.
      // matching `>` inside `>=`).
      let prev = if i > 0 {
        Some(text[i - 1].to_int().unsafe_to_char())
      } else {
        None
      }
      let next = if i + m < n {
        Some(text[i + m].to_int().unsafe_to_char())
      } else {
        None
      }
      let ambiguous = match (op, prev, next) {
        (">", _, Some('=')) | ("<", _, Some('=')) => true
        ("=", Some('='), _)
        | ("=", Some('!'), _)
        | ("=", Some('<'), _)
        | ("=", Some('>'), _) => true
        _ => false
      }
      if !ambiguous {
        return Some(
          (
            String::unsafe_substring(text, start=0, end=i),
            String::unsafe_substring(text, start=i + m, end=n),
          ),
        )
      }
    }
    i = i + 1
  }
  None
}

///|
/// PKL-148: format a constraint threshold for the diagnostic. Int-like
/// magnitudes (e.g. `10.0`) drop their trailing `.0` so the message
/// reads `isBetween(10, 20)` rather than `isBetween(10.0, 20.0)`.
fn format_constraint_arg(value : Double) -> String {
  if value.is_nan() || value.is_inf() {
    "\{value}"
  } else if value == value.floor() && value.abs() < 9.0e15 {
    "\{value.to_int64()}"
  } else {
    "\{value}"
  }
}

///|
fn pkl_constraint_predicate_accepts(
  predicate : ConstraintIntPredicate,
  value : Int64,
) -> Bool {
  // PKL-112: thresholds are stored as Double. Widen the Int side to
  // Double for the comparison so Int(isBetween(0, 10)) keeps working
  // alongside Float(isBetween(0.5, 1.5)).
  pkl_constraint_predicate_accepts_float(predicate, value.to_double())
}

///|
fn pkl_constrained_int_rejection_message_from_source(
  _display_name : String,
  source_name : String,
  value : Int64,
) -> String? {
  for predicate in pkl_constrained_int_predicates(source_name) {
    if !pkl_constraint_predicate_accepts(predicate, value) {
      return Some(
        // PKL-148: align with Apple Pkl's exact diagnostic wording so
        // snippetTest fixtures that capture this string via
        // `test.catch(...)` match byte-for-byte.
        "Type constraint `\{pkl_constraint_name(predicate)}` violated. Value: \{value}",
      )
    }
  }
  None
}

///|
fn pkl_user_defined_constrained_int_rejection_message_from_source(
  _display_name : String,
  source_name : String,
  value : Int64,
  declarations : Array[Declaration],
) -> String? {
  for
    predicate in pkl_user_defined_constrained_int_predicates(
      source_name, declarations,
    ) {
    if !pkl_constraint_predicate_accepts(predicate, value) {
      return Some(
        // PKL-148: align with Apple Pkl's exact diagnostic wording so
        // snippetTest fixtures that capture this string via
        // `test.catch(...)` match byte-for-byte.
        "Type constraint `\{pkl_constraint_name(predicate)}` violated. Value: \{value}",
      )
    }
  }
  None
}

///|
fn pkl_int_literal_value(expr : Expr) -> Int64? {
  match expr {
    IntLiteral(value) => Some(value)
    UnaryExpr(Negate, IntLiteral(value)) => Some(0L - value)
    _ => None
  }
}

///|
fn pkl_constrained_type_annotation_expr_rejection_message_from_source(
  display_name : String,
  source_name : String,
  expr : Expr,
) -> String? {
  match pkl_builtin_type_alias_target(source_name) {
    Some(target) =>
      return pkl_constrained_type_annotation_expr_rejection_message_from_source(
        display_name, target, expr,
      )
    None => ()
  }
  match expr {
    NullLiteral =>
      match pkl_constrained_any_not_null_constraint_name(source_name) {
        Some(name) =>
          return Some("Type constraint `\{name}` violated. Value: null")
        None => ()
      }
    _ => ()
  }
  match pkl_int_literal_value(expr) {
    Some(value) =>
      return pkl_constrained_int_rejection_message_from_source(
        display_name, source_name, value,
      )
    None => ()
  }
  match pkl_string_literal_value(expr) {
    Some(value) =>
      pkl_constrained_string_rejection_message_from_source(
        display_name, source_name, value,
      )
    None => None
  }
}

///|
fn pkl_string_literal_value(expr : Expr) -> String? {
  match expr {
    StringLiteral(value) => Some(value)
    _ => None
  }
}

///|
fn pkl_user_defined_constrained_type_annotation_expr_rejection_message(
  type_name : String?,
  expr : Expr,
  declarations : Array[Declaration],
) -> String? {
  match type_name {
    Some(display_name) =>
      match
        pkl_user_defined_constrained_type_source_name(
          display_name, declarations,
        ) {
        Some(source_name) =>
          match pkl_int_literal_value(expr) {
            Some(value) =>
              pkl_user_defined_constrained_int_rejection_message_from_source(
                display_name, source_name, value, declarations,
              )
            None => None
          }
        None => None
      }
    None => None
  }
}

///|
fn pkl_user_defined_constrained_type_annotation_expr_rejection_message_from_source(
  display_name : String,
  source_name : String,
  expr : Expr,
  declarations : Array[Declaration],
) -> String? {
  match
    pkl_user_defined_constrained_type_source_name(source_name, declarations) {
    Some(resolved_source_name) =>
      match pkl_int_literal_value(expr) {
        Some(value) =>
          pkl_user_defined_constrained_int_rejection_message_from_source(
            display_name, resolved_source_name, value, declarations,
          )
        None => None
      }
    None => None
  }
}