use cssparser::*;
use precomputed_hash::PrecomputedHash;
use selectors::parser::{
  NonTSPseudoClass, ParseRelative, PseudoElement as PseudoElementTrait,
  SelectorImpl as SelectorImplTrait, SelectorList,
};
use std::{
  collections::HashMap,
  fmt::{self, Write},
  mem::take,
  ops::Deref,
};

use crate::{
  error::StyleSheetParseError,
  keyframes::parse_keyframe_prelude,
  style::{KeyframeRule, KeyframesRule, StyleDeclarationBlock, supports::parse_supports_condition},
};

pub use crate::style::media_query::MediaQueryList;

#[derive(Debug, Clone, PartialEq)]
pub struct PropertyRule {
  pub name: String,
  pub syntax: String,
  pub inherits: bool,
  pub initial_value: Option<String>,
  pub media_queries: Vec<MediaQueryList>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LayerName {
  Named(String),
  Anonymous,
}

type LayerPath = Vec<LayerName>;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Ident(String);

impl Deref for Ident {
  type Target = str;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl AsRef<str> for Ident {
  fn as_ref(&self) -> &str {
    &self.0
  }
}

impl PartialEq<&str> for Ident {
  fn eq(&self, other: &&str) -> bool {
    self.0 == *other
  }
}

impl PartialEq<Ident> for &str {
  fn eq(&self, other: &Ident) -> bool {
    self == &other.0
  }
}
impl From<&str> for Ident {
  fn from(s: &str) -> Self {
    Self(s.to_owned())
  }
}

impl ToCss for Ident {
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
  where
    W: Write,
  {
    serialize_identifier(&self.0, dest)
  }
}

impl PrecomputedHash for Ident {
  fn precomputed_hash(&self) -> u32 {
    let mut hash = 0x811c9dc5u32;
    for byte in self.0.as_bytes() {
      hash ^= u32::from(byte.to_ascii_lowercase());
      hash = hash.wrapping_mul(0x0100_0193);
    }
    hash
  }
}

#[derive(Debug, Clone)]
pub struct SelectorImpl;

// Parsed but never matched, so rules with `:hover`, `::before`, etc. survive
// alongside their sibling selectors instead of getting dropped wholesale.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PseudoClass(Ident);

impl ToCss for PseudoClass {
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
  where
    W: Write,
  {
    dest.write_char(':')?;
    self.0.to_css(dest)
  }
}

impl NonTSPseudoClass for PseudoClass {
  type Impl = SelectorImpl;
  fn is_active_or_hover(&self) -> bool {
    false
  }
  fn is_user_action_state(&self) -> bool {
    false
  }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PseudoElement {
  Before,
  After,
  Other(Ident),
}

impl PseudoElement {
  fn from_name(name: &str) -> Self {
    if name.eq_ignore_ascii_case("before") {
      Self::Before
    } else if name.eq_ignore_ascii_case("after") {
      Self::After
    } else {
      Self::Other(Ident::from(name))
    }
  }

  fn as_str(&self) -> &str {
    match self {
      Self::Before => "before",
      Self::After => "after",
      Self::Other(name) => name,
    }
  }
}

impl ToCss for PseudoElement {
  fn to_css<W>(&self, dest: &mut W) -> fmt::Result
  where
    W: Write,
  {
    dest.write_str("::")?;
    serialize_identifier(self.as_str(), dest)
  }
}

impl PseudoElementTrait for PseudoElement {
  type Impl = SelectorImpl;
}

impl SelectorImplTrait for SelectorImpl {
  type ExtraMatchingData<'a> = ();
  type AttrValue = Ident;
  type Identifier = Ident;
  type LocalName = Ident;
  type NamespaceUrl = Ident;
  type NamespacePrefix = Ident;
  type BorrowedNamespaceUrl = Ident;
  type BorrowedLocalName = Ident;
  type NonTSPseudoClass = PseudoClass;
  type PseudoElement = PseudoElement;
}

struct TakumiSelectorParser;

impl<'i> selectors::Parser<'i> for TakumiSelectorParser {
  type Impl = SelectorImpl;
  type Error = StyleSheetParseError;

  fn parse_parent_selector(&self) -> bool {
    true
  }

  fn parse_host(&self) -> bool {
    true
  }

  fn parse_is_and_where(&self) -> bool {
    true
  }

  fn parse_non_ts_pseudo_class(
    &self,
    _location: SourceLocation,
    name: CowRcStr<'i>,
  ) -> Result<<Self::Impl as SelectorImplTrait>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
    Ok(PseudoClass(Ident::from(&*name)))
  }

  fn parse_non_ts_functional_pseudo_class<'t>(
    &self,
    name: CowRcStr<'i>,
    parser: &mut Parser<'i, 't>,
    _after_part: bool,
  ) -> Result<<Self::Impl as SelectorImplTrait>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
    while parser.next_including_whitespace_and_comments().is_ok() {}
    Ok(PseudoClass(Ident::from(&*name)))
  }

  fn parse_pseudo_element(
    &self,
    _location: SourceLocation,
    name: CowRcStr<'i>,
  ) -> Result<<Self::Impl as SelectorImplTrait>::PseudoElement, ParseError<'i, Self::Error>> {
    Ok(PseudoElement::from_name(&name))
  }

  fn parse_functional_pseudo_element<'t>(
    &self,
    name: CowRcStr<'i>,
    arguments: &mut Parser<'i, 't>,
  ) -> Result<<Self::Impl as SelectorImplTrait>::PseudoElement, ParseError<'i, Self::Error>> {
    while arguments.next_including_whitespace_and_comments().is_ok() {}
    Ok(PseudoElement::Other(Ident::from(&*name)))
  }
}

#[derive(Debug, Clone, Default)]
struct StyleSheetFragment {
  rules: Vec<CssRule>,
  keyframes: Vec<KeyframesRule>,
  property_rules: Vec<PropertyRule>,
  declared_layers: Vec<LayerPath>,
}

impl StyleSheetFragment {
  fn extend(&mut self, other: Self) {
    self.rules.extend(other.rules);
    self.keyframes.extend(other.keyframes);
    self.property_rules.extend(other.property_rules);
    self.declared_layers.extend(other.declared_layers);
  }
}

#[derive(Debug)]
enum StyleRuleBodyItem {
  Declarations(Box<StyleDeclarationBlock>),
  Rules(StyleSheetFragment),
}

macro_rules! impl_parser_traits {
  ($parser:ty, $item:ty) => {
    impl<'i> QualifiedRuleParser<'i> for $parser {
      type Prelude = ();
      type QualifiedRule = $item;
      type Error = StyleSheetParseError;
    }

    impl<'i> AtRuleParser<'i> for $parser {
      type Prelude = ();
      type AtRule = $item;
      type Error = StyleSheetParseError;
    }

    impl<'i> RuleBodyItemParser<'i, $item, StyleSheetParseError> for $parser {
      fn parse_qualified(&self) -> bool {
        false
      }
      fn parse_declarations(&self) -> bool {
        true
      }
    }
  };
}

