///|
/// Wrap the colour of each comma-separated shadow layer in
/// `var(, )` so `shadow-` utilities can recolour
/// it. Handles multi-layer shadows and colours with internal spaces such as
/// `rgb(0 0 0 / 0.1)`.
fn shadow_value_with_color(value : String, custom_property : String) -> String {
  let layers = split_function_arguments(value)
  if layers.length() == 0 {
    return value
  }
  let wrapped = layers.map(fn(layer) {
    let nodes = parse_value(layer)
    // The colour is the last non-separator token in the layer.
    let mut index = -1
    for i, node in nodes {
      if !(node is ValueSeparator(_)) {
        index = i
      }
    }
    if index < 0 {
      return layer
    }
    let is_color = match nodes[index] {
      ValueFunction(name, _) =>
        ["rgb", "rgba", "hsl", "hsla", "oklch", "oklab", "hwb", "color"].contains(
          name,
        )
      ValueWord(word) => word.has_prefix("#")
      _ => false
    }
    if is_color {
      let rendered = render_value([nodes[index]])
      nodes[index] = ValueWord("var(\{custom_property}, \{rendered})")
      render_value(nodes)
    } else {
      layer
    }
  })
  wrapped.join(", ")
}

///|
fn shadow_ring_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  let shadow_stack = "var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)"
  if name == "inset-ring" || name.has_prefix("inset-ring-") {
    let key = if name == "inset-ring" { "" } else { name[11:].to_owned() }
    let width = if key == "" {
      Some("1px")
    } else {
      match arbitrary_value(key) {
        Some(value) => Some(value)
        None =>
          if is_nonnegative_integer(key) {
            Some("\{key}px")
          } else {
            theme_css_value(theme, "--inset-ring-width-\{key}")
          }
      }
    }
    match width {
      Some(value) =>
        return Some([
          decl(
            "--tw-inset-ring-shadow",
            "inset 0 0 0 \{value} var(--tw-inset-ring-color, currentcolor)",
          ),
          decl("box-shadow", shadow_stack),
        ])
      None =>
        match theme_css_value(theme, "--color-\{key}") {
          Some(value) => return Some([decl("--tw-inset-ring-color", value)])
          None => return None
        }
    }
  }
  if name == "ring" || name.has_prefix("ring-") {
    let key = if name == "ring" { "" } else { name[5:].to_owned() }
    if key.has_prefix("offset-") {
      let offset_key = key[7:].to_owned()
      let width = match arbitrary_value(offset_key) {
        Some(value) => Some(value)
        None =>
          if is_nonnegative_integer(offset_key) {
            Some("\{offset_key}px")
          } else {
            theme_css_value(theme, "--ring-offset-width-\{offset_key}")
          }
      }
      match width {
        Some(value) =>
          return Some([
            decl("--tw-ring-offset-width", value),
            decl(
              "--tw-ring-offset-shadow", "var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)",
            ),
          ])
        None =>
          match theme_css_value(theme, "--color-\{offset_key}") {
            Some(value) => return Some([decl("--tw-ring-offset-color", value)])
            None => return None
          }
      }
    }
    let width = if key == "" {
      Some("1px")
    } else {
      match arbitrary_value(key) {
        Some(value) => Some(value)
        None =>
          if is_nonnegative_integer(key) {
            Some("\{key}px")
          } else {
            theme_css_value(theme, "--ring-width-\{key}")
          }
      }
    }
    match width {
      Some(value) =>
        return Some([
          decl(
            "--tw-ring-shadow",
            "var(--tw-ring-inset,) 0 0 0 calc(\{value} + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor)",
          ),
          decl("box-shadow", shadow_stack),
        ])
      None =>
        match theme_css_value(theme, "--color-\{key}") {
          Some(value) => return Some([decl("--tw-ring-color", value)])
          None => return None
        }
    }
  }
  let (root, theme_namespace, property, color_property) = if name.has_prefix(
      "inset-shadow-",
    ) {
    (
      "inset-shadow", "--inset-shadow", "--tw-inset-shadow", "--tw-inset-shadow-color",
    )
  } else if name.has_prefix("shadow-") || name == "shadow" {
    ("shadow", "--shadow", "--tw-shadow", "--tw-shadow-color")
  } else {
    return None
  }
  let key = if name == root {
    ""
  } else {
    guard prefixed_value(name, root) is Some(key) else { return None }
    key
  }
  if key == "none" {
    return Some([decl(property, "0 0 #0000"), decl("box-shadow", shadow_stack)])
  }
  match
    (theme.get("--color-\{key}"), theme_css_value(theme, "--color-\{key}")) {
    (Some(literal), Some(value)) => {
      let alpha_property = "\{color_property[:color_property.length() - 6]}-alpha"
      return Some([
        {
          property: color_property,
          value: "color-mix(in oklab, \{value} var(\{alpha_property}), transparent)",
          fallback: Some(literal),
        },
      ])
    }
    _ => ()
  }
  let value = if key == "" {
    theme.get(theme_namespace)
  } else {
    match arbitrary_value(key) {
      Some(value) => Some(value)
      None => theme.get("\{theme_namespace}-\{key}")
    }
  }
  value.map(fn(value) {
    [
      decl(property, shadow_value_with_color(value, color_property)),
      decl("box-shadow", shadow_stack),
    ]
  })
}

