///|
/// CSS Media Query Evaluation
/// Evaluates media queries against viewport dimensions

///|
/// Color scheme preference
pub(all) enum ColorScheme {
  Light
  Dark
} derive(Eq, Debug)

///|
pub impl Show for ColorScheme with output(self, logger) {
  logger.write_string(
    match self {
      Light => "Light"
      Dark => "Dark"
    },
  )
}

///|
/// Environment for evaluating media queries
pub(all) struct MediaEnvironment {
  viewport_width : Double
  viewport_height : Double
  device_pixel_ratio : Double
  color_scheme : ColorScheme
}

///|
/// Create a default media environment for screen
pub fn MediaEnvironment::new(
  viewport_width : Double,
  viewport_height : Double,
) -> MediaEnvironment {
  {
    viewport_width,
    viewport_height,
    device_pixel_ratio: 1.0,
    color_scheme: Light,
  }
}

///|
/// Create a media environment with color scheme
pub fn MediaEnvironment::with_color_scheme(
  viewport_width : Double,
  viewport_height : Double,
  color_scheme : ColorScheme,
) -> MediaEnvironment {
  { viewport_width, viewport_height, device_pixel_ratio: 1.0, color_scheme }
}

///|
/// Evaluate a media query list against the environment
/// Returns true if ANY query in the list matches (OR semantics)
pub fn MediaQueryList::evaluate(
  self : MediaQueryList,
  env : MediaEnvironment,
) -> Bool {
  // Empty query list matches everything
  if self.queries.is_empty() {
    return true
  }
  // Any matching query makes the whole list match
  for query in self.queries {
    if query.evaluate(env) {
      return true
    }
  }
  false
}

///|
/// Evaluate a single media query
pub fn MediaQuery::evaluate(self : MediaQuery, env : MediaEnvironment) -> Bool {
  let result = self.evaluate_without_negation(env)
  if self.negated {
    !result
  } else {
    result
  }
}

///|
/// Evaluate a media query without considering negation
fn MediaQuery::evaluate_without_negation(
  self : MediaQuery,
  env : MediaEnvironment,
) -> Bool {
  // Check media type
  match self.media_type {
    Some(MediaType::Print) => return false // We're rendering to screen
    Some(MediaType::Speech) => return false
    Some(MediaType::All) | Some(MediaType::Screen) | None => ()
  }
  // Check all conditions (AND semantics)
  for condition in self.conditions {
    if !evaluate_feature(condition, env) {
      return false
    }
  }
  true
}

///|
/// Evaluate a single media feature
fn evaluate_feature(feature : MediaFeature, env : MediaEnvironment) -> Bool {
  match feature {
    // Width features
    MediaFeature::Width(value) =>
      match value {
        Some(v) => compare_value(v, env.viewport_width, RangeOp::Eq)
        None => true // Boolean context: width exists
      }
    MediaFeature::MinWidth(value) =>
      compare_value(value, env.viewport_width, RangeOp::Ge)
    MediaFeature::MaxWidth(value) =>
      compare_value(value, env.viewport_width, RangeOp::Le)
    MediaFeature::WidthRange(op, value) =>
      compare_value_with_op(value, env.viewport_width, op)
    // Height features
    MediaFeature::Height(value) =>
      match value {
        Some(v) => compare_value(v, env.viewport_height, RangeOp::Eq)
        None => true
      }
    MediaFeature::MinHeight(value) =>
      compare_value(value, env.viewport_height, RangeOp::Ge)
    MediaFeature::MaxHeight(value) =>
      compare_value(value, env.viewport_height, RangeOp::Le)
    MediaFeature::HeightRange(op, value) =>
      compare_value_with_op(value, env.viewport_height, op)
    // Aspect ratio
    MediaFeature::AspectRatio(value) =>
      match value {
        Some(v) =>
          compare_aspect_ratio(
            v,
            env.viewport_width,
            env.viewport_height,
            RangeOp::Eq,
          )
        None => true
      }
    MediaFeature::MinAspectRatio(value) =>
      compare_aspect_ratio(
        value,
        env.viewport_width,
        env.viewport_height,
        RangeOp::Ge,
      )
    MediaFeature::MaxAspectRatio(value) =>
      compare_aspect_ratio(
        value,
        env.viewport_width,
        env.viewport_height,
        RangeOp::Le,
      )
    // Orientation
    MediaFeature::Orientation(value) =>
      match value.to_lower() {
        "portrait" => env.viewport_height >= env.viewport_width
        "landscape" => env.viewport_width > env.viewport_height
        _ => true
      }
    // Resolution
    MediaFeature::Resolution(value) =>
      match value {
        Some(v) => compare_resolution(v, env.device_pixel_ratio, RangeOp::Eq)
        None => true
      }
    MediaFeature::MinResolution(value) =>
      compare_resolution(value, env.device_pixel_ratio, RangeOp::Ge)
    MediaFeature::MaxResolution(value) =>
      compare_resolution(value, env.device_pixel_ratio, RangeOp::Le)
    // Boolean features (always true for screen)
    MediaFeature::Color => true
    MediaFeature::ColorIndex => false
    MediaFeature::ColorGamut => true
    MediaFeature::Grid => false
    MediaFeature::Hover => true // Assume hover is available
    MediaFeature::Monochrome => false
    MediaFeature::Pointer => true // Assume pointer is available
    MediaFeature::Scripting => true
    MediaFeature::Update => true
    // Discrete features
    MediaFeature::DisplayMode(_) => true // Assume browser mode
    MediaFeature::PrefersColorScheme(scheme) =>
      match scheme.to_lower() {
        "dark" => env.color_scheme == Dark
        "light" => env.color_scheme == Light
        _ => true // Unknown scheme matches any
      }
    MediaFeature::PrefersReducedMotion(_) => true // Accept any motion preference
    // Unknown features default to true
    MediaFeature::Custom(_, _) => true
  }
}

