///|
/// Parse display value
pub fn parse_display(value : String) -> @types.Display {
  let normalized = value.trim().to_lower().to_owned()
  let parts = split_whitespace(normalized)
  if parts.length() == 0 {
    return Block
  }
  if parts.length() == 2 {
    let first = parts[0]
    let second = parts[1]
    if (first == "math" && second == "inline") ||
      (first == "inline" && second == "math") {
      return Inline
    }
    if (first == "math" && second == "block") ||
      (first == "block" && second == "math") {
      return Block
    }
    if (first == "flow-root" && second == "list-item") ||
      (first == "list-item" && second == "flow-root") {
      return FlowRoot
    }
  }
  if parts.length() > 1 {
    return Block
  }
  match parts[0] {
    "block" => Block
    "inline" => Inline
    "inline-block" => InlineBlock
    "flex" => Flex
    "inline-flex" => InlineFlex
    "grid" => Grid
    "inline-grid" => InlineGrid
    "none" => None
    "contents" => Contents
    "flow-root" => FlowRoot
    "math" => Inline
    // Table display types
    "table" => Table
    "inline-table" => InlineTable
    "table-row" => TableRow
    "table-cell" => TableCell
    "table-caption" => TableCaption
    "table-row-group" => TableRowGroup
    "table-header-group" => TableHeaderGroup
    "table-footer-group" => TableFooterGroup
    "table-column" => TableColumn
    "table-column-group" => TableColumnGroup
    // Ruby internal display types are inline-level for layout-tree comparison.
    // (Full ruby formatting context is not yet implemented.)
    "ruby" => Inline
    "ruby-base" => Inline
    "ruby-base-container" => Inline
    "ruby-text" => Inline
    "ruby-text-container" => Inline
    _ => Block
  }
}

///|
/// Parse position value
pub fn parse_position(value : String) -> @types.Position {
  match value.trim().to_lower() {
    "static" => Static
    "absolute" => Absolute
    "fixed" => Fixed
    "relative" | "sticky" => Relative
    _ => Static // Default is static
  }
}

///|
/// Parse float value
pub fn parse_float(value : String) -> @types.Float {
  match value.trim().to_lower() {
    "left" => Left
    "right" => Right
    "none" => None
    _ => None
  }
}

///|
pub fn parse_float_with_direction(
  value : String,
  direction : @style.Direction,
) -> @types.Float {
  match value.trim().to_lower() {
    "inline-start" => if direction.is_rtl() { Right } else { Left }
    "inline-end" => if direction.is_rtl() { Left } else { Right }
    _ => parse_float(value)
  }
}

///|
/// Parse clear value
pub fn parse_clear(value : String) -> @types.Clear {
  match value.trim().to_lower() {
    "left" => Left
    "right" => Right
    "both" => Both
    "none" => None
    _ => None
  }
}

///|
pub fn parse_clear_with_direction(
  value : String,
  direction : @style.Direction,
) -> @types.Clear {
  match value.trim().to_lower() {
    "inline-start" => if direction.is_rtl() { Right } else { Left }
    "inline-end" => if direction.is_rtl() { Left } else { Right }
    _ => parse_clear(value)
  }
}

///|
/// Parse flex-direction value
pub fn parse_flex_direction(value : String) -> @types.FlexDirection {
  match value.trim().to_lower() {
    "row" => Row
    "row-reverse" => RowReverse
    "column" => Column
    "column-reverse" => ColumnReverse
    _ => Row
  }
}

///|
/// Parse flex-wrap value
pub fn parse_flex_wrap(value : String) -> @types.FlexWrap {
  match value.trim().to_lower() {
    "nowrap" => NoWrap
    "wrap" => Wrap
    "wrap-reverse" => WrapReverse
    _ => NoWrap
  }
}

///|
fn tokenize_alignment_value(value : String) -> Array[String] {
  value
  .trim()
  .to_lower()
  .split(" ")
  .map(fn(part) { part.to_owned().trim().to_owned() })
  .filter(fn(part) { !part.is_empty() })
  .collect()
}

///|
fn parse_alignment_keyword(value : String) -> @types.Alignment {
  match value {
    "flex-start" => FlexStart
    "flex-end" => FlexEnd
    "start" => Start
    "end" => End
    "left" => Left
    "right" => Right
    "center" => Center
    "space-between" => SpaceBetween
    "space-around" => SpaceAround
    "space-evenly" => SpaceEvenly
    "stretch" => Stretch
    "baseline" | "first baseline" => Baseline
    // Fallback alignment for last-baseline in axis alignment.
    "last baseline" => End
    _ => FlexStart
  }
}

