#[path = "../support/utils.rs"]
mod _utils;

use _utils::{boxed_error, cleanup_run, create_client, new_run_id, print_summary, require};
use mubit_sdk::{ArchiveOptions, CheckpointOptions, DereferenceOptions, FeedbackOptions, GetContextOptions, HandoffOptions, RecallOptions, RegisterAgentOptions, RememberOptions, TransportMode};
use serde_json::json;
use std::error::Error;
use std::time::Instant;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn Error>> {
    let name = "public_08_multi_agent_claims_or_support_flow";
    let started = Instant::now();
    let client = create_client().await?;
    let run_id = new_run_id("public_claims_flow");
    client.set_run_id(Some(run_id.clone()));
    client.set_transport(TransportMode::Http);

    let mut passed = true;
    let mut detail = "validated multi-agent claims/support flow".to_string();
    let mut metrics = json!({});

    let scenario = async {
        for (agent_id, role, read_scopes, write_scopes) in [
            ("intake", "intake", vec!["fact", "archive_block"], vec!["fact", "archive_block", "trace"]),
            ("coverage", "coverage", vec!["fact", "rule", "archive_block", "handoff", "feedback"], vec!["fact", "trace", "lesson"]),
            ("reviewer", "reviewer", vec!["fact", "archive_block", "handoff", "feedback", "rule"], vec!["feedback", "rule"]),
        ] {
            let mut agent = RegisterAgentOptions::new(agent_id);
            agent.run_id = Some(run_id.clone());
            agent.role = role.to_string();
            agent.read_scopes = read_scopes.into_iter().map(String::from).collect();
            agent.write_scopes = write_scopes.into_iter().map(String::from).collect();
            agent.shared_memory_lanes = vec!["knowledge".into(), "history".into()];
            client.register_agent(agent).await?;
        }

        let mut fact = RememberOptions::new("Policy P-8842 covers windshield damage with a 250 GBP deductible.");
        fact.run_id = Some(run_id.clone());
        fact.agent_id = Some("intake".into());
        fact.intent = Some("fact".into());
        fact.metadata = Some(json!({"claim_id": "CLM-8842", "source": "intake-form"}));
        client.remember(fact).await?;

        let mut archive = ArchiveOptions::new("Exact adjuster note: customer confirmed the damage happened on the motorway and no third party was involved.", "adjuster_note");
        archive.run_id = Some(run_id.clone());
        archive.agent_id = Some("intake".into());
        archive.origin_agent_id = Some("intake".into());
        archive.source_attempt_id = Some("attempt-1".into());
        archive.source_tool = Some("claim-review".into());
        archive.labels = vec!["claims".into(), "coverage".into()];
        archive.family = Some("windshield".into());
        let archived = client.archive(archive).await?;
        let reference_id = archived.get("reference_id").and_then(|v| v.as_str()).ok_or_else(|| boxed_error(format!("archive reference missing: {archived}")))?.to_string();

        let mut checkpoint = CheckpointOptions::new("Coverage verified deductible and adjuster note before escalation.");
        checkpoint.run_id = Some(run_id.clone());
        checkpoint.label = Some("pre-review".into());
        checkpoint.agent_id = Some("coverage".into());
        checkpoint.metadata = Some(json!({"claim_id": "CLM-8842"}));
        let checkpoint_response = client.checkpoint(checkpoint).await?;

        let mut handoff = HandoffOptions::new("claim-8842", "coverage", "reviewer", "Review whether the adjuster note is enough to approve the claim.");
        handoff.run_id = Some(run_id.clone());
        handoff.requested_action = "review".into();
        handoff.metadata = Some(json!({"claim_id": "CLM-8842"}));
        let handoff_response = client.handoff(handoff).await?;
        let handoff_id = handoff_response.get("handoff_id").and_then(|v| v.as_str()).ok_or_else(|| boxed_error(format!("handoff id missing: {handoff_response}")))?.to_string();

        let mut feedback = FeedbackOptions::new(handoff_id.clone(), "approve");
        feedback.run_id = Some(run_id.clone());
        feedback.from_agent_id = Some("reviewer".into());
        feedback.comments = "Deductible and adjuster note are sufficient for routing.".into();
        feedback.metadata = Some(json!({"claim_id": "CLM-8842"}));
        let feedback_response = client.feedback(feedback).await?;

        let mut context_opts = GetContextOptions::default();
        context_opts.run_id = Some(run_id.clone());
        context_opts.query = Some("Prepare the final claims routing summary.".into());
        context_opts.agent_id = Some("coverage".into());
        context_opts.sections = vec!["archive".into(), "facts".into(), "handoffs".into(), "feedback".into()];
        context_opts.mode = Some("sections".into());
        let context = client.get_context(context_opts).await?;

        let mut dereference = DereferenceOptions::new(reference_id.clone());
        dereference.run_id = Some(run_id.clone());
        dereference.agent_id = Some("coverage".into());
        let exact = client.dereference(dereference).await?;

        let mut recall = RecallOptions::new("What deductible applies and what did the adjuster confirm?");
        recall.run_id = Some(run_id.clone());
        recall.agent_id = Some("coverage".into());
        recall.entry_types = vec!["fact".into(), "archive_block".into(), "feedback".into()];
        let recall_response = client.recall(recall).await?;

        require(checkpoint_response.get("success").and_then(|v| v.as_bool()).unwrap_or(false), format!("checkpoint failed: {checkpoint_response}"))?;
        require(handoff_response.get("success").and_then(|v| v.as_bool()).unwrap_or(false), format!("handoff failed: {handoff_response}"))?;
        require(feedback_response.get("success").and_then(|v| v.as_bool()).unwrap_or(false), format!("feedback failed: {feedback_response}"))?;
        require(exact.get("found").and_then(|v| v.as_bool()).unwrap_or(false), format!("dereference failed: {exact}"))?;
        let context_text = context.get("context_block").and_then(|v| v.as_str()).unwrap_or("").to_lowercase();
        let recall_text = recall_response.get("final_answer").and_then(|v| v.as_str()).unwrap_or("").to_lowercase();
        let has_archive_source = context
            .get("sources")
            .and_then(|v| v.as_array())
            .map(|sources| {
                sources.iter().any(|source| {
                    source
                        .get("entry_type")
                        .and_then(|v| v.as_str())
                        == Some("archive_block")
                })
            })
            .unwrap_or(false);
        let exact_refs_surfaced = context
            .get("exact_references_surfaced")
            .and_then(|v| v.as_u64())
            .unwrap_or(0);
        require(
            context_text.contains("adjuster note")
                || context_text.contains("motorway")
                || has_archive_source
                || exact_refs_surfaced > 0,
            format!("expected explicit or telemetry-backed exact reuse in context: {context}"),
        )?;
        require(recall_text.contains("250") && (recall_text.contains("motorway") || recall_text.contains("third party")), format!("expected recall to include fact + archive content: {recall_response}"))?;

        metrics = json!({
            "run_id": run_id,
            "handoff_id": handoff_id,
            "reference_id": reference_id,
            "feedback_id": feedback_response.get("feedback_id").cloned().unwrap_or(serde_json::Value::Null),
        });
        Ok::<(), Box<dyn Error>>(())
    }
    .await;

    if let Err(err) = scenario {
        passed = false;
        detail = err.to_string();
    }

    let cleanup_ok = cleanup_run(&client, &run_id).await;
    if !cleanup_ok {
        passed = false;
        detail = format!("{detail} | cleanup failures");
    }

    print_summary(name, passed, &detail, &metrics, started.elapsed().as_secs_f64(), cleanup_ok);
    if passed { Ok(()) } else { Err(boxed_error(detail)) }
}
