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

use _utils::{boxed_error, cleanup_run, create_client, new_run_id, print_summary, require};
use mubit_sdk::{RecallOptions, 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_01_remember_recall";
    let started = Instant::now();
    let client = create_client().await?;
    let run_id = new_run_id("public_01_remember_recall");
    client.set_run_id(Some(run_id.clone()));
    client.set_transport(TransportMode::Http);

    let mut passed = true;
    let mut detail = "validated helper remember -> recall flow with upsert_key idempotency".to_string();
    let mut metrics = json!({});

    let scenario = async {
        let upsert_key = format!("remember-recall-{run_id}");
        let mut remember = RememberOptions::new(
            "Before retrying cache rebuilds, verify the token signer has rotated cleanly.",
        );
        remember.run_id = Some(run_id.clone());
        remember.agent_id = Some("helper-example".to_string());
        remember.intent = Some("lesson".to_string());
        remember.lesson_type = Some("success".to_string());
        remember.lesson_scope = Some("session".to_string());
        remember.upsert_key = Some(upsert_key.clone());
        remember.metadata = Some(json!({ "source": "rust-helper-example" }));

        let remembered = client.remember(remember).await?;
        require(
            remembered
                .get("done")
                .and_then(|value| value.as_bool())
                .unwrap_or(false),
            format!("remember should wait for ingest completion: {remembered}"),
        )?;

        let mut updated = RememberOptions::new(
            "Before retrying cache rebuilds, verify the signer rotation completed and invalidate stale cache tokens.",
        );
        updated.run_id = Some(run_id.clone());
        updated.agent_id = Some("helper-example".to_string());
        updated.intent = Some("lesson".to_string());
        updated.lesson_type = Some("success".to_string());
        updated.lesson_scope = Some("session".to_string());
        updated.upsert_key = Some(upsert_key.clone());
        updated.metadata = Some(json!({ "source": "rust-helper-example", "revision": 2 }));

        let updated_response = client.remember(updated).await?;
        require(
            updated_response
                .get("done")
                .and_then(|value| value.as_bool())
                .unwrap_or(false),
            format!("upsert remember should wait for ingest completion: {updated_response}"),
        )?;

        let mut recall = RecallOptions::new("What should I verify before retrying cache rebuilds?");
        recall.run_id = Some(run_id.clone());
        recall.entry_types = vec!["lesson".to_string(), "fact".to_string()];
        let answer = client.recall(recall).await?;
        let evidence = answer
            .get("evidence")
            .and_then(|value| value.as_array())
            .ok_or_else(|| boxed_error(format!("evidence missing: {answer}")))?;
        require(!evidence.is_empty(), format!("evidence missing: {answer}"))?;
        let rendered = evidence
            .iter()
            .filter_map(|item| item.get("content").and_then(|value| value.as_str()))
            .collect::<Vec<_>>()
            .join(" ");
        require(
            rendered.contains("invalidate stale cache tokens"),
            format!("upserted lesson should be recalled: {answer}"),
        )?;
        require(
            answer
                .get("final_answer")
                .and_then(|value| value.as_str())
                .is_some(),
            format!("final answer missing: {answer}"),
        )?;

        metrics = json!({
            "run_id": run_id,
            "evidence_count": evidence.len(),
            "confidence": answer.get("confidence").cloned().unwrap_or(serde_json::Value::Null),
            "upsert_key": upsert_key,
        });

        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))
    }
}
