///|
/// A pure condition over the current event and previously completed captures.
/// Arrays are detached when a Stage or Pattern is constructed.
pub(all) enum Condition {
  Always
  Kind(String)
  Has(String)
  Compare(String, Comparison, Value)
  FieldCompare(String, Comparison, String)
  CaptureCompare(String, Comparison, String, String)
  CaptureCount(String, Comparison, Int32)
  TextPrefix(String, String)
  TextContains(String, String)
  All(Array[Condition])
  Any(Array[Condition])
  Not(Condition)
} derive(Eq, @debug.Debug)

///|
pub extend Condition with Eq::{equal, not_equal}

///|
pub extend Condition with @debug.Debug::{to_repr}

///|
/// Events captured by one named positive stage, in their matching order.
pub(all) struct Capture {
  name : String
  events : Array[Event]
} derive(Eq, @debug.Debug)

///|
pub extend Capture with Eq::{equal, not_equal}

///|
pub extend Capture with @debug.Debug::{to_repr}

///|
fn validate_capture_reference(
  name : String,
  allowed : Array[String]?,
) -> Unit raise CepError {
  require_label(name, "condition.capture")
  if allowed is Some(names) && !names.contains(name) {
    raise Invalid(
      path="condition.capture",
      reason="capture \{name} must name an earlier stage",
    )
  }
}

///|
fn validate_condition_node(
  condition : Condition,
  allowed : Array[String]?,
  depth : Int32,
  used : Int32,
) -> Int32 raise CepError {
  if depth > 32 {
    raise Resource(resource="condition depth", limit=32)
  }
  let mut nodes = used + 1
  if nodes > 512 {
    raise Resource(resource="condition nodes", limit=512)
  }
  match condition {
    Always => ()
    Kind(kind) => require_label(kind, "condition.kind")
    Has(field) => require_label(field, "condition.field")
    Compare(field, _, value) => {
      require_label(field, "condition.field")
      value.validate()
    }
    FieldCompare(left, _, right) => {
      require_label(left, "condition.field")
      require_label(right, "condition.other_field")
    }
    CaptureCompare(field, _, stage, captured_field) => {
      require_label(field, "condition.field")
      validate_capture_reference(stage, allowed)
      require_label(captured_field, "condition.captured_field")
    }
    CaptureCount(stage, _, count) => {
      validate_capture_reference(stage, allowed)
      if count < 0 {
        raise Invalid(
          path="condition.capture_count",
          reason="capture count must be nonnegative",
        )
      }
    }
    TextPrefix(field, text) | TextContains(field, text) => {
      require_label(field, "condition.field")
      Text(text).validate()
    }
    All(children) | Any(children) =>
      for child in children {
        nodes = validate_condition_node(child, allowed, depth + 1, nodes)
      }
    Not(child) =>
      nodes = validate_condition_node(child, allowed, depth + 1, nodes)
  }
  nodes
}

///|
/// Validates the complete AST, including branches that would short circuit.
/// Depth is at most 32 and the unfolded node count is at most 512. These bounds
/// also reject cyclic arrays before any copying or runtime evaluation occurs.
pub fn Condition::validate(
  self : Condition,
  allowed_captures : Array[String],
) -> Unit raise CepError {
  ignore(validate_condition_node(self, Some(allowed_captures), 1, 0))
}

///|
/// Structural validation is used before stage construction, where the names
/// of earlier captures are not known until the enclosing pattern is built.
fn validate_condition_structure(condition : Condition) -> Unit raise CepError {
  ignore(validate_condition_node(condition, None, 1, 0))
}

///|
/// Detaches every mutable child array. Callers validate before cloning.
fn clone_condition(condition : Condition) -> Condition {
  match condition {
    All(children) => All(children.map(clone_condition))
    Any(children) => Any(children.map(clone_condition))
    Not(child) => Not(clone_condition(child))
    _ => condition
  }
}