/// Runtime AST — mirrors the compile-time macro AST but operates on owned strings.

#[derive(Debug, Clone)]
pub enum Node {
    Element(Element),
    Text(Vec<TextPart>),
    If(IfBlock),
    For(ForBlock),
    Match(MatchBlock),
    LetDecl(LetDecl),
    Include(IncludeNode),
    RawText(String),
}

#[derive(Debug, Clone)]
pub struct Element {
    pub tag: String,
    pub id: Option<String>,
    pub classes: Vec<String>,
    pub conditional_classes: Vec<ConditionalClass>,
    pub event_handlers: Vec<EventHandler>,
    pub bindings: Vec<Binding>,
    pub animations: Vec<AnimationSpec>,
    pub children: Vec<Node>,
}

#[derive(Debug, Clone)]
pub struct ConditionalClass {
    pub class: String,
    pub condition: String,
}

#[derive(Debug, Clone)]
pub struct EventHandler {
    pub event: String,
    pub modifiers: Vec<String>,
    pub handler: String,
}

#[derive(Debug, Clone)]
pub struct Binding {
    pub prop: String,
    pub value: String,
}

/// An animation specification parsed from `animate:property={duration easing}`.
///
/// ```text
/// div animate:opacity={300ms ease-in-out} animate:scale={500ms linear}
/// ```
#[derive(Debug, Clone)]
pub struct AnimationSpec {
    /// The property to animate: "opacity", "scale", "slide-x", "slide-y", "fade", "pulse"
    pub property: String,
    /// Duration expression (e.g. "300ms", "1s", "0.5s")
    pub duration_expr: String,
    /// Easing function name: "linear", "ease-in-out", "quadratic", "bounce"
    pub easing: String,
    /// Whether the animation loops
    pub repeat: bool,
}

#[derive(Debug, Clone)]
pub enum TextPart {
    Literal(String),
    Expr(String),
}

#[derive(Debug, Clone)]
pub struct IfBlock {
    pub condition: String,
    pub then_children: Vec<Node>,
    pub else_children: Option<Vec<Node>>,
}

#[derive(Debug, Clone)]
pub struct ForBlock {
    pub pattern: String,
    pub iterator: String,
    pub body: Vec<Node>,
}

#[derive(Debug, Clone)]
pub struct MatchBlock {
    pub expr: String,
    pub arms: Vec<MatchArm>,
}

#[derive(Debug, Clone)]
pub struct MatchArm {
    pub pattern: String,
    pub body: Vec<Node>,
}

#[derive(Debug, Clone)]
pub struct LetDecl {
    pub name: String,
    pub expr: String,
    /// If true, only sets the variable when it is not already present in the context.
    /// Used for component prop defaults: `$: default name = value`
    pub is_default: bool,
}

/// An `include` directive — embeds another `.crepus` file as a component.
///
/// ```text
/// include components/button.crepus label="Click me" count={total}
///     div p-4
///         "slot content"
/// ```
#[derive(Debug, Clone)]
pub struct IncludeNode {
    /// Relative path to the included `.crepus` file.
    pub path: String,
    /// Props passed to the component: (key, expr_string) pairs.
    /// The expr_string is evaluated against the parent context.
    pub props: Vec<(String, String)>,
    /// Children of the include directive — become the component's slot content.
    pub slot: Vec<Node>,
}
