///|
/// CSS Selector Matcher
/// Determines if an element matches a given selector

///|
/// Check if an element matches a simple selector
fn matches_simple(element : Element, selector : SimpleSelector) -> Bool {
  match selector {
    Type(name) => element.tag_name == name
    Universal => true
    Id(id) =>
      match element.id {
        Some(elem_id) => elem_id == id
        None => false
      }
    Class(cls) => element.has_class(cls)
    Attribute(attr_sel) => matches_attribute(element, attr_sel)
    PseudoClass(pc) => matches_pseudo_class(element, pc)
    PseudoElement(_) =>
      // Pseudo-elements (::before, ::after) create virtual elements that don't exist in the DOM
      // Selectors with pseudo-elements should NOT match the actual element
      // because we don't support creating pseudo-element content
      false
  }
}

///|
/// Check if an element matches an attribute selector
fn matches_attribute(element : Element, selector : AttributeSelector) -> Bool {
  let attr_value = element.get_attribute(selector.name)
  match selector.match_type {
    Exists =>
      match attr_value {
        Some(_) => true
        None => false
      }
    Exact(value) =>
      match attr_value {
        Some(v) =>
          if selector.case_insensitive {
            v.to_lower() == value.to_lower()
          } else {
            v == value
          }
        None => false
      }
    Includes(value) =>
      match attr_value {
        Some(v) => {
          let parts = split_whitespace(v)
          for part in parts {
            let matches = if selector.case_insensitive {
              part.to_lower() == value.to_lower()
            } else {
              part == value
            }
            if matches {
              return true
            }
          }
          false
        }
        None => false
      }
    DashMatch(value) =>
      match attr_value {
        Some(v) => {
          let (check_val, check_value) = if selector.case_insensitive {
            (v.to_lower(), value.to_lower())
          } else {
            (v, value)
          }
          check_val == check_value || check_val.has_prefix(check_value + "-")
        }
        None => false
      }
    Prefix(value) =>
      match attr_value {
        Some(v) => {
          let (check_val, check_value) = if selector.case_insensitive {
            (v.to_lower(), value.to_lower())
          } else {
            (v, value)
          }
          check_val.has_prefix(check_value)
        }
        None => false
      }
    Suffix(value) =>
      match attr_value {
        Some(v) => {
          let (check_val, check_value) = if selector.case_insensitive {
            (v.to_lower(), value.to_lower())
          } else {
            (v, value)
          }
          check_val.has_suffix(check_value)
        }
        None => false
      }
    Substring(value) =>
      match attr_value {
        Some(v) => {
          let (check_val, check_value) = if selector.case_insensitive {
            (v.to_lower(), value.to_lower())
          } else {
            (v, value)
          }
          check_val.contains(check_value)
        }
        None => false
      }
  }
}

