///|
fn stateful_numeric_value(
  theme : Map[String, String],
  key : String,
  theme_namespace : String,
  unit : String,
) -> String? {
  match arbitrary_value(key) {
    Some(value) => Some(value)
    None =>
      match theme_css_value(theme, "\{theme_namespace}-\{key}") {
        Some(value) => Some(value)
        None =>
          if is_spacing_number(key) {
            if unit == "" {
              Some(key)
            } else {
              Some("\{key}\{unit}")
            }
          } else {
            None
          }
      }
  }
}

///|
fn transform_origin_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  guard prefixed_value(name, "origin") is Some(key) else { return None }
  let keywords : Array[(String, String)] = [
    ("center", "center"),
    ("top", "top"),
    ("top-right", "100% 0"),
    ("right", "100%"),
    ("bottom-right", "100% 100%"),
    ("bottom", "bottom"),
    ("bottom-left", "0 100%"),
    ("left", "0"),
    ("top-left", "0 0"),
  ]
  let value = match arbitrary_value(key) {
    Some(value) => Some(value)
    None =>
      match theme_css_value(theme, "--transform-origin-\{key}") {
        // A theme key wins over the built-in keyword, as it does everywhere.
        Some(value) => Some(value)
        None => {
          let mut keyword = None
          for entry in keywords {
            let (candidate, value) = entry
            if key == candidate {
              keyword = Some(value)
              break
            }
          }
          keyword
        }
      }
  }
  value.map(fn(value) { [decl("transform-origin", value)] })
}

///|
fn translate_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  if name == "translate-3d" {
    return Some([
      decl(
        "translate", "var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z)",
      ),
    ])
  }
  let (root, properties) = if name.has_prefix("translate-x-") {
    ("translate-x", ["--tw-translate-x"])
  } else if name.has_prefix("translate-y-") {
    ("translate-y", ["--tw-translate-y"])
  } else if name.has_prefix("translate-z-") {
    ("translate-z", ["--tw-translate-z"])
  } else if name.has_prefix("translate-") {
    ("translate", ["--tw-translate-x", "--tw-translate-y"])
  } else {
    return None
  }
  guard prefixed_value(name, root) is Some(key) else { return None }
  // `translate-full` and its two axes are upstream statics; the z axis has none.
  let value = if key == "full" && root != "translate-z" {
    Some("100%")
  } else {
    match theme_css_value(theme, "--translate-\{key}") {
      Some(value) => Some(value)
      None => resolved_value(theme, key, "--translate", true)
    }
  }
  guard value is Some(value) else { return None }
  let declarations = properties.map(fn(property) { decl(property, value) })
  if root == "translate-z" {
    declarations.push(
      decl(
        "translate", "var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z)",
      ),
    )
  } else {
    declarations.push(
      decl("translate", "var(--tw-translate-x) var(--tw-translate-y)"),
    )
  }
  Some(declarations)
}

///|
fn rotate_skew_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  // `skew-*` drives both axes; every other member drives one, and bare
  // `rotate-*` sets the `rotate` property instead of a transform function.
  let (root, functions) : (String, Array[(String, String)]) = if name.has_prefix(
      "rotate-x-",
    ) {
    ("rotate-x", [("--tw-rotate-x", "rotateX")])
  } else if name.has_prefix("rotate-y-") {
    ("rotate-y", [("--tw-rotate-y", "rotateY")])
  } else if name.has_prefix("rotate-z-") {
    ("rotate-z", [("--tw-rotate-z", "rotateZ")])
  } else if name.has_prefix("skew-x-") {
    ("skew-x", [("--tw-skew-x", "skewX")])
  } else if name.has_prefix("skew-y-") {
    ("skew-y", [("--tw-skew-y", "skewY")])
  } else if name.has_prefix("skew-") {
    ("skew", [("--tw-skew-x", "skewX"), ("--tw-skew-y", "skewY")])
  } else if name.has_prefix("rotate-") {
    ("rotate", [])
  } else {
    return None
  }
  guard prefixed_value(name, root) is Some(key) else { return None }
  let theme_namespace = if root.has_prefix("skew") {
    "--skew"
  } else {
    "--rotate"
  }
  guard stateful_numeric_value(theme, key, theme_namespace, "deg")
    is Some(value) else {
    return None
  }
  if functions.is_empty() {
    return Some([decl("rotate", value)])
  }
  let declarations = functions.map(fn(entry) {
    let (property, function_name) = entry
    decl(property, "\{function_name}(\{value})")
  })
  declarations.push(
    decl(
      "transform", "var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)",
    ),
  )
  Some(declarations)
}

