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

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

    let mut passed = true;
    let mut detail = "validated helper archive + dereference flow".to_string();
    let mut metrics = json!({});

    let scenario = async {
        let content =
            "AssertionError: expected queryset ordering to preserve annotated alias.";
        let mut archive = ArchiveOptions::new(content, "failing_test_summary");
        archive.run_id = Some(run_id.clone());
        archive.agent_id = Some("solver".to_string());
        archive.source_attempt_id = Some("attempt-1".to_string());
        archive.source_tool = Some("pytest".to_string());
        archive.labels = vec!["django".to_string(), "query".to_string()];
        archive.family = Some("patch-repair".to_string());
        archive.metadata = Some(json!({ "source": "rust-helper-example" }));

        let archived = client.archive(archive).await?;
        let reference_id = archived
            .get("reference_id")
            .and_then(|value| value.as_str())
            .ok_or_else(|| boxed_error(format!("archive reference missing: {archived}")))?
            .to_string();

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

        require(
            archived
                .get("success")
                .and_then(|value| value.as_bool())
                .unwrap_or(false),
            format!("archive failed: {archived}"),
        )?;
        require(
            exact.get("found")
                .and_then(|value| value.as_bool())
                .unwrap_or(false),
            format!("dereference failed: {exact}"),
        )?;
        require(
            exact.get("evidence")
                .and_then(|value| value.get("content"))
                .and_then(|value| value.as_str())
                == Some(content),
            format!("dereferenced content mismatch: {exact}"),
        )?;

        metrics = json!({
            "run_id": run_id,
            "reference_id": reference_id,
            "entry_type": archived.get("entry_type").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))
    }
}
