///|
/// CSS Stylesheet Parser
/// Parses full stylesheets with selectors and declaration blocks

///|
/// Maximum allowed CSS input size (10 MB)
let css_max_input_size : Int = 10 * 1024 * 1024

///|
/// Parse a full stylesheet from CSS text
pub fn parse_stylesheet(css : String) -> @cascade.Stylesheet {
  // Security: reject excessively large CSS inputs
  if css.length() > css_max_input_size {
    return @cascade.Stylesheet::new(@cascade.Author)
  }
  let stylesheet = @cascade.Stylesheet::new(@cascade.Author)
  let tokens = @token.tokenize(css)
  let mut pos = 0
  while pos < tokens.length() {
    // Skip whitespace
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::EOF => break
      @token.Token::AtKeyword(name) =>
        if name.to_lower() == "media" {
          // Parse @media rule
          pos = parse_media_rule(stylesheet, tokens, pos)
        } else if name.to_lower() == "supports" {
          // Parse @supports rule (treat condition as always true)
          pos = parse_supports_rule(stylesheet, tokens, pos)
        } else {
          // Skip other @-rules (e.g., @import, @keyframes)
          pos = skip_at_rule(tokens, pos)
        }
      _ =>
        // Try to parse a style rule
        match parse_style_rule(tokens, pos) {
          Some((selectors, declarations, new_pos)) => {
            // Add rules for each selector
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule(selector_text, selector, declarations.copy())
            }
            let nested_rules = extract_ampersand_nested_rules(
              tokens, pos, new_pos, selectors,
            )
            for nested_rule in nested_rules {
              stylesheet.add_rule(
                nested_rule.0,
                nested_rule.1,
                nested_rule.2.copy(),
              )
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  stylesheet
}

///|
/// Parse an @media rule and add its rules to the stylesheet
fn parse_media_rule(
  stylesheet : @cascade.Stylesheet,
  tokens : Array[@token.Token],
  start : Int,
) -> Int {
  let mut pos = start + 1 // Skip @media token

  // Collect media query tokens until '{'
  let query_start = pos
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftBrace => break
      @token.Token::EOF => return pos
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return pos
  }

  // Extract and parse media query
  let query_text = tokens_to_string(tokens, query_start, pos).trim().to_string()
  let media_query = @media.parse_media_query_list(query_text)

  // Skip '{'
  pos = pos + 1

  // Parse rules inside @media block until '}'
  let mut brace_depth = 1
  while pos < tokens.length() && brace_depth > 0 {
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::RightBrace => {
        brace_depth = brace_depth - 1
        pos = pos + 1
        if brace_depth <= 0 {
          break
        }
      }
      @token.Token::LeftBrace => {
        // This shouldn't happen at top level, skip past it
        brace_depth = brace_depth + 1
        pos = pos + 1
      }
      @token.Token::EOF => break
      @token.Token::AtKeyword(at_name) =>
        if at_name.to_lower() == "supports" {
          // Parse @supports inside @media — rules get the media query
          pos = parse_supports_rule_with_media(
            stylesheet, tokens, pos, media_query,
          )
        } else {
          // Skip other nested @-rules inside @media
          pos = skip_at_rule(tokens, pos)
        }
      _ =>
        // Try to parse a style rule
        match parse_style_rule(tokens, pos) {
          Some((selectors, declarations, new_pos)) => {
            // Add rules with media query for each selector
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule_with_media(
                selector_text,
                selector,
                declarations.copy(),
                media_query,
              )
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  pos
}

///|
/// Parse an @supports rule inside @media, attaching the media query to rules.
fn parse_supports_rule_with_media(
  stylesheet : @cascade.Stylesheet,
  tokens : Array[@token.Token],
  start : Int,
  media_query : @media.MediaQueryList,
) -> Int {
  let mut pos = start + 1 // Skip @supports token

  // Skip condition tokens until '{' (handling nested parens)
  let mut paren_depth = 0
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftParen => {
        paren_depth += 1
        pos = pos + 1
      }
      @token.Token::RightParen => {
        paren_depth -= 1
        pos = pos + 1
      }
      @token.Token::LeftBrace if paren_depth == 0 => break
      @token.Token::EOF => return pos
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return pos
  }

  // Skip '{'
  pos = pos + 1

  // Parse rules inside @supports block until '}'
  let mut brace_depth = 1
  while pos < tokens.length() && brace_depth > 0 {
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::RightBrace => {
        brace_depth -= 1
        pos = pos + 1
        if brace_depth <= 0 {
          break
        }
      }
      @token.Token::EOF => break
      @token.Token::AtKeyword(_) => pos = skip_at_rule(tokens, pos)
      _ =>
        match parse_style_rule(tokens, pos) {
          Some((selectors, declarations, new_pos)) => {
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule_with_media(
                selector_text,
                selector,
                declarations.copy(),
                media_query,
              )
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  pos
}

///|
/// Parse an @supports rule and add its rules to the stylesheet.
/// The condition is treated as always true (minimal implementation).
fn parse_supports_rule(
  stylesheet : @cascade.Stylesheet,
  tokens : Array[@token.Token],
  start : Int,
) -> Int {
  let mut pos = start + 1 // Skip @supports token

  // Skip condition tokens until '{' (handling nested parens)
  let mut paren_depth = 0
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftParen => {
        paren_depth += 1
        pos = pos + 1
      }
      @token.Token::RightParen => {
        paren_depth -= 1
        pos = pos + 1
      }
      @token.Token::LeftBrace if paren_depth == 0 => break
      @token.Token::EOF => return pos
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return pos
  }

  // Skip '{'
  pos = pos + 1

  // Parse rules inside @supports block until '}'
  let mut brace_depth = 1
  while pos < tokens.length() && brace_depth > 0 {
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::RightBrace => {
        brace_depth -= 1
        pos = pos + 1
        if brace_depth <= 0 {
          break
        }
      }
      @token.Token::EOF => break
      @token.Token::AtKeyword(name) =>
        if name.to_lower() == "media" {
          pos = parse_media_rule(stylesheet, tokens, pos)
        } else if name.to_lower() == "supports" {
          pos = parse_supports_rule(stylesheet, tokens, pos)
        } else {
          pos = skip_at_rule(tokens, pos)
        }
      _ =>
        match parse_style_rule(tokens, pos) {
          Some((selectors, declarations, new_pos)) => {
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule(selector_text, selector, declarations.copy())
            }
            let nested_rules = extract_ampersand_nested_rules(
              tokens, pos, new_pos, selectors,
            )
            for nested_rule in nested_rules {
              stylesheet.add_rule(
                nested_rule.0,
                nested_rule.1,
                nested_rule.2.copy(),
              )
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  pos
}

///|
/// Skip whitespace tokens
fn skip_ws(tokens : Array[@token.Token], pos : Int) -> Int {
  let mut p = pos
  while p < tokens.length() {
    match tokens[p] {
      @token.Token::Whitespace => p = p + 1
      _ => break
    }
  }
  p
}

///|
/// Skip an @-rule (until semicolon or matching braces)
fn skip_at_rule(tokens : Array[@token.Token], start : Int) -> Int {
  let mut pos = start + 1
  let mut brace_depth = 0
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftBrace => {
        brace_depth = brace_depth + 1
        pos = pos + 1
      }
      @token.Token::RightBrace => {
        brace_depth = brace_depth - 1
        pos = pos + 1
        if brace_depth <= 0 {
          break
        }
      }
      @token.Token::Semicolon => {
        if brace_depth == 0 {
          pos = pos + 1
          break
        }
        pos = pos + 1
      }
      @token.Token::EOF => break
      _ => pos = pos + 1
    }
  }
  pos
}

///|
/// Parse a style rule: selector { declarations }
fn parse_style_rule(
  tokens : Array[@token.Token],
  start : Int,
) -> (
  Array[(String, @selector.ComplexSelector)],
  Array[@cascade.Declaration],
  Int,
)? {
  let mut pos = start

  // Collect selector tokens until '{'
  let selector_start = pos
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftBrace => break
      @token.Token::EOF => return None
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return None
  }

  // Extract selector text
  let selector_text = tokens_to_string(tokens, selector_start, pos)

  // Parse selectors (comma-separated)
  let selectors = parse_selector_list_text(selector_text)
  if selectors.is_empty() {
    return None
  }

  // Skip '{'
  pos = pos + 1

  // Parse declarations until '}'
  let declarations : Array[@cascade.Declaration] = []
  let mut decl_order = 0
  while pos < tokens.length() {
    pos = skip_ws(tokens, pos)
    match tokens[pos] {
      @token.Token::RightBrace => {
        pos = pos + 1
        break
      }
      @token.Token::EOF => break
      @token.Token::Delim('&') =>
        match skip_nested_rule_block(tokens, pos) {
          Some(next_pos) => pos = next_pos
          None => pos = pos + 1
        }
      _ =>
        // Parse declaration
        match parse_rule_declaration(tokens, pos) {
          Some((property, value, importance, new_pos)) => {
            let decl : @cascade.Declaration = {
              property,
              value: @cascade.Value(value),
              origin: @cascade.Author,
              importance,
              specificity: { a: 0, b: 0, c: 0 }, // Will be set during cascade
              source_order: decl_order,
            }
            declarations.push(decl)
            decl_order = decl_order + 1
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  Some((selectors, declarations, pos))
}

///|
/// Parse a declaration within a rule block
fn parse_rule_declaration(
  tokens : Array[@token.Token],
  start : Int,
) -> (String, String, @cascade.Importance, Int)? {
  let mut pos = skip_ws(tokens, start)

  // Get property name
  let property = match tokens[pos] {
    @token.Token::Ident(name) => name
    _ => return None
  }
  pos = pos + 1
  pos = skip_ws(tokens, pos)

  // Expect colon
  match tokens[pos] {
    @token.Token::Colon => pos = pos + 1
    _ => return None
  }
  pos = skip_ws(tokens, pos)

  // Collect value tokens until ';' or '}'
  let value_start = pos
  let mut has_important = false
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::Semicolon | @token.Token::RightBrace => break
      @token.Token::EOF => break
      @token.Token::Delim('!') => {
        // Check for !important
        let next_pos = skip_ws(tokens, pos + 1)
        match tokens[next_pos] {
          @token.Token::Ident(s) if s.to_lower() == "important" => {
            has_important = true
            pos = next_pos + 1
            continue
          }
          _ => pos = pos + 1
        }
      }
      _ => pos = pos + 1
    }
  }

  // Extract value (excluding !important)
  let value_end = if has_important {
    // Find where !important starts
    let mut end = value_start
    while end < pos {
      match tokens[end] {
        @token.Token::Delim('!') => break
        _ => end = end + 1
      }
    }
    end
  } else {
    pos
  }
  let value = tokens_to_string(tokens, value_start, value_end)
    .trim()
    .to_string()

  // Skip semicolon if present
  if pos < tokens.length() {
    match tokens[pos] {
      @token.Token::Semicolon => pos = pos + 1
      _ => ()
    }
  }
  let importance = if has_important {
    @cascade.Important
  } else {
    @cascade.Normal
  }
  Some((property, value, importance, pos))
}

///|
/// Find matching right brace index for a left brace token.
fn find_matching_right_brace(
  tokens : Array[@token.Token],
  left_brace_pos : Int,
  limit : Int,
) -> Int? {
  if left_brace_pos < 0 || left_brace_pos >= tokens.length() {
    return None
  }
  match tokens[left_brace_pos] {
    @token.Token::LeftBrace => ()
    _ => return None
  }
  let mut depth = 1
  let mut pos = left_brace_pos + 1
  while pos < tokens.length() && pos < limit {
    match tokens[pos] {
      @token.Token::LeftBrace => depth = depth + 1
      @token.Token::RightBrace => {
        depth = depth - 1
        if depth == 0 {
          return Some(pos)
        }
      }
      @token.Token::EOF => return None
      _ => ()
    }
    pos = pos + 1
  }
  None
}

///|
/// Skip a nested ampersand rule block like `&::foo { ... }`.
fn skip_nested_rule_block(tokens : Array[@token.Token], start : Int) -> Int? {
  let mut pos = start
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftBrace =>
        match find_matching_right_brace(tokens, pos, tokens.length()) {
          Some(close) => return Some(close + 1)
          None => return None
        }
      @token.Token::RightBrace | @token.Token::EOF => return None
      _ => pos = pos + 1
    }
  }
  None
}

///|
fn replace_ampersand(
  nested_selector : String,
  parent_selector : String,
) -> String {
  let out = StringBuilder::new()
  for i = 0; i < nested_selector.length(); i = i + 1 {
    let c = nested_selector[i].to_int().unsafe_to_char()
    if c == '&' {
      out.write_string(parent_selector)
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
/// Extract one-level nested `&... { ... }` rules as parsed selector/declaration entries.
fn extract_ampersand_nested_rules(
  tokens : Array[@token.Token],
  rule_start : Int,
  rule_end : Int,
  selectors : Array[(String, @selector.ComplexSelector)],
) -> Array[(String, @selector.ComplexSelector, Array[@cascade.Declaration])] {
  let rules : Array[
    (String, @selector.ComplexSelector, Array[@cascade.Declaration]),
  ] = []
  let mut body_start = -1
  let mut i = rule_start
  while i < rule_end {
    match tokens[i] {
      @token.Token::LeftBrace => {
        body_start = i
        break
      }
      _ => i = i + 1
    }
  }
  if body_start < 0 {
    return rules
  }

  let mut pos = body_start + 1
  while pos < rule_end - 1 {
    pos = skip_ws(tokens, pos)
    if pos >= rule_end - 1 {
      break
    }
    match tokens[pos] {
      @token.Token::Delim('&') => {
        let nested_selector_start = pos
        while pos < rule_end - 1 {
          match tokens[pos] {
            @token.Token::LeftBrace
            | @token.Token::RightBrace
            | @token.Token::EOF => break
            _ => pos = pos + 1
          }
        }
        if pos >= rule_end - 1 {
          break
        }
        match tokens[pos] {
          @token.Token::LeftBrace => {
            let nested_selector_text = tokens_to_string(
                tokens, nested_selector_start, pos,
              )
              .trim()
              .to_string()
            match find_matching_right_brace(tokens, pos, rule_end) {
              Some(nested_close) => {
                let nested_decls : Array[@cascade.Declaration] = []
                let mut decl_order = 0
                let mut decl_pos = pos + 1
                while decl_pos < nested_close {
                  decl_pos = skip_ws(tokens, decl_pos)
                  if decl_pos >= nested_close {
                    break
                  }
                  match parse_rule_declaration(tokens, decl_pos) {
                    Some((property, value, importance, new_decl_pos)) => {
                      let decl : @cascade.Declaration = {
                        property,
                        value: @cascade.Value(value),
                        origin: @cascade.Author,
                        importance,
                        specificity: { a: 0, b: 0, c: 0 },
                        source_order: decl_order,
                      }
                      nested_decls.push(decl)
                      decl_order = decl_order + 1
                      if new_decl_pos > decl_pos {
                        decl_pos = new_decl_pos
                      } else {
                        decl_pos = decl_pos + 1
                      }
                    }
                    None => decl_pos = decl_pos + 1
                  }
                }
                if !nested_selector_text.is_empty() && !nested_decls.is_empty() {
                  let nested_parts = split_by_comma(nested_selector_text)
                  for parent_item in selectors {
                    let parent_selector = parent_item.0.trim().to_string()
                    if parent_selector.is_empty() {
                      continue
                    }
                    for nested_part in nested_parts {
                      let nested_trim = nested_part.trim().to_string()
                      if nested_trim.is_empty() {
                        continue
                      }
                      let expanded = replace_ampersand(
                        nested_trim, parent_selector,
                      )
                      match @selector.parse_selector_text(expanded) {
                        Some(sel) =>
                          rules.push((expanded, sel, nested_decls.copy()))
                        None => ()
                      }
                    }
                  }
                }
                pos = nested_close + 1
                continue
              }
              None => break
            }
          }
          _ => pos = pos + 1
        }
      }
      @token.Token::LeftBrace =>
        match find_matching_right_brace(tokens, pos, rule_end) {
          Some(close) => pos = close + 1
          None => break
        }
      @token.Token::RightBrace | @token.Token::EOF => break
      _ => pos = pos + 1
    }
  }
  rules
}

///|
/// Convert token range to string
fn tokens_to_string(
  tokens : Array[@token.Token],
  start : Int,
  end : Int,
) -> String {
  let buf = StringBuilder::new()
  for i = start; i < end; i = i + 1 {
    match tokens[i] {
      @token.Token::Ident(s) => buf.write_string(s)
      @token.Token::Hash(s, _) => {
        buf.write_char('#')
        buf.write_string(s)
      }
      @token.Token::Delim(c) => buf.write_char(c)
      @token.Token::Colon => buf.write_char(':')
      @token.Token::Comma => buf.write_char(',')
      @token.Token::Whitespace => buf.write_char(' ')
      @token.Token::Dimension(n, u) => {
        buf.write_string(n.to_string())
        buf.write_string(u)
      }
      @token.Token::Number(n, _) => buf.write_string(n.to_string())
      @token.Token::Percentage(n) => {
        buf.write_string(n.to_string())
        buf.write_char('%')
      }
      @token.Token::String(s) => {
        buf.write_char('"')
        buf.write_string(s)
        buf.write_char('"')
      }
      @token.Token::LeftParen => buf.write_char('(')
      @token.Token::RightParen => buf.write_char(')')
      @token.Token::LeftBracket => buf.write_char('[')
      @token.Token::RightBracket => buf.write_char(']')
      @token.Token::Function(name) => {
        buf.write_string(name)
        buf.write_char('(')
      }
      _ => ()
    }
  }
  buf.to_string()
}

///|
/// Parse comma-separated selector list
fn parse_selector_list_text(
  css : String,
) -> Array[(String, @selector.ComplexSelector)] {
  let result : Array[(String, @selector.ComplexSelector)] = []
  let parts = split_by_comma(css)
  for part in parts {
    let trimmed = part.trim().to_string()
    if !trimmed.is_empty() {
      match @selector.parse_selector_text(trimmed) {
        Some(sel) => result.push((trimmed, sel))
        None => ()
      }
    }
  }
  result
}

///|
/// Split string by comma
fn split_by_comma(s : String) -> Array[String] {
  let result : Array[String] = []
  let current = StringBuilder::new()
  let mut paren_depth = 0
  let mut bracket_depth = 0
  let mut in_single_quote = false
  let mut in_double_quote = false
  let mut escaped = false
  for i = 0; i < s.length(); i = i + 1 {
    let c = s[i].to_int().unsafe_to_char()
    if escaped {
      current.write_char(c)
      escaped = false
      continue
    }
    if c == '\\' && (in_single_quote || in_double_quote) {
      current.write_char(c)
      escaped = true
      continue
    }
    if in_single_quote {
      current.write_char(c)
      if c == '\'' {
        in_single_quote = false
      }
      continue
    }
    if in_double_quote {
      current.write_char(c)
      if c == '"' {
        in_double_quote = false
      }
      continue
    }

    if c == '\'' {
      in_single_quote = true
      current.write_char(c)
      continue
    }
    if c == '"' {
      in_double_quote = true
      current.write_char(c)
      continue
    }
    if c == '(' {
      paren_depth += 1
      current.write_char(c)
      continue
    }
    if c == ')' {
      if paren_depth > 0 {
        paren_depth -= 1
      }
      current.write_char(c)
      continue
    }
    if c == '[' {
      bracket_depth += 1
      current.write_char(c)
      continue
    }
    if c == ']' {
      if bracket_depth > 0 {
        bracket_depth -= 1
      }
      current.write_char(c)
      continue
    }
    if c == ',' && paren_depth == 0 && bracket_depth == 0 {
      result.push(current.to_string())
      current.reset()
    } else {
      current.write_char(c)
    }
  }
  if current.to_string().length() > 0 {
    result.push(current.to_string())
  }
  result
}

///|
/// Parse result with diagnostics
pub(all) struct ParseResult {
  stylesheet : @cascade.Stylesheet
  diagnostics : @diagnostics.DiagnosticsCollector
}

///|
/// Parse a stylesheet and collect diagnostics
pub fn parse_stylesheet_with_diagnostics(css : String) -> ParseResult {
  let stylesheet = @cascade.Stylesheet::new(@cascade.Author)
  let diagnostics = @diagnostics.DiagnosticsCollector::new()
  let tokens = @token.tokenize(css)
  let mut pos = 0
  while pos < tokens.length() {
    // Skip whitespace
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::EOF => break
      @token.Token::AtKeyword(name) =>
        if name.to_lower() == "media" {
          // Parse @media rule
          pos = parse_media_rule_with_diagnostics(
            stylesheet, diagnostics, tokens, pos,
          )
        } else if name.to_lower() == "supports" {
          // Parse @supports rule (treat condition as always true)
          pos = parse_supports_rule_with_diagnostics(
            stylesheet, diagnostics, tokens, pos,
          )
        } else {
          // Skip other @-rules (e.g., @import, @keyframes)
          pos = skip_at_rule(tokens, pos)
        }
      _ =>
        // Try to parse a style rule
        match parse_style_rule_with_diagnostics(tokens, pos, diagnostics) {
          Some((selectors, declarations, new_pos)) => {
            // Add rules for each selector
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule(selector_text, selector, declarations.copy())
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  { stylesheet, diagnostics }
}

///|
/// Parse an @media rule with diagnostics
fn parse_media_rule_with_diagnostics(
  stylesheet : @cascade.Stylesheet,
  diagnostics : @diagnostics.DiagnosticsCollector,
  tokens : Array[@token.Token],
  start : Int,
) -> Int {
  let mut pos = start + 1 // Skip @media token

  // Collect media query tokens until '{'
  let query_start = pos
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftBrace => break
      @token.Token::EOF => return pos
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return pos
  }

  // Extract and parse media query
  let query_text = tokens_to_string(tokens, query_start, pos).trim().to_string()
  let media_query = @media.parse_media_query_list(query_text)

  // Skip '{'
  pos = pos + 1

  // Parse rules inside @media block until '}'
  let mut brace_depth = 1
  while pos < tokens.length() && brace_depth > 0 {
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::RightBrace => {
        brace_depth = brace_depth - 1
        pos = pos + 1
        if brace_depth <= 0 {
          break
        }
      }
      @token.Token::LeftBrace => {
        brace_depth = brace_depth + 1
        pos = pos + 1
      }
      @token.Token::EOF => break
      @token.Token::AtKeyword(at_name) =>
        if at_name.to_lower() == "supports" {
          pos = parse_supports_rule_with_media(
            stylesheet, tokens, pos, media_query,
          )
        } else {
          // Skip other nested @-rules inside @media
          pos = skip_at_rule(tokens, pos)
        }
      _ =>
        // Try to parse a style rule
        match parse_style_rule_with_diagnostics(tokens, pos, diagnostics) {
          Some((selectors, declarations, new_pos)) => {
            // Add rules with media query for each selector
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule_with_media(
                selector_text,
                selector,
                declarations.copy(),
                media_query,
              )
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  pos
}

///|
/// Parse an @supports rule with diagnostics.
/// The condition is treated as always true (minimal implementation).
fn parse_supports_rule_with_diagnostics(
  stylesheet : @cascade.Stylesheet,
  diagnostics : @diagnostics.DiagnosticsCollector,
  tokens : Array[@token.Token],
  start : Int,
) -> Int {
  let mut pos = start + 1 // Skip @supports token

  // Skip condition tokens until '{' (handling nested parens)
  let mut paren_depth = 0
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftParen => {
        paren_depth += 1
        pos = pos + 1
      }
      @token.Token::RightParen => {
        paren_depth -= 1
        pos = pos + 1
      }
      @token.Token::LeftBrace if paren_depth == 0 => break
      @token.Token::EOF => return pos
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return pos
  }

  // Skip '{'
  pos = pos + 1

  // Parse rules inside @supports block until '}'
  let mut brace_depth = 1
  while pos < tokens.length() && brace_depth > 0 {
    pos = skip_ws(tokens, pos)
    if pos >= tokens.length() {
      break
    }
    match tokens[pos] {
      @token.Token::RightBrace => {
        brace_depth -= 1
        pos = pos + 1
        if brace_depth <= 0 {
          break
        }
      }
      @token.Token::EOF => break
      @token.Token::AtKeyword(name) =>
        if name.to_lower() == "media" {
          pos = parse_media_rule_with_diagnostics(
            stylesheet, diagnostics, tokens, pos,
          )
        } else if name.to_lower() == "supports" {
          pos = parse_supports_rule_with_diagnostics(
            stylesheet, diagnostics, tokens, pos,
          )
        } else {
          pos = skip_at_rule(tokens, pos)
        }
      _ =>
        match parse_style_rule_with_diagnostics(tokens, pos, diagnostics) {
          Some((selectors, declarations, new_pos)) => {
            for item in selectors {
              let selector_text = item.0
              let selector = item.1
              stylesheet.add_rule(selector_text, selector, declarations.copy())
            }
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  pos
}

///|
/// Parse a style rule with diagnostics
fn parse_style_rule_with_diagnostics(
  tokens : Array[@token.Token],
  start : Int,
  diagnostics : @diagnostics.DiagnosticsCollector,
) -> (
  Array[(String, @selector.ComplexSelector)],
  Array[@cascade.Declaration],
  Int,
)? {
  let mut pos = start

  // Collect selector tokens until '{'
  let selector_start = pos
  while pos < tokens.length() {
    match tokens[pos] {
      @token.Token::LeftBrace => break
      @token.Token::EOF => return None
      _ => pos = pos + 1
    }
  }
  if pos >= tokens.length() {
    return None
  }

  // Extract selector text
  let selector_text = tokens_to_string(tokens, selector_start, pos)

  // Parse selectors (comma-separated)
  let selectors = parse_selector_list_text(selector_text)
  if selectors.is_empty() {
    return None
  }

  // Get first selector for diagnostic context
  let selector_context = if selectors.length() > 0 {
    selectors[0].0
  } else {
    ""
  }

  // Skip '{'
  pos = pos + 1

  // Parse declarations until '}'
  let declarations : Array[@cascade.Declaration] = []
  let mut decl_order = 0
  while pos < tokens.length() {
    pos = skip_ws(tokens, pos)
    match tokens[pos] {
      @token.Token::RightBrace => {
        pos = pos + 1
        break
      }
      @token.Token::EOF => break
      _ =>
        // Parse declaration
        match parse_rule_declaration(tokens, pos) {
          Some((property, value, importance, new_pos)) => {
            // Add diagnostic
            diagnostics.add_property(property, value, selector_context)
            let decl : @cascade.Declaration = {
              property,
              value: @cascade.Value(value),
              origin: @cascade.Author,
              importance,
              specificity: { a: 0, b: 0, c: 0 },
              source_order: decl_order,
            }
            declarations.push(decl)
            decl_order = decl_order + 1
            pos = new_pos
          }
          None => pos = pos + 1
        }
    }
  }
  Some((selectors, declarations, pos))
}