///|
fn scale_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  let (root, properties) = if name.has_prefix("scale-x-") {
    ("scale-x", ["--tw-scale-x"])
  } else if name.has_prefix("scale-y-") {
    ("scale-y", ["--tw-scale-y"])
  } else if name.has_prefix("scale-z-") {
    ("scale-z", ["--tw-scale-z"])
  } else if name.has_prefix("scale-") {
    ("scale", ["--tw-scale-x", "--tw-scale-y", "--tw-scale-z"])
  } else {
    return None
  }
  guard prefixed_value(name, root) is Some(key) else { return None }
  // `scale-[2]` sets the property itself rather than the per-axis pair.
  if root == "scale" && arbitrary_value(key) is Some(value) {
    return Some([decl("scale", value)])
  }
  let value = match arbitrary_value(key) {
    Some(value) => Some(value)
    None =>
      match theme_css_value(theme, "--scale-\{key}") {
        Some(value) => Some(value)
        // Upstream takes a positive integer here, so `scale-1.5` is no candidate.
        None => if is_nonnegative_integer(key) { Some("\{key}%") } else { None }
      }
  }
  guard value is Some(value) else { return None }
  let declarations = properties.map(fn(property) { decl(property, value) })
  // Touching the z axis puts it in the shorthand too.
  declarations.push(
    decl(
      "scale",
      if root == "scale-z" {
        "var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)"
      } else {
        "var(--tw-scale-x) var(--tw-scale-y)"
      },
    ),
  )
  Some(declarations)
}

///|
fn perspective_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  if name.has_prefix("perspective-origin-") {
    let key = name[19:].to_owned()
    // A theme key resolves under both `--perspective-origin-*` and, read as
    // `perspective-origin-`, under `--perspective-*`, so upstream compiles
    // the candidate against both roots. An arbitrary value only matches the
    // `perspective-origin` root.
    match arbitrary_value(key) {
      Some(value) => return Some([decl("perspective-origin", value)])
      None => ()
    }
    return theme_css_value(theme, "--perspective-origin-\{key}").map(fn(value) {
      [decl("perspective-origin", value), decl("perspective", value)]
    })
  }
  guard prefixed_value(name, "perspective") is Some(key) else { return None }
  let value = match arbitrary_value(key) {
    Some(value) => Some(value)
    None =>
      match theme_css_value(theme, "--perspective-\{key}") {
        Some(value) => Some(value)
        None => if key == "none" { Some("none") } else { None }
      }
  }
  value.map(fn(value) { [decl("perspective", value)] })
}

///|
fn stateful_transform_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  match name {
    "transform-flat" => return Some([decl("transform-style", "flat")])
    "transform-3d" => return Some([decl("transform-style", "preserve-3d")])
    "transform-none" => return Some([decl("transform", "none")])
    "transform" | "transform-cpu" =>
      return Some([
        decl(
          "transform", "var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)",
        ),
      ])
    "transform-gpu" =>
      return Some([
        decl(
          "transform", "translateZ(0) var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)",
        ),
      ])
    "transform-view" => return Some([decl("transform-box", "view-box")])
    _ => ()
  }
  // `transform-[scaleZ(2)_rotateY(45deg)]` writes the property outright.
  if prefixed_value(name, "transform") is Some(key) {
    match arbitrary_value(key) {
      Some(value) => return Some([decl("transform", value)])
      None => ()
    }
  }
  match transform_origin_utility(theme, name) {
    Some(value) => return Some(value)
    None => ()
  }
  match translate_utility(theme, name) {
    Some(value) => return Some(value)
    None => ()
  }
  match rotate_skew_utility(theme, name) {
    Some(value) => return Some(value)
    None => ()
  }
  match scale_utility(theme, name) {
    Some(value) => return Some(value)
    None => perspective_utility(theme, name)
  }
}