///|
/// Convert media value to pixels
fn to_pixels(value : MediaValue) -> Double {
  match value {
    MediaValue::Number(n) => n
    MediaValue::Dimension(n, unit) =>
      match unit.to_lower() {
        "px" => n
        "em" | "rem" => n * 16.0 // Assume 16px base font size
        "vw" | "vh" => n // Would need viewport for accurate conversion
        "pt" => n * 1.333333 // 1pt = 4/3 px
        "pc" => n * 16.0 // 1pc = 16px
        "in" => n * 96.0 // 1in = 96px
        "cm" => n * 37.8 // 1cm ≈ 37.8px
        "mm" => n * 3.78 // 1mm ≈ 3.78px
        _ => n
      }
    MediaValue::Ratio(_, _) => 0.0 // Ratios aren't pixel values
    MediaValue::Ident(_) => 0.0 // Identifiers aren't pixel values
  }
}

///|
/// Compare a media value against an actual value
/// For min-X: actual >= query (Le means query <= actual)
/// For max-X: actual <= query (Ge means query >= actual)
fn compare_value(
  query_value : MediaValue,
  actual : Double,
  op : RangeOp,
) -> Bool {
  let query_pixels = to_pixels(query_value)
  match op {
    RangeOp::Eq => (actual - query_pixels).abs() < 0.01
    RangeOp::Lt => actual < query_pixels
    RangeOp::Le => actual <= query_pixels
    RangeOp::Gt => actual > query_pixels
    RangeOp::Ge => actual >= query_pixels
  }
}

///|
/// Compare value with swapped semantics for range syntax
/// (width >= 500px) means actual >= query
fn compare_value_with_op(
  query_value : MediaValue,
  actual : Double,
  op : RangeOp,
) -> Bool {
  let query_pixels = to_pixels(query_value)
  match op {
    RangeOp::Eq => (actual - query_pixels).abs() < 0.01
    RangeOp::Lt => actual < query_pixels
    RangeOp::Le => actual <= query_pixels
    RangeOp::Gt => actual > query_pixels
    RangeOp::Ge => actual >= query_pixels
  }
}

///|
/// Compare aspect ratio
fn compare_aspect_ratio(
  query_value : MediaValue,
  width : Double,
  height : Double,
  op : RangeOp,
) -> Bool {
  if height == 0.0 {
    return false
  }
  let actual_ratio = width / height
  let query_ratio = match query_value {
    MediaValue::Ratio(w, h) =>
      if h == 0 {
        0.0
      } else {
        w.to_double() / h.to_double()
      }
    MediaValue::Number(n) => n
    _ => 1.0
  }
  match op {
    RangeOp::Eq => (actual_ratio - query_ratio).abs() < 0.01
    RangeOp::Lt => actual_ratio < query_ratio
    RangeOp::Le => actual_ratio <= query_ratio
    RangeOp::Gt => actual_ratio > query_ratio
    RangeOp::Ge => actual_ratio >= query_ratio
  }
}

///|
/// Compare resolution (device pixel ratio)
fn compare_resolution(
  query_value : MediaValue,
  actual_dpr : Double,
  op : RangeOp,
) -> Bool {
  let query_dpr = match query_value {
    MediaValue::Number(n) => n
    MediaValue::Dimension(n, unit) =>
      match unit.to_lower() {
        "dppx" | "x" => n
        "dpi" => n / 96.0
        "dpcm" => n / 37.8
        _ => n
      }
    _ => 1.0
  }
  match op {
    RangeOp::Eq => (actual_dpr - query_dpr).abs() < 0.01
    RangeOp::Lt => actual_dpr < query_dpr
    RangeOp::Le => actual_dpr <= query_dpr
    RangeOp::Gt => actual_dpr > query_dpr
    RangeOp::Ge => actual_dpr >= query_dpr
  }
}