Documentation
use std::time::Duration;
use anyhow::Result;
use async_trait::async_trait;
use sein::{AgentEngine, NodeTrait};

// Define application-specific node types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum AppNode {
    Thinking,
    GreetTool,
    Exit,
}

struct CounterContext {
    current: u32,
    max: u32,
}

struct ThinkingNode;

#[async_trait]
impl NodeTrait<CounterContext> for ThinkingNode {
    async fn enter(&self, ctx: &mut CounterContext) -> Result<()> {
        tokio::time::sleep(Duration::from_millis(500)).await;
        println!("Thinking... current count: {}/{}", ctx.current, ctx.max);
        Ok(())
    }
}

struct GreetToolNode;

#[async_trait]
impl NodeTrait<CounterContext> for GreetToolNode {
    async fn enter(&self, ctx: &mut CounterContext) -> Result<()> {
        tokio::time::sleep(Duration::from_secs(1)).await;
        ctx.current += 1;
        println!("Hello! This is greeting number {}", ctx.current);
        Ok(())
    }
}

struct ExitNode;

#[async_trait]
impl NodeTrait<CounterContext> for ExitNode {
    async fn enter(&self, _ctx: &mut CounterContext) -> Result<()> {
        println!("Done! Exiting...");
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut engine = AgentEngine::builder(CounterContext { current: 0, max: 3 })
        .with_node(AppNode::Thinking, ThinkingNode)
        .with_node(AppNode::GreetTool, GreetToolNode)
        .with_node(AppNode::Exit, ExitNode)
        .with_transition(AppNode::Thinking, AppNode::GreetTool, |ctx| ctx.current < ctx.max)?
        .with_transition(AppNode::Thinking, AppNode::Exit, |ctx| ctx.current >= ctx.max)?
        .with_transition(AppNode::GreetTool, AppNode::Thinking, |_| true)?
        .with_start_node(AppNode::Thinking)
        .build()?;

    engine.run().await?;

    Ok(())
}