///|
fn parse_alignment_segment(
  tokens : Array[String],
  start : Int,
) -> (String, Int)? {
  if start >= tokens.length() {
    return None
  }
  let head = tokens[start]
  if start + 1 < tokens.length() {
    let next = tokens[start + 1]
    if head == "safe" ||
      head == "unsafe" ||
      ((head == "first" || head == "last") && next == "baseline") {
      return Some((head + " " + next, start + 2))
    }
  }
  Some((head, start + 1))
}

///|
/// Parse alignment value (justify-content, align-items, align-content)
/// Note: flex-start/flex-end are flex-relative (affected by wrap-reverse)
///       start/end are physical/logical (not affected by wrap-reverse)
pub fn parse_alignment_with_overflow(
  value : String,
) -> (@types.Alignment, Bool) {
  let tokens = tokenize_alignment_value(value)
  if tokens.is_empty() {
    return (FlexStart, false)
  }
  let mut offset = 0
  let mut is_safe = false
  let mut is_unsafe = false
  if tokens[0] == "safe" {
    is_safe = true
    offset = 1
  } else if tokens[0] == "unsafe" {
    is_unsafe = true
    offset = 1
  }
  if offset >= tokens.length() {
    return (FlexStart, false)
  }
  let keyword = if offset + 1 < tokens.length() &&
    tokens[offset + 1] == "baseline" &&
    (tokens[offset] == "first" || tokens[offset] == "last") {
    tokens[offset] + " baseline"
  } else {
    tokens[offset]
  }
  let parsed = parse_alignment_keyword(keyword)
  let parsed = if is_safe {
    match parsed {
      Center | End | FlexEnd => @types.Alignment::Start
      _ => parsed
    }
  } else {
    parsed
  }
  (parsed, is_unsafe)
}

///|
pub fn parse_alignment(value : String) -> @types.Alignment {
  let (alignment, _) = parse_alignment_with_overflow(value)
  alignment
}

///|
/// Parse place-content shorthand.
/// Returns (align_content, justify_content).
pub fn parse_place_content_with_overflow(
  value : String,
) -> ((@types.Alignment, Bool), (@types.Alignment, Bool))? {
  let tokens = tokenize_alignment_value(value)
  if tokens.is_empty() {
    return None
  }
  match parse_alignment_segment(tokens, 0) {
    Some((first, next_index)) => {
      let first_alignment = parse_alignment_with_overflow(first)
      if next_index >= tokens.length() {
        return Some((first_alignment, first_alignment))
      }
      match parse_alignment_segment(tokens, next_index) {
        Some((second, _)) =>
          Some((first_alignment, parse_alignment_with_overflow(second)))
        None => Some((first_alignment, first_alignment))
      }
    }
    None => None
  }
}

///|
/// Parse place-content shorthand.
/// Returns (align_content, justify_content).
pub fn parse_place_content(
  value : String,
) -> (@types.Alignment, @types.Alignment)? {
  match parse_place_content_with_overflow(value) {
    Some(((align_content, _), (justify_content, _))) =>
      Some((align_content, justify_content))
    None => None
  }
}

///|
/// Parse place-items shorthand: `<'align-items'> <'justify-items'>?`.
/// A single component is duplicated to both axes. Returns
/// (align_items, justify_items).
pub fn parse_place_items(
  value : String,
) -> (@types.Alignment, @types.Alignment)? {
  let tokens = tokenize_alignment_value(value)
  if tokens.is_empty() {
    return None
  }
  match parse_alignment_segment(tokens, 0) {
    Some((first, next_index)) => {
      let align_items = parse_alignment_with_overflow(first).0
      if next_index >= tokens.length() {
        return Some((align_items, align_items))
      }
      match parse_alignment_segment(tokens, next_index) {
        Some((second, _)) =>
          Some((align_items, parse_alignment_with_overflow(second).0))
        None => Some((align_items, align_items))
      }
    }
    None => None
  }
}

///|
/// Parse place-self shorthand: `<'align-self'> <'justify-self'>?`.
/// A single component is duplicated to both axes. Returns
/// (align_self, justify_self).
pub fn parse_place_self(
  value : String,
) -> (@types.AlignSelf, @types.AlignSelf)? {
  let tokens = tokenize_alignment_value(value)
  if tokens.is_empty() {
    return None
  }
  match parse_alignment_segment(tokens, 0) {
    Some((first, next_index)) => {
      let align_self = parse_align_self(first)
      if next_index >= tokens.length() {
        return Some((align_self, align_self))
      }
      match parse_alignment_segment(tokens, next_index) {
        Some((second, _)) => Some((align_self, parse_align_self(second)))
        None => Some((align_self, align_self))
      }
    }
    None => None
  }
}