///|
/// Split string by whitespace
fn split_whitespace(s : String) -> Array[String] {
  let result : Array[String] = []
  let current = StringBuilder::new()
  for i = 0; i < s.length(); i = i + 1 {
    let c = s[i].to_int().unsafe_to_char()
    if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
      if current.to_string().length() > 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
}

///|
/// Check if an element matches a pseudo-class
fn matches_pseudo_class(element : Element, pc : PseudoClass) -> Bool {
  match pc {
    FirstChild => element.is_first_child()
    LastChild => element.is_last_child()
    OnlyChild => element.sibling_count == 1
    NthChild(expr) => matches_nth(element.sibling_index, expr)
    NthLastChild(expr) => {
      let from_end = element.sibling_count - element.sibling_index + 1
      matches_nth(from_end, expr)
    }
    FirstOfType =>
      // Would need to check siblings of same type
      // For now, approximate with first-child
      element.is_first_child()
    LastOfType => element.is_last_child()
    OnlyOfType => element.sibling_count == 1
    NthOfType(expr) =>
      // Would need type-specific index
      matches_nth(element.sibling_index, expr)
    NthLastOfType(expr) => {
      let from_end = element.sibling_count - element.sibling_index + 1
      matches_nth(from_end, expr)
    }
    Root =>
      match element.parent {
        None => true
        Some(_) => false
      }
    Empty => element.children.is_empty()
    Not(selectors) => {
      for sel in selectors {
        if matches_compound(element, sel) {
          return false
        }
      }
      true
    }
    Is(selectors) => {
      for sel in selectors {
        if matches_complex(element, sel) {
          return true
        }
      }
      false
    }
    Where(selectors) => {
      for sel in selectors {
        if matches_complex(element, sel) {
          return true
        }
      }
      false
    }
    Has(_) =>
      // :has() requires checking descendants/siblings
      // Complex to implement, return false for now
      false
    State(state) => {
      for token in element.custom_states {
        if token == state {
          return true
        }
      }
      false
    }
    // User-action pseudo-classes - these depend on runtime state
    // For static matching, we return false (or could check element state)
    Hover | Active | Focus | FocusVisible | FocusWithin => false
    // Link pseudo-classes. Static rendering has no visited-history state, so
    // :visited must not match by default; otherwise later visited rules override
    // normal link styling.
    Link | AnyLink =>
      element.tag_name == "a" && element.get_attribute("href") is Some(_)
    Visited => false
    // Form pseudo-classes - would need form element state
    Enabled
    | Disabled
    | Checked
    | Indeterminate
    | Required
    | Optional
    | Valid
    | Invalid
    | ReadOnly
    | ReadWrite => false
  }
}

///|
/// Check if index matches An+B expression
fn matches_nth(index : Int, expr : NthExpr) -> Bool {
  if expr.a == 0 {
    // Just check if index == b
    return index == expr.b
  }

  // Check if (index - b) is divisible by a
  let diff = index - expr.b
  if expr.a > 0 {
    // Positive a: diff must be >= 0 and divisible by a
    diff >= 0 && diff % expr.a == 0
  } else {
    // Negative a: diff must be <= 0 and divisible by |a|
    diff <= 0 && diff % expr.a == 0
  }
}

///|
/// Check if an element matches a compound selector
fn matches_compound(element : Element, selector : CompoundSelector) -> Bool {
  // Check type selector
  match selector.type_selector {
    Some(type_sel) => if !matches_simple(element, type_sel) { return false }
    None => ()
  }

  // Check all subclass selectors
  for sub in selector.subclasses {
    if !matches_simple(element, sub) {
      return false
    }
  }
  true
}

///|
/// Check if an element matches a complex selector
pub fn matches_complex(element : Element, selector : ComplexSelector) -> Bool {
  // First, check if the element matches the head (rightmost/subject)
  if !matches_compound(element, selector.head) {
    return false
  }

  // If there's no tail, we're done
  if selector.tail.is_empty() {
    return true
  }

  // Walk up the tree following the combinators
  let mut current_element = element
  for step in selector.tail {
    match step.combinator {
      Descendant => {
        // Find any ancestor that matches
        let mut found = false
        let mut ancestor = current_element.parent
        while true {
          match ancestor {
            None => break
            Some(anc) => {
              if matches_compound(anc, step.selector) {
                current_element = anc
                found = true
                break
              }
              ancestor = anc.parent
            }
          }
        }
        if !found {
          return false
        }
      }
      Child =>
        // Parent must match
        match current_element.parent {
          None => return false
          Some(parent) => {
            if !matches_compound(parent, step.selector) {
              return false
            }
            current_element = parent
          }
        }
      NextSibling =>
        // Previous sibling must match
        match current_element.prev_sibling {
          None => return false
          Some(prev) => {
            if !matches_compound(prev, step.selector) {
              return false
            }
            current_element = prev
          }
        }
      SubsequentSibling => {
        // Any previous sibling must match
        let mut found = false
        let mut sibling = current_element.prev_sibling
        while true {
          match sibling {
            None => break
            Some(sib) => {
              if matches_compound(sib, step.selector) {
                current_element = sib
                found = true
                break
              }
              sibling = sib.prev_sibling
            }
          }
        }
        if !found {
          return false
        }
      }
    }
  }
  true
}

///|
/// Check if an element matches any selector in a list
pub fn matches_selector_list(element : Element, list : SelectorList) -> Bool {
  for sel in list.selectors {
    if matches_complex(element, sel) {
      return true
    }
  }
  false
}