pub(crate) struct StyleDeclarationParser;

impl<'i> DeclarationParser<'i> for StyleDeclarationParser {
  type Declaration = StyleDeclarationBlock;
  type Error = StyleSheetParseError;

  fn parse_value<'t>(
    &mut self,
    name: CowRcStr<'i>,
    input: &mut Parser<'i, 't>,
    _state: &ParserState,
  ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
    let mut declarations = StyleDeclarationBlock::parse(&name, input).map_err(ParseError::into)?;
    let important = input.try_parse(parse_important).is_ok();
    if important {
      for declaration in &declarations.declarations {
        declarations.importance.insert_declaration(declaration);
      }
    }
    Ok(declarations)
  }
}

impl_parser_traits!(StyleDeclarationParser, StyleDeclarationBlock);

struct PropertyRuleDeclarationParser;

impl<'i> DeclarationParser<'i> for PropertyRuleDeclarationParser {
  type Declaration = (String, String);
  type Error = StyleSheetParseError;

  fn parse_value<'t>(
    &mut self,
    name: CowRcStr<'i>,
    input: &mut Parser<'i, 't>,
    _state: &ParserState,
  ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
    let start = input.position();
    while input.next_including_whitespace_and_comments().is_ok() {}
    Ok((name.to_string(), input.slice_from(start).trim().to_owned()))
  }
}

impl_parser_traits!(PropertyRuleDeclarationParser, (String, String));

struct NestedStyleRuleParser<'a> {
  parent_selectors: SelectorList<SelectorImpl>,
  media_queries: &'a [MediaQueryList],
  layer: Option<LayerPath>,
  lossy: bool,
}

impl<'i> DeclarationParser<'i> for NestedStyleRuleParser<'_> {
  type Declaration = StyleRuleBodyItem;
  type Error = StyleSheetParseError;

  fn parse_value<'t>(
    &mut self,
    name: CowRcStr<'i>,
    input: &mut Parser<'i, 't>,
    state: &ParserState,
  ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
    let mut parser = StyleDeclarationParser;
    parser
      .parse_value(name, input, state)
      .map(Box::new)
      .map(StyleRuleBodyItem::Declarations)
  }
}

impl<'i> QualifiedRuleParser<'i> for NestedStyleRuleParser<'_> {
  type Prelude = SelectorList<SelectorImpl>;
  type QualifiedRule = StyleRuleBodyItem;
  type Error = StyleSheetParseError;

  fn parse_prelude<'t>(
    &mut self,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
    SelectorList::parse(&TakumiSelectorParser, input, ParseRelative::ForNesting)
  }

  fn parse_block<'t>(
    &mut self,
    nested_selectors: Self::Prelude,
    _location: &ParserState,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
    let selectors = nested_selectors.replace_parent_selector(&self.parent_selectors);
    let fragment = parse_style_rule_block(
      selectors,
      self.media_queries,
      self.layer.as_ref(),
      self.lossy,
      input,
    )?;
    Ok(StyleRuleBodyItem::Rules(fragment))
  }
}

impl<'i> AtRuleParser<'i> for NestedStyleRuleParser<'_> {
  type Prelude = AtRulePrelude;
  type AtRule = StyleRuleBodyItem;
  type Error = StyleSheetParseError;

  fn parse_prelude<'t>(
    &mut self,
    name: CowRcStr<'i>,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
    parse_at_rule_prelude(name, input)
  }

  fn parse_block<'t>(
    &mut self,
    prelude: Self::Prelude,
    _location: &ParserState,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
    let fragment = parse_nested_at_rule_block(
      &self.parent_selectors,
      self.media_queries,
      self.layer.as_ref(),
      self.lossy,
      prelude,
      input,
    )?;
    Ok(StyleRuleBodyItem::Rules(fragment))
  }
}

impl<'i> RuleBodyItemParser<'i, StyleRuleBodyItem, StyleSheetParseError>
  for NestedStyleRuleParser<'_>
{
  fn parse_qualified(&self) -> bool {
    true
  }

  fn parse_declarations(&self) -> bool {
    true
  }
}

struct KeyframeDeclarationParser;

impl<'i> DeclarationParser<'i> for KeyframeDeclarationParser {
  type Declaration = StyleDeclarationBlock;
  type Error = StyleSheetParseError;

  fn parse_value<'t>(
    &mut self,
    name: CowRcStr<'i>,
    input: &mut Parser<'i, 't>,
    _state: &ParserState,
  ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
    let declarations = StyleDeclarationBlock::parse(&name, input).map_err(ParseError::into)?;
    // !important flag is ignored in keyframe declartion.
    // Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes#!important_in_a_keyframe
    input.try_parse(parse_important).ok();
    Ok(declarations)
  }
}

impl_parser_traits!(KeyframeDeclarationParser, StyleDeclarationBlock);

struct KeyframeRuleParser;

impl<'i> QualifiedRuleParser<'i> for KeyframeRuleParser {
  type Prelude = Vec<f32>;
  type QualifiedRule = KeyframeRule;
  type Error = StyleSheetParseError;

  fn parse_prelude<'t>(
    &mut self,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
    parse_keyframe_prelude(input)
  }

  fn parse_block<'t>(
    &mut self,
    offsets: Self::Prelude,
    _location: &ParserState,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
    let mut declaration_parser = KeyframeDeclarationParser;
    let mut declarations = StyleDeclarationBlock::default();
    for result in RuleBodyParser::new(input, &mut declaration_parser) {
      match result {
        Ok(block) => declarations.append(block),
        Err((error, _)) => return Err(error),
      }
    }

    Ok(KeyframeRule {
      offsets,
      declarations,
    })
  }
}

impl<'i> AtRuleParser<'i> for KeyframeRuleParser {
  type Prelude = ();
  type AtRule = KeyframeRule;
  type Error = StyleSheetParseError;
}

struct RuleParser {
  current_layer: Option<LayerPath>,
  lossy: bool,
}

#[derive(Debug, Clone)]
enum AtRulePrelude {
  Keyframes(String),
  Layer(Vec<LayerPath>),
  Media(MediaQueryList),
  Property(String),
  Supports(bool),
}

fn parse_fragment_with_mode<'i, 't>(
  input: &mut Parser<'i, 't>,
  parser: &mut RuleParser,
) -> Result<StyleSheetFragment, ParseError<'i, StyleSheetParseError>> {
  let mut fragment = StyleSheetFragment::default();
  let lossy = parser.lossy;
  for nested in StyleSheetParser::new(input, parser) {
    match nested {
      Ok(nested) => fragment.extend(nested),
      Err((error, _)) => {
        if lossy {
          continue;
        }
        return Err(error);
      }
    }
  }

  Ok(fragment)
}

