///|
pub fn stable_hash(text : String) -> Int {
  let mut h = 216613
  for ch in text {
    let code = ch.to_int()
    h = (h * 167 + code + 97) % 2147483
  }
  if h < 0 {
    -h
  } else {
    h
  }
}

///|
pub fn rollout_bucket(user_id : String, salt : String) -> Int {
  stable_hash(salt + ":" + user_id) % 10000
}

///|
pub fn in_percent_bucket(
  user_id : String,
  salt : String,
  percent_bp : Int,
) -> Bool {
  let safe_percent = clamp(percent_bp, 0, 10000)
  rollout_bucket(user_id, salt) < safe_percent
}

///|
pub fn cohort_label(
  user_id : String,
  salt : String,
  ring_count : Int,
) -> String {
  let safe_count = if ring_count <= 0 { 1 } else { ring_count }
  let index = stable_hash("ring:" + salt + ":" + user_id) % safe_count
  "ring-" + index.to_string()
}

///|
pub fn split_bucket(bucket_bp : Int) -> String {
  if bucket_bp < 1000 {
    "canary-seed"
  } else if bucket_bp < 3000 {
    "early-adopter"
  } else if bucket_bp < 7000 {
    "mainline"
  } else if bucket_bp < 9000 {
    "late-majority"
  } else {
    "safety-tail"
  }
}

///|
pub fn stable_sample_key(user : UserProfile, salt : String) -> String {
  user.id +
  ":" +
  region_text(user.region) +
  ":" +
  tier_text(user.tier) +
  ":" +
  salt
}

///|
pub fn traffic_weighted_bucket(user : UserProfile, salt : String) -> Int {
  let base = rollout_bucket(user.id, salt)
  let weight = clamp(user.traffic_units, 1, 100)
  (base + weight * 37) % 10000
}