///|
fn text_shadow_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  guard prefixed_value(name, "text-shadow") is Some(key) else { return None }
  match
    (theme.get("--color-\{key}"), theme_css_value(theme, "--color-\{key}")) {
    (Some(literal), Some(value)) =>
      return Some([
        {
          property: "--tw-text-shadow-color",
          value: "color-mix(in oklab, \{value} var(--tw-text-shadow-alpha), transparent)",
          fallback: Some(literal),
        },
      ])
    _ => ()
  }
  let value = match arbitrary_value(key) {
    Some(value) => Some(value)
    None => theme.get("--text-shadow-\{key}")
  }
  value.map(fn(value) {
    [
      decl(
        "text-shadow",
        shadow_value_with_color(value, "--tw-text-shadow-color"),
      ),
    ]
  })
}

///|
fn gradient_color_or_position(
  theme : Map[String, String],
  key : String,
) -> (String, Bool)? {
  if key.has_suffix("%") {
    return Some((key, true))
  }
  match arbitrary_value(key) {
    Some(value) =>
      Some(
        (
          value,
          value.has_suffix("%") ||
          value.has_suffix("px") ||
          value.has_suffix("rem"),
        ),
      )
    None =>
      match theme_css_value(theme, "--color-\{key}") {
        Some(value) => Some((value, false))
        None => None
      }
  }
}

///|
fn gradient_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  let directions : Array[(String, String)] = [
    ("bg-linear-to-t", "to top"),
    ("bg-linear-to-tr", "to top right"),
    ("bg-linear-to-r", "to right"),
    ("bg-linear-to-br", "to bottom right"),
    ("bg-linear-to-b", "to bottom"),
    ("bg-linear-to-bl", "to bottom left"),
    ("bg-linear-to-l", "to left"),
    ("bg-linear-to-tl", "to top left"),
  ]
  for entry in directions {
    let (candidate, direction) = entry
    if name == candidate {
      return Some([
        decl("--tw-gradient-position", direction),
        decl("background-image", "linear-gradient(var(--tw-gradient-stops))"),
      ])
    }
  }
  if name.has_prefix("bg-linear-") {
    let key = name[10:].to_owned()
    match arbitrary_value(key) {
      Some(value) =>
        return Some([
          decl("--tw-gradient-position", value),
          decl(
            "background-image",
            "linear-gradient(var(--tw-gradient-stops,\{value}))",
          ),
        ])
      None => ()
    }
  }
  let (root, color_property, position_property) = if name.has_prefix("from-") {
    ("from", "--tw-gradient-from", "--tw-gradient-from-position")
  } else if name.has_prefix("via-") {
    ("via", "--tw-gradient-via", "--tw-gradient-via-position")
  } else if name.has_prefix("to-") {
    ("to", "--tw-gradient-to", "--tw-gradient-to-position")
  } else {
    return None
  }
  guard prefixed_value(name, root) is Some(key) else { return None }
  guard gradient_color_or_position(theme, key) is Some((value, position)) else {
    return None
  }
  if position {
    return Some([decl(position_property, value)])
  }
  let standard_stops = "var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))"
  match root {
    "via" =>
      Some([
        decl(color_property, value),
        decl(
          "--tw-gradient-via-stops", "var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position)",
        ),
        decl("--tw-gradient-stops", "var(--tw-gradient-via-stops)"),
      ])
    _ =>
      Some([
        decl(color_property, value),
        decl("--tw-gradient-stops", standard_stops),
      ])
  }
}

///|
fn stateful_effect_utility(
  theme : Map[String, String],
  name : String,
) -> Array[Declaration]? {
  match shadow_ring_utility(theme, name) {
    Some(value) => return Some(value)
    None => ()
  }
  match text_shadow_utility(theme, name) {
    Some(value) => return Some(value)
    None => gradient_utility(theme, name)
  }
}