#[derive(Debug, Clone)]
pub struct CssRule {
  pub selectors: SelectorList<SelectorImpl>,
  pub normal_declarations: StyleDeclarationBlock,
  pub important_declarations: StyleDeclarationBlock,
  pub media_queries: Vec<MediaQueryList>,
  pub layer: Option<LayerPath>,
  pub layer_order: Option<usize>,
}

fn parse_property_rule<'i, 't>(
  property_name: String,
  input: &mut Parser<'i, 't>,
) -> Result<PropertyRule, ParseError<'i, StyleSheetParseError>> {
  let mut parser = PropertyRuleDeclarationParser;
  let mut syntax = None;
  let mut inherits = None;
  let mut initial_value = None;
  let mut invalid_inherits = false;

  for result in RuleBodyParser::new(input, &mut parser) {
    let (name, value) = match result {
      Ok(value) => value,
      Err((error, _)) => return Err(error),
    };

    if name.eq_ignore_ascii_case("syntax") {
      syntax = Some(value);
      continue;
    }

    if name.eq_ignore_ascii_case("inherits") {
      if value.eq_ignore_ascii_case("true") {
        inherits = Some(true);
        continue;
      }

      if value.eq_ignore_ascii_case("false") {
        inherits = Some(false);
        continue;
      }

      invalid_inherits = true;
      continue;
    }

    if name.eq_ignore_ascii_case("initial-value") {
      initial_value = Some(value);
    }
  }

  if invalid_inherits {
    return Err(input.new_custom_error(StyleSheetParseError::property_inherits_must_be_boolean()));
  }

  let Some(syntax) = syntax else {
    return Err(input.new_custom_error(StyleSheetParseError::missing_property_syntax()));
  };
  let Some(inherits) = inherits else {
    return Err(input.new_custom_error(StyleSheetParseError::missing_property_inherits()));
  };

  Ok(PropertyRule {
    name: property_name,
    syntax,
    inherits,
    initial_value,
    media_queries: Vec::new(),
  })
}

fn parse_at_rule_prelude<'i, 't>(
  name: CowRcStr<'i>,
  input: &mut Parser<'i, 't>,
) -> Result<AtRulePrelude, ParseError<'i, StyleSheetParseError>> {
  if name.eq_ignore_ascii_case("layer") {
    let mut layer_names = input
      .try_parse(|input| input.parse_comma_separated(parse_layer_name))
      .unwrap_or_default();
    if layer_names.is_empty() {
      layer_names.push(vec![LayerName::Anonymous]);
    }
    return Ok(AtRulePrelude::Layer(layer_names));
  }

  if name.eq_ignore_ascii_case("keyframes") {
    return Ok(AtRulePrelude::Keyframes(
      input.expect_ident_or_string()?.to_string(),
    ));
  }

  if name.eq_ignore_ascii_case("media") {
    return MediaQueryList::parse(input).map(AtRulePrelude::Media);
  }

  if name.eq_ignore_ascii_case("supports") {
    return parse_supports_condition(input).map(AtRulePrelude::Supports);
  }

  if name.eq_ignore_ascii_case("property") {
    let property_name = input.expect_ident_or_string()?.to_string();
    if !property_name.starts_with("--") {
      return Err(
        input.new_custom_error(StyleSheetParseError::property_name_must_be_custom_property()),
      );
    }
    return Ok(AtRulePrelude::Property(property_name));
  }

  Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
}

fn parse_layer_name<'i, 't>(
  input: &mut Parser<'i, 't>,
) -> Result<LayerPath, ParseError<'i, StyleSheetParseError>> {
  let mut segments = Vec::new();

  loop {
    let location = input.current_source_location();
    let segment = match input.next()? {
      Token::Ident(value) | Token::QuotedString(value) => value.to_string(),
      token => return Err(location.new_unexpected_token_error(token.clone())),
    };
    segments.push(LayerName::Named(segment));

    if input.try_parse(|input| input.expect_delim('.')).is_err() {
      break;
    }
  }

  Ok(segments)
}

fn extend_layer_name(current_layer: Option<&LayerPath>, layer_name: &[LayerName]) -> LayerPath {
  if layer_name == [LayerName::Anonymous] {
    let mut nested_layer = current_layer.cloned().unwrap_or_default();
    nested_layer.push(LayerName::Anonymous);
    return nested_layer;
  }

  let mut combined = current_layer.cloned().unwrap_or_default();
  combined.extend(layer_name.iter().cloned());
  combined
}

fn ensure_single_layer_name<'i>(
  layer_names: &[LayerPath],
  input: &Parser<'i, '_>,
) -> Result<(), ParseError<'i, StyleSheetParseError>> {
  if layer_names.len() <= 1 {
    return Ok(());
  }

  Err(input.new_custom_error(StyleSheetParseError::layer_block_multiple_names()))
}

fn parse_style_rule_block<'i, 't>(
  selectors: SelectorList<SelectorImpl>,
  media_queries: &[MediaQueryList],
  layer: Option<&LayerPath>,
  lossy: bool,
  input: &mut Parser<'i, 't>,
) -> Result<StyleSheetFragment, ParseError<'i, StyleSheetParseError>> {
  let mut normal_declarations = StyleDeclarationBlock::default();
  let mut important_declarations = StyleDeclarationBlock::default();
  let layer = layer.cloned();
  let mut fragment = StyleSheetFragment::default();
  let mut parser = NestedStyleRuleParser {
    parent_selectors: selectors.clone(),
    media_queries,
    layer: layer.clone(),
    lossy,
  };

  for result in RuleBodyParser::new(input, &mut parser) {
    match result {
      Err((error, _)) => {
        if lossy {
          continue;
        }
        return Err(error);
      }
      Ok(StyleRuleBodyItem::Declarations(declarations)) => {
        let declarations = *declarations;
        if declarations.importance.is_empty() {
          normal_declarations.append(declarations);
        } else {
          important_declarations.append(declarations);
        }
      }
      Ok(StyleRuleBodyItem::Rules(nested_rules)) => {
        if !normal_declarations.declarations.is_empty()
          || !important_declarations.declarations.is_empty()
        {
          fragment.rules.push(CssRule {
            selectors: selectors.clone(),
            normal_declarations: take(&mut normal_declarations),
            important_declarations: take(&mut important_declarations),
            media_queries: media_queries.to_vec(),
            layer: layer.clone(),
            layer_order: None,
          });
        }
        fragment.extend(nested_rules);
      }
    }
  }

  if normal_declarations.declarations.is_empty() && important_declarations.declarations.is_empty() {
    return Ok(fragment);
  }

  fragment.rules.push(CssRule {
    selectors,
    normal_declarations,
    important_declarations,
    media_queries: media_queries.to_vec(),
    layer,
    layer_order: None,
  });
  Ok(fragment)
}

