///|
pub(all) enum BoxSizeKeyword {
  SizeAuto
  SizeNone
  SizeMinContent
  SizeMaxContent
  SizeFitContent
} derive(Eq, Debug)

///|
pub fn box_size_keyword(value : BoxSizeKeyword) -> BoxSize {
  {
    expression: literal_expression(
      match value {
        SizeAuto => "auto"
        SizeNone => "none"
        SizeMinContent => "min-content"
        SizeMaxContent => "max-content"
        SizeFitContent => "fit-content"
      },
      "box-size",
    ),
  }
}

///|
/// Intrinsic box sizing is distinct from the Grid `fit_content` constructor.
pub fn[T : BoxSizeValueInput] fit_content_size(limit : T) -> BoxSize {
  {
    expression: group_expression(
      "box-size",
      "fit-content(",
      [radius_operand(limit.value_expression())],
      "",
      ")",
    ),
  }
}

///|
pub fn basis_content() -> FlexBasis {
  { expression: literal_expression("content", "flex-basis"), }
}

///|
pub impl FlexBasisValueInput for BoxSize with fn flex_basis_value_expression(
  self,
) {
  self.expression
}

///|
pub(all) enum FontSizeKeyword {
  FontXXSmall
  FontXSmall
  FontSmall
  FontMedium
  FontLarge
  FontXLarge
  FontXXLarge
  FontXXXLarge
  FontSmaller
  FontLarger
} derive(Eq, Debug)

///|
pub fn font_size_keyword(value : FontSizeKeyword) -> FontSize {
  {
    expression: literal_expression(
      match value {
        FontXXSmall => "xx-small"
        FontXSmall => "x-small"
        FontSmall => "small"
        FontMedium => "medium"
        FontLarge => "large"
        FontXLarge => "x-large"
        FontXXLarge => "xx-large"
        FontXXXLarge => "xxx-large"
        FontSmaller => "smaller"
        FontLarger => "larger"
      },
      "font-size",
    ),
  }
}

///|
pub fn line_height_normal() -> LineHeight {
  { expression: literal_expression("normal", "line-height"), }
}

///|
// Range constraints apply to an authored atom, not to all operands of calc.
// CSS evaluates math ranges after arithmetic; negative intermediate operands
// are legal and cannot be evaluated here without layout information.
fn atom_nonnegative(value : Expression) -> Expression {
  fn computation(node : ValueNode) -> Bool {
    match node {
      Group(_, _, _, _) => true
      Constrained(_, child) => computation(child)
      _ => false
    }
  }
  if computation(value.node) {
    value
  } else {
    value.constrain("nonnegative")
  }
}

///|
fn radius_operand(value : Expression) -> Expression {
  if value.category == "box-size" {
    return literal_expression(
      issue_css("STYLE_MATH_DIMENSION", "Expected a length or percentage"),
      "invalid",
    )
  }
  match value.node {
    Group(_, _, _, _) => atom_nonnegative(value)
    _ => atom_nonnegative(value.constrain("math:length-percentage"))
  }
}

///|
pub struct BorderRadii {
  expression : Expression
} derive(Eq, Debug)

///|
/// Builds the CSS horizontal / vertical radius lists, each with 1–4 entries.
pub fn border_radii(
  horizontal : Array[MathExpr[LengthPercentage]],
  vertical? : Array[MathExpr[LengthPercentage]],
) -> BorderRadii {
  fn list(values : Array[MathExpr[LengthPercentage]]) -> Expression {
    if values.is_empty() || values.length() > 4 {
      literal_expression(
        issue_css(
          "STYLE_RADIUS_COUNT", "A radius list requires one to four values",
        ),
        "border-radii",
      )
    } else {
      group_expression(
        "border-radii",
        "",
        values.map(value => radius_operand(value.expression)),
        " ",
        "",
      )
    }
  }
  let horizontal = list(horizontal)
  {
    expression: match vertical {
      Some(values) =>
        group_expression(
          "border-radii",
          "",
          [horizontal, list(values)],
          " / ",
          "",
        )
      None => horizontal
    },
  }
}

///|
pub fn[T : BoxSizeValueInput] single_radius(value : T) -> BorderRadii {
  { expression: radius_operand(value.value_expression()), }
}

///|
pub fn BorderRadii::expression(self : BorderRadii) -> Expression {
  self.expression
}

///|
pub fn BorderRadii::to_css(self : BorderRadii) -> String {
  self.expression.to_css()
}

///|
pub fn[T : BoxSizeValueInput] CornerRadius::vertical(
  self : CornerRadius,
  value : T,
) -> CornerRadius {
  {
    expression: group_expression(
      "corner-radius",
      "",
      [self.horizontal, radius_operand(value.value_expression())],
      " ",
      "",
    ),
    horizontal: self.horizontal,
  }
}

///|
pub fn CornerRadius::expression(self : CornerRadius) -> Expression {
  self.expression
}

///|
pub impl BoxSizeContractExpression for CornerRadius with fn box_size_contract_expression(
  self,
) {
  self.expression.identity()
}

///|
pub impl BoxSizeContractExpression for BorderRadii with fn box_size_contract_expression(
  self,
) {
  self.expression.identity()
}

///|
pub impl FlexBasisContractExpression for BoxSize with fn flex_basis_contract_expression(
  self,
) {
  self.expression.identity()
}

///|
fn common_value_property(property : String) -> Bool {
  [
    "width", "height", "minWidth", "maxWidth", "minHeight", "maxHeight", "min-width",
    "max-width", "min-height", "max-height", "inlineSize", "blockSize", "minInlineSize",
    "maxInlineSize", "minBlockSize", "maxBlockSize", "inline-size", "block-size",
    "min-inline-size", "max-inline-size", "min-block-size", "max-block-size", "flexBasis",
    "flex-basis", "fontSize", "font-size", "lineHeight", "line-height", "borderRadius",
    "border-radius", "borderStartStartRadius", "borderStartEndRadius", "borderEndStartRadius",
    "borderEndEndRadius", "borderTopLeftRadius", "borderTopRightRadius", "borderBottomLeftRadius",
    "borderBottomRightRadius",
  ].contains(property)
}

///|
fn common_property_keyword_valid(property : String, value : String) -> Bool {
  let maximum = [
    "maxWidth", "maxHeight", "maxInlineSize", "maxBlockSize", "max-width", "max-height",
    "max-inline-size", "max-block-size",
  ].contains(property)
  let font = property == "fontSize" || property == "font-size"
  let line = property == "lineHeight" || property == "line-height"
  let radius = property.contains("Radius") || property.contains("radius")
  let basis = property == "flexBasis" || property == "flex-basis"
  if value == "auto" {
    return !maximum && !font && !line && !radius
  }
  if value == "none" {
    return maximum
  }
  if value == "content" {
    return basis
  }
  if ["min-content", "max-content", "fit-content"].contains(value) {
    return !font && !line && !radius
  }
  if value == "normal" {
    return line
  }
  if [
      "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "xxx-large",
      "smaller", "larger",
    ].contains(value) {
    return font
  }
  true
}