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

use _utils::{boxed_error, cleanup_run, create_client, new_run_id, print_summary, require};
use mubit_sdk::{
    CheckpointOptions, ListAgentsOptions, RecordOutcomeOptions, RegisterAgentOptions,
    RememberOptions, SurfaceStrategiesOptions, 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_03_checkpoint_reflect_outcome";
    let started = Instant::now();
    let client = create_client().await?;
    let run_id = new_run_id("public_03_checkpoint_reflect_outcome");
    client.set_run_id(Some(run_id.clone()));
    client.set_transport(TransportMode::Http);

    let mut passed = true;
    let mut detail = "validated helper MAS checkpoint/outcome/strategies flow".to_string();
    let mut metrics = json!({});

    let scenario = async {
        let mut register = RegisterAgentOptions::new("planner");
        register.run_id = Some(run_id.clone());
        register.role = "planner".to_string();
        register.read_scopes = vec!["rule".to_string(), "lesson".to_string(), "fact".to_string()];
        register.write_scopes = vec!["lesson".to_string(), "trace".to_string()];
        register.shared_memory_lanes = vec!["knowledge".to_string(), "history".to_string()];
        client.register_agent(register).await?;

        let mut remember = RememberOptions::new(
            "If a retry succeeds after token signer rotation, record it as a reusable recovery pattern.",
        );
        remember.run_id = Some(run_id.clone());
        remember.agent_id = Some("planner".to_string());
        remember.intent = Some("lesson".to_string());
        remember.lesson_type = Some("success".to_string());
        remember.lesson_scope = Some("session".to_string());
        remember.lesson_importance = Some("high".to_string());
        client.remember(remember).await?;

        let mut checkpoint = CheckpointOptions::new(
            "Planner isolated the recovery path to token signer rotation.",
        );
        checkpoint.run_id = Some(run_id.clone());
        checkpoint.label = Some("pre-compaction-1".to_string());
        checkpoint.metadata = Some(json!({ "stage": "analysis" }));
        checkpoint.agent_id = Some("planner".to_string());
        let checkpoint_response = client.checkpoint(checkpoint).await?;

        let mut list_agents = ListAgentsOptions::default();
        list_agents.run_id = Some(run_id.clone());
        let agents = client.list_agents(list_agents).await?;

        let lessons = client.control.lessons(json!({ "run_id": run_id, "limit": 10 })).await?;
        let lesson_id = lessons
            .get("lessons")
            .and_then(|value| value.as_array())
            .and_then(|items| items.first())
            .and_then(|item| item.get("id"))
            .and_then(|value| value.as_str())
            .ok_or_else(|| boxed_error(format!("expected at least one lesson to record outcome against: {lessons}")))?
            .to_string();

        let mut outcome = RecordOutcomeOptions::new(lesson_id, "success");
        outcome.run_id = Some(run_id.clone());
        outcome.signal = 0.75;
        outcome.rationale = "Recovery succeeded after following the stored lesson.".to_string();
        outcome.agent_id = Some("planner".to_string());
        let outcome_response = client.record_outcome(outcome).await?;

        let mut strategies = SurfaceStrategiesOptions::default();
        strategies.run_id = Some(run_id.clone());
        strategies.lesson_types = vec!["success".to_string(), "failure".to_string()];
        strategies.max_strategies = 5;
        let strategy_response = client.surface_strategies(strategies).await?;

        require(
            checkpoint_response
                .get("success")
                .and_then(|value| value.as_bool())
                .unwrap_or(false),
            format!("checkpoint failed: {checkpoint_response}"),
        )?;
        let agent_count = agents
            .get("agents")
            .and_then(|value| value.as_array())
            .map(|items| items.len())
            .unwrap_or(0);
        require(agent_count == 1, format!("agent registration missing: {agents}"))?;
        require(
            outcome_response
                .get("success")
                .and_then(|value| value.as_bool())
                .unwrap_or(false),
            format!("record_outcome failed: {outcome_response}"),
        )?;
        let strategy_count = strategy_response
            .get("strategies")
            .and_then(|value| value.as_array())
            .map(|items| items.len())
            .unwrap_or(0);

        metrics = json!({
            "run_id": run_id,
            "checkpoint_id": checkpoint_response.get("checkpoint_id").cloned().unwrap_or(serde_json::Value::Null),
            "agent_count": agent_count,
            "strategy_count": strategy_count,
        });

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