fn parse_nested_at_rule_block<'i, 't>(
  parent_selectors: &SelectorList<SelectorImpl>,
  media_queries: &[MediaQueryList],
  current_layer: Option<&LayerPath>,
  lossy: bool,
  prelude: AtRulePrelude,
  input: &mut Parser<'i, 't>,
) -> Result<StyleSheetFragment, ParseError<'i, StyleSheetParseError>> {
  match prelude {
    AtRulePrelude::Layer(layer_names) => {
      ensure_single_layer_name(&layer_names, input)?;
      let Some(layer_name) = layer_names.into_iter().next() else {
        return Ok(StyleSheetFragment::default());
      };
      let nested_layer = extend_layer_name(current_layer, &layer_name);
      parse_style_rule_block(
        parent_selectors.clone(),
        media_queries,
        Some(&nested_layer),
        lossy,
        input,
      )
    }
    AtRulePrelude::Media(media_query) => {
      let mut merged_media_queries = media_queries.to_vec();
      merged_media_queries.push(media_query);
      parse_style_rule_block(
        parent_selectors.clone(),
        &merged_media_queries,
        current_layer,
        lossy,
        input,
      )
    }
    AtRulePrelude::Supports(true) => parse_style_rule_block(
      parent_selectors.clone(),
      media_queries,
      current_layer,
      lossy,
      input,
    ),
    AtRulePrelude::Supports(false) => {
      let mut parser = NestedStyleRuleParser {
        parent_selectors: parent_selectors.clone(),
        media_queries,
        layer: current_layer.cloned(),
        lossy,
      };
      for _ in RuleBodyParser::new(input, &mut parser).flatten() {}
      Ok(StyleSheetFragment::default())
    }
    AtRulePrelude::Keyframes(_) | AtRulePrelude::Property(_) => {
      Err(input.new_custom_error(StyleSheetParseError::unsupported_nested_at_rule()))
    }
  }
}

impl<'i> QualifiedRuleParser<'i> for RuleParser {
  type Prelude = SelectorList<SelectorImpl>;
  type QualifiedRule = StyleSheetFragment;
  type Error = StyleSheetParseError;

  fn parse_prelude<'t>(
    &mut self,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
    SelectorList::parse(&TakumiSelectorParser, input, ParseRelative::No)
  }

  fn parse_block<'t>(
    &mut self,
    selectors: Self::Prelude,
    _location: &ParserState,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
    parse_style_rule_block(
      selectors,
      &[],
      self.current_layer.as_ref(),
      self.lossy,
      input,
    )
  }
}

impl<'i> AtRuleParser<'i> for RuleParser {
  type Prelude = AtRulePrelude;
  type AtRule = StyleSheetFragment;
  type Error = StyleSheetParseError;

  fn parse_prelude<'t>(
    &mut self,
    name: CowRcStr<'i>,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
    parse_at_rule_prelude(name, input)
  }

  fn parse_block<'t>(
    &mut self,
    prelude: Self::Prelude,
    _location: &ParserState,
    input: &mut Parser<'i, 't>,
  ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
    match prelude {
      AtRulePrelude::Layer(layer_names) => {
        ensure_single_layer_name(&layer_names, input)?;
        let declared_layers = layer_names
          .iter()
          .map(|layer_name| extend_layer_name(self.current_layer.as_ref(), layer_name))
          .collect::<Vec<_>>();
        let Some(layer_name) = layer_names.into_iter().next() else {
          return Ok(StyleSheetFragment {
            declared_layers,
            ..StyleSheetFragment::default()
          });
        };
        let nested_layer = extend_layer_name(self.current_layer.as_ref(), &layer_name);
        let mut fragment = parse_fragment_with_mode(
          input,
          &mut RuleParser {
            current_layer: Some(nested_layer),
            lossy: self.lossy,
          },
        )?;
        fragment.declared_layers.splice(0..0, declared_layers);
        Ok(fragment)
      }
      AtRulePrelude::Keyframes(name) => {
        let mut parser = KeyframeRuleParser;
        let mut keyframes = Vec::new();
        for keyframe in StyleSheetParser::new(input, &mut parser) {
          match keyframe {
            Ok(keyframe) => keyframes.push(keyframe),
            Err((error, _)) => {
              if self.lossy {
                continue;
              }
              return Err(error);
            }
          }
        }

        Ok(StyleSheetFragment {
          keyframes: vec![
            KeyframesRule::builder()
              .name(name)
              .keyframes(keyframes)
              .build(),
          ],
          ..StyleSheetFragment::default()
        })
      }
      AtRulePrelude::Media(media_query) => {
        let mut fragment = parse_fragment_with_mode(
          input,
          &mut RuleParser {
            current_layer: self.current_layer.clone(),
            lossy: self.lossy,
          },
        )?;

        for rule in &mut fragment.rules {
          rule.media_queries.push(media_query.clone());
        }
        for keyframes in &mut fragment.keyframes {
          keyframes.media_queries.push(media_query.clone());
        }
        for property_rule in &mut fragment.property_rules {
          property_rule.media_queries.push(media_query.clone());
        }

        Ok(fragment)
      }
      AtRulePrelude::Supports(is_supported) => {
        if !is_supported {
          let mut parser = RuleParser {
            current_layer: self.current_layer.clone(),
            lossy: self.lossy,
          };
          for _ in StyleSheetParser::new(input, &mut parser) {}
          return Ok(StyleSheetFragment::default());
        }

        parse_fragment_with_mode(
          input,
          &mut RuleParser {
            current_layer: self.current_layer.clone(),
            lossy: self.lossy,
          },
        )
      }
      AtRulePrelude::Property(name) => Ok(StyleSheetFragment {
        property_rules: vec![parse_property_rule(name, input)?],
        ..StyleSheetFragment::default()
      }),
    }
  }

  fn rule_without_block(
    &mut self,
    prelude: Self::Prelude,
    _start: &ParserState,
  ) -> Result<Self::AtRule, ()> {
    match prelude {
      AtRulePrelude::Layer(layer_names) => Ok(StyleSheetFragment {
        declared_layers: layer_names
          .into_iter()
          .map(|layer_name| extend_layer_name(self.current_layer.as_ref(), &layer_name))
          .collect(),
        ..StyleSheetFragment::default()
      }),
      _ => Err(()),
    }
  }
}

/// Defines a stylesheet with rules, keyframes, and property rules.
#[derive(Debug, Clone, Default)]
pub struct StyleSheet {
  pub rules: Vec<CssRule>,
  pub keyframes: Vec<KeyframesRule>,
  pub property_rules: Vec<PropertyRule>,
  pub layer_count: usize,
}

