//! Example demonstrating duroxide with observability enabled.
//!
//! This example shows how to configure structured logging and metrics
//! for production observability.
//!
//! Run with:
//! ```bash
//! cargo run --example with_observability --features observability
//! ```

use duroxide::providers::sqlite::SqliteProvider;
use duroxide::runtime::registry::ActivityRegistry;
use duroxide::runtime::{self, LogFormat, ObservabilityConfig, RuntimeOptions};
use duroxide::{ActivityContext, Client, OrchestrationContext, OrchestrationRegistry, OrchestrationStatus};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure observability with compact logging format
    let observability = ObservabilityConfig {
        metrics_enabled: false,        // Enable if you have an OTLP collector running
        metrics_export_endpoint: None, // Some("http://localhost:4317".to_string()),
        metrics_export_interval_ms: 10000,
        log_format: LogFormat::Compact,
        log_export_endpoint: None,
        log_level: "info".to_string(),
        service_name: "duroxide-example".to_string(),
        service_version: Some("1.0.0".to_string()),
    };

    let options = RuntimeOptions {
        observability,
        ..Default::default()
    };

    // Create provider
    let store = Arc::new(SqliteProvider::new_in_memory().await?);

    // Register activities
    let activities = ActivityRegistry::builder()
        .register("Greet", |ctx: ActivityContext, name: String| async move {
            ctx.trace_info("Greeting activity started");
            // Simulate some work
            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
            ctx.trace_info("Greeting activity complete");
            Ok(format!("Hello, {}!", name))
        })
        .register("Farewell", |ctx: ActivityContext, name: String| async move {
            ctx.trace_info("Farewell activity started");
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;
            ctx.trace_info("Farewell activity complete");
            Ok(format!("Goodbye, {}!", name))
        })
        .build();

    // Define orchestration
    let greeting_orch = |ctx: OrchestrationContext, name: String| async move {
        ctx.trace_info("Starting greeting orchestration");

        let greeting = ctx.schedule_activity("Greet", name.clone()).into_activity().await?;

        ctx.trace_info(format!("Got greeting: {}", greeting));

        let farewell = ctx.schedule_activity("Farewell", name).into_activity().await?;

        ctx.trace_info("Orchestration completing");
        Ok::<_, String>(format!("{} | {}", greeting, farewell))
    };

    let orchestrations = OrchestrationRegistry::builder()
        .register("GreetingWorkflow", greeting_orch)
        .build();

    // Start runtime with observability
    let rt = runtime::Runtime::start_with_options(store.clone(), Arc::new(activities), orchestrations, options).await;

    println!("Runtime started with observability enabled");
    println!("Watch the logs below with structured context fields:\n");

    // Create client and start orchestration
    let client = Client::new(store);

    client
        .start_orchestration("greeting-1", "GreetingWorkflow", "World")
        .await?;

    // Wait for completion
    match client
        .wait_for_orchestration("greeting-1", std::time::Duration::from_secs(5))
        .await
    {
        Ok(OrchestrationStatus::Completed { output }) => {
            println!("\n✅ Orchestration completed successfully!");
            println!("Output: {}", output);
        }
        Ok(OrchestrationStatus::Failed { details }) => {
            println!("\n❌ Orchestration failed: {}", details.display_message());
        }
        Ok(_) => {
            println!("\n⏳ Orchestration still running");
        }
        Err(e) => {
            println!("\n⚠️ Wait error: {:?}", e);
        }
    }

    // Shutdown gracefully
    rt.shutdown(None).await;

    println!("\nRuntime shut down");

    Ok(())
}
