//! Example: two `JsonlBackend`s side by side, demonstrating boot-from-config,
//! priority-ordered read routing, write fanout, and a health snapshot.
//!
//! Layout:
//!   - `primary`  → `read_priority = 0`, `failure_mode = required` (default)
//!   - `mirror`   → `read_priority = 10`, `failure_mode = best_effort`
//!
//! The primary is the default write target (lowest priority among
//! Required+write-active backends). `append_messages` fans out inline to the
//! mirror too. Reads walk the registrations in priority order, so the primary
//! answers first; if it is missing a session, the walk falls through to the
//! mirror.
//!
//! Run with:
//!
//!     cargo run --package dirigent_archivist --example multi_backend

use std::sync::Arc;

use chrono::Utc;
use dirigent_archivist::coordinator::Archivist;
use dirigent_archivist::registry::{ArchivesConfig, BackendRegistry};
use dirigent_archivist::types::{
    MessageRecord, RegisterConnectorRequest, RegisterSessionRequest,
};
use uuid::Uuid;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let dir_a = tempfile::tempdir()?;
    let dir_b = tempfile::tempdir()?;

    // Build a two-archive config entirely from TOML so the example doubles as
    // a faithful demonstration of the config surface.
    let cfg_src = format!(
        r#"
        [[archives]]
        name = "primary"
        type = "jsonl"
        read_priority = 0
        [archives.params]
        path = "{a}"

        [[archives]]
        name = "mirror"
        type = "jsonl"
        failure_mode = "best_effort"
        read_priority = 10
        [archives.params]
        path = "{b}"
        "#,
        a = dir_a.path().to_string_lossy().replace('\\', "/"),
        b = dir_b.path().to_string_lossy().replace('\\', "/"),
    );
    let cfg: ArchivesConfig = toml::from_str(&cfg_src)?;
    let registry = BackendRegistry::with_jsonl();
    let archivist = Arc::new(Archivist::from_config(cfg, &registry, None).await?);

    println!("\n=== Multi-backend Archivist example ===\n");
    println!("Boot complete. Archives (ordered by read_priority):");
    for s in archivist.list_archives_with_health().await {
        println!(
            "  - name={:<8} type={:<6} priority={:<3} enabled={} write_active={} failure_mode={:?} health={:?}",
            s.name,
            s.type_name,
            s.read_priority,
            s.enabled,
            s.write_active,
            s.failure_mode,
            s.health,
        );
    }

    // ------------------------------------------------------------------
    // Register a connector. The primary owns the canonical record; fanout
    // mirrors it to the secondary.
    // ------------------------------------------------------------------
    let connector_resp = archivist
        .register_connector(
            RegisterConnectorRequest {
                r#type: "Example".into(),
                title: "multi-backend demo".into(),
                client_native_id: "example://multi_backend".into(),
                custom_uid: None,
                metadata: serde_json::json!({ "demo": true }),
                fingerprint: None,
            },
            None,
        )
        .await?;
    let connector_uid = connector_resp.connector_uid;
    println!(
        "\nRegistered connector: uid={} status={:?}",
        connector_uid, connector_resp.status
    );

    // ------------------------------------------------------------------
    // Register a session under that connector. `register_session` writes
    // the mapping and `session.json` on the primary first, then fans out
    // to any enabled secondaries.
    // ------------------------------------------------------------------
    let session_resp = archivist
        .register_session(
            RegisterSessionRequest {
                connector_uid,
                native_session_id: "demo-session-1".into(),
                title: Some("multi-backend demo session".into()),
                custom_scroll_id: None,
                metadata: serde_json::json!({ "model": "demo" }),
                completeness: Default::default(),
                parent_scroll_id: None,
                is_subagent: false,
                continuation: None,
                agent_id: None,
                subagent_type: None,
                spawning_tool_use_id: None,
            },
            None,
        )
        .await?;
    let scroll_id = session_resp.scroll_id;
    println!(
        "Registered session:   scroll_id={} status={:?}",
        scroll_id, session_resp.status
    );

    // ------------------------------------------------------------------
    // Append a couple of messages. `append_messages` writes to the primary
    // and then fans out inline to the mirror.
    // ------------------------------------------------------------------
    let user_msg = MessageRecord {
        version: 1,
        message_id: Uuid::now_v7(),
        session: scroll_id,
        parent_id: None,
        ts: Utc::now(),
        role: "user".into(),
        author: Some("alice".into()),
        content_md: "Hello from the multi-backend example!".into(),
        content_parts: None,
        attachments: vec![],
        metadata: serde_json::json!({}),
    };
    let asst_msg = MessageRecord {
        version: 1,
        message_id: Uuid::now_v7(),
        session: scroll_id,
        parent_id: Some(user_msg.message_id),
        ts: Utc::now(),
        role: "assistant".into(),
        author: Some("claude".into()),
        content_md: "Greetings. I have been written to two archives.".into(),
        content_parts: None,
        attachments: vec![],
        metadata: serde_json::json!({}),
    };
    archivist
        .append_messages(scroll_id, vec![user_msg, asst_msg], None)
        .await?;
    println!("\nAppended 2 messages — fanned out to primary + mirror.");

    // ------------------------------------------------------------------
    // Read path: the priority walk tries the primary first (priority=0).
    // It finds the session there and never consults the mirror.
    // ------------------------------------------------------------------
    let meta = archivist.get_session_metadata(scroll_id, None).await?;
    println!(
        "\nRead session via priority walk: title={:?} completeness={:?}",
        meta.title, meta.completeness
    );
    println!(
        "Read cache size after read: {}",
        archivist.read_cache_size().await
    );

    let messages = archivist.get_messages(scroll_id, None).await?;
    println!("Read {} message(s) from the archive:", messages.len());
    for m in &messages {
        println!("  - [{}] {}", m.role, m.content_md);
    }

    // ------------------------------------------------------------------
    // Final health snapshot. Both backends should still be Available and
    // have no queued writes (both run Inline write policies by default).
    // ------------------------------------------------------------------
    println!("\nFinal health snapshot:");
    for s in archivist.list_archives_with_health().await {
        println!(
            "  - {:<8} health={:?} queue_depth={:?} last_error={:?}",
            s.name, s.health, s.queue_depth, s.last_error
        );
    }

    // Clean shutdown drains any queued writer tasks. Both backends here run
    // Inline, so this is effectively a no-op but remains the correct API.
    archivist.shutdown().await?;
    println!("\nShutdown complete.");
    Ok(())
}