impl From<Vec<KeyframesRule>> for StyleSheet {
  fn from(keyframes: Vec<KeyframesRule>) -> Self {
    Self {
      keyframes,
      layer_count: 0,
      ..Default::default()
    }
  }
}

impl StyleSheet {
  /// Extends the stylesheet with keyframes.
  pub fn extend_keyframes(&mut self, keyframes: Vec<KeyframesRule>) {
    self.keyframes.extend(keyframes);
  }

  /// Parses a list of stylesheets.
  pub fn parse_list<I, S>(stylesheets: I) -> Result<Self, StyleSheetParseError>
  where
    I: IntoIterator<Item = S>,
    S: AsRef<str>,
  {
    let mut combined_css = String::new();

    for css in stylesheets {
      combined_css.push_str(css.as_ref());
    }

    Self::parse(&combined_css)
  }

  /// Parses a list of stylesheets while discarding invalid rules.
  pub fn parse_list_loosy<I, S>(stylesheets: I) -> Self
  where
    I: IntoIterator<Item = S>,
    S: AsRef<str>,
  {
    let mut combined_css = String::new();

    for css in stylesheets {
      combined_css.push_str(css.as_ref());
    }

    Self::parse_loosy(&combined_css)
  }

  /// Parses a list of owned stylesheets while discarding invalid rules.
  pub fn parse_owned_list_loosy(stylesheets: Vec<String>) -> Self {
    if stylesheets.is_empty() {
      return Self::default();
    }

    if stylesheets.len() == 1 {
      return Self::parse_loosy(&stylesheets[0]);
    }

    let mut combined_css = String::with_capacity(stylesheets.iter().map(String::len).sum());

    for css in stylesheets {
      combined_css.push_str(&css);
    }

    Self::parse_loosy(&combined_css)
  }

  /// Parses a stylesheet.
  pub fn parse(css: &str) -> Result<Self, StyleSheetParseError> {
    Self::parse_with_mode(css, false)
  }

  /// Parses a stylesheet while discarding invalid rules.
  pub fn parse_loosy(css: &str) -> Self {
    let Ok(stylesheet) = Self::parse_with_mode(css, true) else {
      return Self::default();
    };
    stylesheet
  }