///|
/// Parse align-self value
pub fn parse_align_self(value : String) -> @types.AlignSelf {
  match value.trim().to_lower() {
    "auto" => Auto
    "flex-start" | "start" => Start
    "flex-end" | "end" => End
    "center" => Center
    "stretch" => Stretch
    "baseline" | "first baseline" => Baseline
    // Fallback alignment for last-baseline in self alignment.
    "last baseline" => End
    _ => Auto
  }
}

///|
/// Parse overflow value
pub fn parse_overflow(value : String) -> @types.Overflow {
  match value.trim().to_lower() {
    "visible" => Visible
    "hidden" => Hidden
    "clip" => Clip
    "scroll" => Scroll
    "auto" => Auto
    _ => Visible
  }
}

///|
/// Parse scroll-snap-type value
/// Supports a minimal subset: none | x mandatory | y mandatory | both mandatory.
pub fn parse_scroll_snap_type(value : String) -> @style.ScrollSnapType {
  let tokens = value
    .trim()
    .to_lower()
    .split(" ")
    .map(fn(part) { part.to_owned().trim().to_owned() })
    .filter(fn(part) { !part.is_empty() })
    .collect()
  if tokens.is_empty() {
    return @style.ScrollSnapType::none()
  }

  let mut axis = @style.ScrollSnapAxis::None
  let mut strictness = @style.ScrollSnapStrictness::None
  for token in tokens {
    match token {
      "none" => return @style.ScrollSnapType::none()
      "x" | "inline" => axis = X
      "y" | "block" => axis = Y
      "both" => axis = Both
      "mandatory" => strictness = Mandatory
      "proximity" => strictness = Proximity
      _ => ()
    }
  }
  if axis == None || strictness == None {
    @style.ScrollSnapType::none()
  } else {
    { axis, strictness }
  }
}

///|
/// Parse scroll-snap-align value as (inline, block).
/// Supports keywords: none | start | end | center.
pub fn parse_scroll_snap_align(
  value : String,
) -> (@style.ScrollSnapAlign, @style.ScrollSnapAlign) {
  let tokens = value
    .trim()
    .to_lower()
    .split(" ")
    .map(fn(part) { part.to_owned().trim().to_owned() })
    .filter(fn(part) { !part.is_empty() })
    .collect()
  if tokens.is_empty() {
    return (None, None)
  }
  if tokens.length() == 1 {
    let keyword = parse_scroll_snap_align_keyword(tokens[0])
    return (keyword, keyword)
  }
  let block = parse_scroll_snap_align_keyword(tokens[0])
  let inline = parse_scroll_snap_align_keyword(tokens[1])
  (inline, block)
}

///|
fn parse_scroll_snap_align_keyword(value : String) -> @style.ScrollSnapAlign {
  match value.trim().to_lower() {
    "none" => None
    "start" => Start
    "end" => End
    "center" => Center
    _ => None
  }
}

///|
/// Parse white-space value
pub fn parse_white_space(value : String) -> @style.WhiteSpace {
  match value.trim().to_lower() {
    "normal" => Normal
    "nowrap" => Nowrap
    "pre" => Pre
    "pre-wrap" => PreWrap
    "pre-line" => PreLine
    _ => Normal
  }
}

///|
/// Parse writing-mode value
pub fn parse_writing_mode(value : String) -> @style.WritingMode {
  match value.trim().to_lower() {
    "horizontal-tb" => HorizontalTb
    "vertical-rl" => VerticalRl
    "vertical-lr" => VerticalLr
    // Legacy values
    "lr" | "lr-tb" | "rl" | "rl-tb" => HorizontalTb
    "tb" | "tb-rl" => VerticalRl
    "tb-lr" => VerticalLr
    _ => HorizontalTb
  }
}

///|
/// Parse direction value (ltr/rtl)
pub fn parse_direction(value : String) -> @style.Direction {
  match value.trim().to_lower() {
    "rtl" => Rtl
    "ltr" | _ => Ltr
  }
}

///|
/// Parse text-align value
pub fn parse_text_align(value : String) -> @style.TextAlign {
  match value.trim().to_lower() {
    "center" => Center
    "justify" => Justify
    "left" => Left
    "right" => Right
    "end" => End
    "start" | _ => Start
  }
}

///|