  fn parse_with_mode(css: &str, lossy: bool) -> Result<Self, StyleSheetParseError> {
    let mut input = ParserInput::new(css);
    let mut parser = Parser::new(&mut input);
    let mut rule_parser = RuleParser {
      current_layer: None,
      lossy,
    };

    let mut rules = Vec::new();
    let mut keyframes = Vec::new();
    let mut property_rules = Vec::new();
    let mut declared_layers = Vec::new();

    for fragment in StyleSheetParser::new(&mut parser, &mut rule_parser) {
      match fragment {
        Ok(fragment) => {
          rules.extend(fragment.rules);
          keyframes.extend(fragment.keyframes);
          property_rules.extend(fragment.property_rules);
          declared_layers.extend(fragment.declared_layers);
        }
        Err((error, context)) => {
          if lossy {
            continue;
          }
          return Err(StyleSheetParseError::from_parse_error(context, error));
        }
      }
    }

    let mut layer_order = HashMap::<LayerPath, usize>::new();

    for layer_name in declared_layers {
      let next_order = layer_order.len();
      layer_order.entry(layer_name).or_insert(next_order);
    }

    for rule in &rules {
      if let Some(layer_name) = &rule.layer {
        let next_order = layer_order.len();
        layer_order.entry(layer_name.clone()).or_insert(next_order);
      }
    }

    for rule in &mut rules {
      rule.layer_order = rule
        .layer
        .as_ref()
        .and_then(|layer_name| layer_order.get(layer_name).copied());
    }

    rules.retain(|rule| {
      !rule.normal_declarations.declarations.is_empty()
        || !rule.important_declarations.declarations.is_empty()
    });

    Ok(Self {
      rules,
      keyframes,
      property_rules,
      layer_count: layer_order.len(),
    })
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use cssparser::ToCss;

  use crate::Viewport;
  use crate::style::{Color, ColorInput, ComputedStyle, Length, Style, StyleDeclaration};

  fn computed_style_from_declarations(declarations: &StyleDeclarationBlock) -> ComputedStyle {
    let mut style = Style::default();
    for declaration in &declarations.declarations {
      declaration.merge_into_ref(&mut style);
    }
    style.inherit(&ComputedStyle::default())
  }

  fn selector_text(rule: &CssRule) -> String {
    rule.selectors.to_css_string()
  }

  fn parse_stylesheet(css: &str) -> StyleSheet {
    let result = StyleSheet::parse(css);
    assert!(result.is_ok(), "expected stylesheet to parse: {result:?}");
    result.unwrap_or_default()
  }

  fn parse_stylesheet_loosy(css: &str) -> StyleSheet {
    StyleSheet::parse_loosy(css)
  }

  fn assert_lossy_parse_keeps_single_valid_rule(css: &str) {
    let sheet = parse_stylesheet_loosy(css);
    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_parse_stylesheet() {
    let css = r#"
            .box {
                width: 100px;
                color: red;
            }
        "#;
    let sheet = parse_stylesheet(css);
    assert_eq!(sheet.rules.len(), 1);
    let rule = &sheet.rules[0];

    assert_eq!(rule.selectors.slice().len(), 1);
    assert_eq!(
      computed_style_from_declarations(&rule.normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_parse_stylesheet_compound_selectors_specificity() {
    let sheet = parse_stylesheet(
      r#"
        div.box { width: 10px; }
        #hero .label { height: 20px; }
      "#,
    );
    assert_eq!(sheet.rules.len(), 2);
    assert_eq!(sheet.rules[0].selectors.slice().len(), 1);
    assert_eq!(sheet.rules[1].selectors.slice().len(), 1);
    assert!(sheet.rules[0].selectors.slice()[0].specificity() > 0);
    assert!(
      sheet.rules[1].selectors.slice()[0].specificity()
        > sheet.rules[0].selectors.slice()[0].specificity()
    );
  }

  #[test]
  fn test_parse_stylesheet_multiple_rules() {
    let sheet = parse_stylesheet(
      r#"
        .a { width: 10px; }
        .b { height: 20px; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 2);
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(10.0)
    );
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[1].normal_declarations).height,
      Length::Px(20.0)
    );
  }

  #[test]
  fn test_parse_stylesheet_multiple_selectors_in_rule() {
    let sheet = parse_stylesheet(
      r#"
        .a, .b { width: 12px; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(sheet.rules[0].selectors.slice().len(), 2);
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(12.0)
    );
  }

  #[test]
  fn test_parse_stylesheet_universal_selector() {
    let sheet = parse_stylesheet(
      r#"
        * { width: 100px; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), "*");
    assert_eq!(sheet.rules[0].selectors.slice().len(), 1);
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_parse_stylesheet_important_declaration() {
    let sheet = parse_stylesheet(
      r#"
        .a { width: 10px !important; height: 20px; }
      "#,
    );

    let rule = &sheet.rules[0];
    assert_eq!(
      computed_style_from_declarations(&rule.important_declarations).width,
      Length::Px(10.0)
    );
    assert_eq!(
      computed_style_from_declarations(&rule.normal_declarations).height,
      Length::Px(20.0)
    );
  }

  #[test]
  fn test_parse_stylesheet_shorthand_clears_prior_longhand() {
    let sheet = parse_stylesheet(
      r#"
        .a { padding-left: 4px; padding: 10px; }
      "#,
    );

    let declarations = &sheet.rules[0].normal_declarations;
    assert_eq!(declarations.declarations.len(), 5);
    assert_eq!(
      declarations.declarations[0],
      StyleDeclaration::padding_left(Length::Px(4.0))
    );
    assert_eq!(
      declarations.declarations[1],
      StyleDeclaration::padding_top(Length::Px(10.0))
    );
    assert_eq!(
      declarations.declarations[2],
      StyleDeclaration::padding_right(Length::Px(10.0))
    );
    assert_eq!(
      declarations.declarations[3],
      StyleDeclaration::padding_bottom(Length::Px(10.0))
    );
    assert_eq!(
      declarations.declarations[4],
      StyleDeclaration::padding_left(Length::Px(10.0))
    );
  }

  #[test]
  fn test_parse_stylesheet_webkit_alias_property() {
    let sheet = parse_stylesheet(
      r#"
        .a { -webkit-text-fill-color: rgb(255, 0, 0); }
      "#,
    );

    let style = computed_style_from_declarations(&sheet.rules[0].normal_declarations);
    assert_eq!(
      style.webkit_text_fill_color,
      Some(ColorInput::Value(Color([255, 0, 0, 255])))
    );
  }

  #[test]
  fn test_parse_stylesheet_unknown_property_does_not_drop_supported_declarations() {
    let sheet = parse_stylesheet(
      r#"
        .a { --local-token: 1; width: 14px; unsupported-prop: 2; height: 6px; }
      "#,
    );

    let style = computed_style_from_declarations(&sheet.rules[0].normal_declarations);
    assert_eq!(style.width, Length::Px(14.0));
    assert_eq!(style.height, Length::Px(6.0));
  }

  #[test]
  fn test_attribute_selector_rule_is_preserved() {
    let sheet = parse_stylesheet_loosy(
      r#"
        [data-kind="hero"] { width: 10px; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
  }

  #[test]
  fn test_parse_stylesheet_accepts_attribute_selector() {
    let result = StyleSheet::parse(
      r#"
        [data-kind="hero"] { width: 10px; }
      "#,
    );

    assert!(result.is_ok());
  }

  #[test]
  fn test_ignored_pseudo_selector_rule_is_kept_but_never_matches() {
    let sheet = parse_stylesheet_loosy(
      r#"
        .a:hover { width: 10px; }
        .a, .a:hover { height: 20px; }
        .a::before { color: red; }
        .a:lang(en) { color: blue; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 4);
    assert_eq!(selector_text(&sheet.rules[0]), ".a:hover");
    assert_eq!(selector_text(&sheet.rules[1]), ".a, .a:hover");
    assert_eq!(selector_text(&sheet.rules[2]), ".a::before");
    assert_eq!(selector_text(&sheet.rules[3]), ".a:lang");
  }

  #[test]
  fn test_is_and_where_selectors_are_accepted() {
    let sheet = parse_stylesheet_loosy(
      r#"
        .a:where(.b) div { background: red; }
        .a:is(.b, .c) { color: blue; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 2);
    assert_eq!(selector_text(&sheet.rules[0]), ".a:where(.b) div");
    assert_eq!(selector_text(&sheet.rules[1]), ".a:is(.b, .c)");
  }

  #[test]
  fn test_parse_keyframes_rule() {
    let sheet = parse_stylesheet(
      r#"
        @keyframes fade {
          from { opacity: 0; }
          50% { opacity: 0.5; }
          to { opacity: 1; }
        }
      "#,
    );

    assert!(sheet.rules.is_empty());
    assert_eq!(sheet.keyframes.len(), 1);
    assert_eq!(sheet.keyframes[0].name, "fade");
    assert_eq!(sheet.keyframes[0].keyframes.len(), 3);
    assert_eq!(sheet.keyframes[0].keyframes[0].offsets, vec![0.0]);
    assert_eq!(sheet.keyframes[0].keyframes[1].offsets, vec![0.5]);
    assert_eq!(sheet.keyframes[0].keyframes[2].offsets, vec![1.0]);
  }

  #[test]
  fn test_parse_media_rule_with_viewport_features() {
    let sheet = parse_stylesheet(
      r#"
        @media screen and (min-width: 600px) and (orientation: landscape) {
          .card { width: 100px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert!(sheet.keyframes.is_empty());
    assert!(
      sheet.rules[0]
        .media_queries
        .first()
        .is_some_and(|media| media.matches(Viewport::new((800, 600))))
    );
    assert!(
      !sheet.rules[0]
        .media_queries
        .first()
        .is_some_and(|media| media.matches(Viewport::new((500, 800))))
    );
  }

  #[test]
  fn test_parse_media_rule_with_comma_list() {
    let sheet = parse_stylesheet(
      r#"
        @media (max-width: 480px), (min-width: 1024px) {
          .card { width: 100px; }
        }
      "#,
    );

    let Some(media) = sheet.rules[0].media_queries.first() else {
      unreachable!("expected media queries on parsed rule");
    };
    assert!(media.matches(Viewport::new((400, 800))));
    assert!(media.matches(Viewport::new((1280, 800))));
    assert!(!media.matches(Viewport::new((800, 800))));
  }

  #[test]
  fn test_parse_media_rule_applies_to_keyframes_and_property_rules() {
    let sheet = parse_stylesheet(
      r#"
        @media (min-width: 600px) {
          @keyframes fade {
            from { opacity: 0; }
            to { opacity: 1; }
          }

          @property --box-size {
            syntax: "<length>";
            inherits: false;
            initial-value: 10px;
          }
        }
      "#,
    );

    assert_eq!(sheet.keyframes.len(), 1);
    assert_eq!(sheet.property_rules.len(), 1);
    assert!(
      sheet.keyframes[0]
        .media_queries
        .first()
        .is_some_and(|media| media.matches(Viewport::new((800, 600))))
    );
    assert!(
      sheet.property_rules[0]
        .media_queries
        .first()
        .is_some_and(|media| media.matches(Viewport::new((800, 600))))
    );
  }

  #[test]
  fn test_parse_nested_rule_is_flattened() {
    let sheet = parse_stylesheet(
      r#"
        .card {
          width: 100px;
          .title { height: 20px; }
          & > .icon { width: 12px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 3);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(selector_text(&sheet.rules[1]), ":is(.card) .title");
    assert_eq!(selector_text(&sheet.rules[2]), ":is(.card) > .icon");
  }

  #[test]
  fn test_parse_nested_rule_cross_product_for_selector_lists() {
    let sheet = parse_stylesheet(
      r#"
        .card, .panel {
          & .title, & .subtitle { width: 12px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(
      selector_text(&sheet.rules[0]),
      ":is(.card, .panel) .title, :is(.card, .panel) .subtitle"
    );
  }

  #[test]
  fn test_parse_nested_rule_uses_is_wrapper_for_multi_parent_lists() {
    let sheet = parse_stylesheet(
      r#"
        .card, .panel {
          & + .item { width: 12px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ":is(.card, .panel) + .item");
  }

  #[test]
  fn test_parse_nested_media_and_supports_rules() {
    let sheet = parse_stylesheet(
      r#"
        .card {
          @media (min-width: 600px) {
            @supports (display: grid) {
              width: 100px;
            }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(sheet.rules[0].media_queries.len(), 1);
    assert!(
      sheet.rules[0]
        .media_queries
        .first()
        .is_some_and(|media| media.matches(Viewport::new((800, 600))))
    );
  }

  #[test]
  fn test_parse_multiple_nested_media_queries_accumulate() {
    let sheet = parse_stylesheet(
      r#"
        .card {
          @media (min-width: 600px) {
            @media (orientation: landscape) {
              width: 100px;
            }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(sheet.rules[0].media_queries.len(), 2);
    assert!(sheet.rules[0].media_queries[0].matches(Viewport::new((800, 600))));
    assert!(sheet.rules[0].media_queries[1].matches(Viewport::new((800, 600))));
    assert!(!sheet.rules[0].media_queries[1].matches(Viewport::new((500, 800))));
  }

  #[test]
  fn test_parse_supports_rule_filters_unsupported_declarations() {
    let sheet = parse_stylesheet(
      r#"
        @supports (display: grid) {
          .card { width: 100px; }
        }

        @supports (unknown-prop: nope) {
          .card { height: 20px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_parse_supports_not_and_or_conditions() {
    let sheet = parse_stylesheet(
      r#"
        @supports (display: grid) and (not (unknown-prop: nope)) {
          .grid { width: 10px; }
        }

        @supports (unknown-prop: nope) or (display: flex) {
          .flex { height: 20px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 2);
    assert_eq!(selector_text(&sheet.rules[0]), ".grid");
    assert_eq!(selector_text(&sheet.rules[1]), ".flex");
  }

  #[test]
  fn test_parse_supports_mixed_and_or_requires_parentheses() {
    let sheet = parse_stylesheet_loosy(
      r#"
        @supports (display: grid) and (color: red) or (display: flex) {
          .invalid { width: 10px; }
        }

        .valid { height: 20px; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".valid");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).height,
      Length::Px(20.0)
    );
  }

  #[test]
  fn test_parse_property_rule() {
    let sheet = parse_stylesheet(
      r#"
        @property --box-size {
          syntax: "<length>";
          inherits: false;
          initial-value: 10px;
        }
      "#,
    );

    assert_eq!(sheet.property_rules.len(), 1);
    assert_eq!(sheet.property_rules[0].name, "--box-size");
    assert_eq!(sheet.property_rules[0].syntax, "\"<length>\"");
    assert!(!sheet.property_rules[0].inherits);
    assert_eq!(
      sheet.property_rules[0].initial_value,
      Some("10px".to_owned())
    );
  }

  #[test]
  fn test_parse_property_rule_descriptors_case_insensitively() {
    let sheet = parse_stylesheet(
      r#"
        @property --box-size {
          SYNTAX: "<length>";
          InHeRiTs: false;
          INITIAL-VALUE: 10px;
        }
      "#,
    );

    assert_eq!(sheet.property_rules.len(), 1);
    assert_eq!(sheet.property_rules[0].name, "--box-size");
    assert_eq!(sheet.property_rules[0].syntax, "\"<length>\"");
    assert!(!sheet.property_rules[0].inherits);
    assert_eq!(
      sheet.property_rules[0].initial_value,
      Some("10px".to_owned())
    );
  }

  #[test]
  fn test_parse_property_rule_requires_initial_value_for_typed_syntax() {
    let sheet = parse_stylesheet(
      r#"
        @property --tw-rotate-x {
          syntax: "*";
          inherits: false;
        }
      "#,
    );

    assert_eq!(sheet.property_rules.len(), 1);
    assert_eq!(sheet.property_rules[0].name, "--tw-rotate-x");
    assert_eq!(sheet.property_rules[0].syntax, "\"*\"");
    assert!(!sheet.property_rules[0].inherits);
    assert_eq!(sheet.property_rules[0].initial_value, None);

    let sheet = parse_stylesheet(
      r#"
        @property --box-size {
          syntax: "<length>";
          inherits: false;
        }
      "#,
    );

    assert_eq!(sheet.property_rules.len(), 1);
    assert_eq!(sheet.property_rules[0].initial_value, None);
  }

  #[test]
  fn test_parse_property_rule_supports_extended_syntaxes() {
    let sheet = parse_stylesheet(
      r#"
        @property --accent {
          syntax: "<length> | <color>";
          inherits: false;
          initial-value: red;
        }
        @property --display-state {
          syntax: "none | auto";
          inherits: false;
          initial-value: none;
        }
        @property --fade-duration {
          syntax: "<time>";
          inherits: false;
          initial-value: 150ms;
        }
        @property --move {
          syntax: "<transform-function>";
          inherits: false;
          initial-value: translate(10px, 20px);
        }
        @property --curve {
          syntax: "<easing-function>";
          inherits: false;
          initial-value: ease-in-out;
        }
        @property --fx {
          syntax: "<filter-function>";
          inherits: false;
          initial-value: blur(4px);
        }
        @property --bg {
          syntax: "<image>";
          inherits: false;
          initial-value: linear-gradient(red, blue);
        }
      "#,
    );

    assert_eq!(sheet.property_rules.len(), 7);
    assert_eq!(sheet.property_rules[0].syntax, "\"<length> | <color>\"");
    assert_eq!(sheet.property_rules[1].syntax, "\"none | auto\"");
    assert_eq!(sheet.property_rules[2].syntax, "\"<time>\"");
    assert_eq!(sheet.property_rules[3].syntax, "\"<transform-function>\"");
    assert_eq!(sheet.property_rules[4].syntax, "\"<easing-function>\"");
    assert_eq!(sheet.property_rules[5].syntax, "\"<filter-function>\"");
    assert_eq!(sheet.property_rules[6].syntax, "\"<image>\"");
  }

  #[test]
  fn test_lossy_parse_rejects_invalid_property_rules() {
    for css in [
      r#"
        @property color {
          syntax: "<color>";
          inherits: false;
          initial-value: red;
        }

        .card { width: 100px; }
      "#,
      r#"
        @property --box-size {
          inherits: false;
          initial-value: 10px;
        }

        .card { width: 100px; }
      "#,
      r#"
        @property --accent {
          syntax: "<color>";
          initial-value: red;
        }

        .card { width: 100px; }
      "#,
      r#"
        @property --box-size {
          syntax: "<length>";
          inherits: maybe;
          initial-value: 10px;
        }

        .card { width: 100px; }
      "#,
    ] {
      let sheet = parse_stylesheet_loosy(css);

      assert!(sheet.property_rules.is_empty());
      assert_eq!(sheet.rules.len(), 1);
      assert_eq!(selector_text(&sheet.rules[0]), ".card");
      assert_eq!(
        computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
        Length::Px(100.0)
      );
    }
  }

  #[test]
  fn test_parse_stylesheet_returns_error_for_invalid_property_rule() {
    let result = StyleSheet::parse(
      r#"
        @property --box-size {
          inherits: false;
          initial-value: 10px;
        }
      "#,
    );

    assert!(result.is_err());
  }

  #[test]
  fn test_property_rule_computationally_dependent_initial_value_is_preserved() {
    let sheet = parse_stylesheet(
      r#"
        @property --box-size {
          syntax: "<length>";
          inherits: false;
          initial-value: var(--fallback);
        }
      "#,
    );

    assert_eq!(sheet.property_rules.len(), 1);
    assert_eq!(
      sheet.property_rules[0].initial_value,
      Some("var(--fallback)".to_owned())
    );
  }

  #[test]
  fn test_parse_layer_rule_without_block() {
    let sheet = parse_stylesheet(
      r#"
        @layer theme, base, components, utilities;
        @layer utilities {
          .card { width: 100px; }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      sheet.rules[0].layer.as_ref(),
      Some(&vec![LayerName::Named("utilities".to_owned())])
    );
    assert_eq!(sheet.rules[0].layer_order, Some(3));
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_parse_nested_layers_are_transparent() {
    let sheet = parse_stylesheet(
      r#"
        @layer theme {
          @layer components {
            .card { width: 100px; }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      sheet.rules[0].layer.as_ref(),
      Some(&vec![
        LayerName::Named("theme".to_owned()),
        LayerName::Named("components".to_owned()),
      ])
    );
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_parse_nested_layer_inside_style_rule_preserves_parent_selector() {
    let sheet = parse_stylesheet(
      r#"
        .card {
          @layer theme {
            width: 100px;
            .title { height: 20px; }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 2);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(selector_text(&sheet.rules[1]), ":is(.card) .title");
    assert_eq!(
      sheet.rules[0].layer.as_ref(),
      Some(&vec![LayerName::Named("theme".to_owned())])
    );
    assert_eq!(
      sheet.rules[1].layer.as_ref(),
      Some(&vec![LayerName::Named("theme".to_owned())])
    );
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[1].normal_declarations).height,
      Length::Px(20.0)
    );
  }

  #[test]
  fn test_parse_anonymous_nested_layer_has_distinct_order() {
    let sheet = parse_stylesheet(
      r#"
        @layer theme {
          .parent { width: 10px; }

          @layer {
            .child { width: 20px; }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 2);
    assert_eq!(
      sheet.rules[0].layer.as_ref(),
      Some(&vec![LayerName::Named("theme".to_owned())])
    );
    assert_eq!(
      sheet.rules[1].layer.as_ref(),
      Some(&vec![
        LayerName::Named("theme".to_owned()),
        LayerName::Anonymous,
      ])
    );
    assert_ne!(sheet.rules[0].layer_order, sheet.rules[1].layer_order);
  }

  #[test]
  fn test_parse_layer_block_rejects_multiple_names() {
    let sheet = parse_stylesheet_loosy(
      r#"
        @layer theme, components {
          .invalid { width: 10px; }
        }

        .valid { height: 20px; }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".valid");
    assert_eq!(sheet.rules[0].layer, None);
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).height,
      Length::Px(20.0)
    );
  }

  #[test]
  fn test_parse_nested_rules_preserves_source_order() {
    let sheet = parse_stylesheet(
      r#"
        .card {
          width: 100px;
          & .title { color: red; }
          height: 20px;
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 3);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
    assert_eq!(selector_text(&sheet.rules[1]), ":is(.card) .title");
    assert_eq!(selector_text(&sheet.rules[2]), ".card");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[2].normal_declarations).height,
      Length::Px(20.0)
    );
  }

  #[test]
  fn test_nested_unsupported_supports_rule_is_discarded() {
    let sheet = parse_stylesheet(
      r#"
        .card {
          width: 100px;
          @supports (unknown-prop: nope) {
            height: 20px;
            & .title { color: red; }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");

    let computed = computed_style_from_declarations(&sheet.rules[0].normal_declarations);
    assert_eq!(computed.width, Length::Px(100.0));
    assert_eq!(computed.height, Length::Auto);
  }

  #[test]
  fn test_nested_keyframes_rule_is_rejected() {
    let sheet = parse_stylesheet_loosy(
      r#"
        .card {
          width: 100px;
          @keyframes pulse {
            from { opacity: 0; }
            to { opacity: 1; }
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert_eq!(sheet.keyframes.len(), 0);
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_nested_property_rule_is_rejected() {
    let sheet = parse_stylesheet_loosy(
      r#"
        .card {
          width: 100px;
          @property --box-size {
            syntax: "<length>";
            inherits: false;
            initial-value: 10px;
          }
        }
      "#,
    );

    assert_eq!(sheet.rules.len(), 1);
    assert!(sheet.property_rules.is_empty());
    assert_eq!(selector_text(&sheet.rules[0]), ".card");
    assert_eq!(
      computed_style_from_declarations(&sheet.rules[0].normal_declarations).width,
      Length::Px(100.0)
    );
  }

  #[test]
  fn test_lossy_parse_rejects_unknown_rules_and_keeps_valid_siblings() {
    for css in [
      r#"
        @media (resolution: 2dppx) {
          .card { width: 10px; }
        }

        .card { width: 100px; }
      "#,
      r#"
        @unknown something {
          .card { width: 10px; }
        }

        .card { width: 100px; }
      "#,
    ] {
      assert_lossy_parse_keeps_single_valid_rule(css);
    }
  }
}
