//! Dual-server endpoint latency benchmark (Rust Axum HTTP vs Node reference server).
//!
//! This test benchmarks response latency in milliseconds across a broad set of
//! feasible endpoints and generates a Markdown report with Mermaid charts.

#![cfg(feature = "axum")]

mod compat;

use std::collections::BTreeMap;
use std::fmt::Write as _;
use std::sync::Arc;
use std::time::{Duration, Instant};

use athena_auth::handlers::AxumIntegration;
use athena_auth::{
    Argon2Config, AuthBuilder, AuthConfig, MemoryDatabaseAdapter, RateLimitConfig,
    plugins::{
        AccountManagementPlugin, AdminPlugin, ApiKeyPlugin, EmailPasswordPlugin,
        EmailVerificationPlugin, OAuthPlugin, OrganizationPlugin, PasskeyPlugin,
        PasswordManagementPlugin, SessionManagementPlugin, TwoFactorPlugin, UserManagementPlugin,
    },
};
use axum::Router;
use reqwest::Method;
use serde::Serialize;
use serde_json::{Value, json};
use tokio::net::TcpListener;

use compat::benchmark_report::{
    BenchmarkArgon2ProfileArtifact, BenchmarkMetricsArtifact, BenchmarkRunArtifact,
    BenchmarkSampleArtifact, BenchmarkScenarioArtifact, BenchmarkSuiteArtifact, ReportPalette,
};
use compat::helpers::{test_secret, unique_email};
use compat::reference_node::{
    RefClient, SERIAL, SessionCookie, ensure_reference_server, reference_transport_origin,
};

const DEFAULT_ITERATIONS: usize = 12;
const DEFAULT_WARMUP_ITERATIONS: usize = 2;
const DEFAULT_SUITE_REPEATS: usize = 1;
const REPORT_PATH: &str = "compat-tests/reports/dual-server-latency-report.md";
const REPORT_JSON_PATH: &str = "compat-tests/reports/dual-server-latency-report.json";
const REPORT_HTML_PATH: &str = "compat-tests/reports/dual-server-latency-report.html";
const ATHENA_INDIGO_HEX: &str = "#3440D5";
const BETTER_AUTH_LIGHT_ORANGE_HEX: &str = "#FDBA74";
const DELTA_POSITIVE_HEX: &str = "#FDBA74";
const DELTA_NEGATIVE_HEX: &str = "#3440D5";
const NEUTRAL_HEX: &str = "#98A2B3";
const BACKGROUND_HEX: &str = "#F8FAFC";

#[derive(Debug, Clone)]
struct BenchmarkArgon2Profile {
    config: Argon2Config,
    override_active: bool,
}

impl BenchmarkArgon2Profile {
    fn source_label(&self) -> &'static str {
        if self.override_active {
            "ATHENA_BENCH_ARGON2_* override"
        } else {
            "Athena Auth default"
        }
    }
}

struct AxumJoinGuard(tokio::task::JoinHandle<std::result::Result<(), std::io::Error>>);

impl Drop for AxumJoinGuard {
    fn drop(&mut self) {
        self.0.abort();
    }
}

struct RustHttpClient {
    client: reqwest::Client,
    api_base: String,
    origin: String,
}

impl RustHttpClient {
    fn new(api_base: String, origin: String) -> Result<Self, String> {
        let client = reqwest::Client::builder()
            .redirect(reqwest::redirect::Policy::none())
            .timeout(Duration::from_secs(10))
            .build()
            .map_err(|e| format!("build rust benchmark client: {e}"))?;
        Ok(Self {
            client,
            api_base,
            origin,
        })
    }

    fn with_origin(&self, req: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
        req.header("origin", &self.origin)
    }

    async fn request_json(
        &self,
        method: Method,
        path: &str,
        body: Option<&Value>,
        bearer_token: Option<&str>,
    ) -> Result<(u16, Value), String> {
        let url = format!("{}{}", self.api_base, path);
        let mut req = self.with_origin(self.client.request(method, &url));
        if let Some(token) = bearer_token {
            req = req.header("authorization", format!("Bearer {token}"));
        }
        if let Some(body) = body {
            req = req.header("content-type", "application/json").json(body);
        }

        let resp = req
            .send()
            .await
            .map_err(|e| format!("Rust request {url} failed: {e}"))?;
        let status = resp.status().as_u16();
        let json: Value = resp
            .json()
            .await
            .unwrap_or_else(|_| Value::String("non-json".into()));
        Ok((status, json))
    }

    async fn get_json(&self, path: &str) -> Result<(u16, Value), String> {
        self.request_json(Method::GET, path, None, None).await
    }

    async fn get_bearer(&self, path: &str, token: &str) -> Result<(u16, Value), String> {
        self.request_json(Method::GET, path, None, Some(token))
            .await
    }

    async fn post_json(&self, path: &str, body: &Value) -> Result<(u16, Value), String> {
        self.request_json(Method::POST, path, Some(body), None)
            .await
    }

    async fn post_json_bearer(
        &self,
        path: &str,
        body: &Value,
        token: &str,
    ) -> Result<(u16, Value), String> {
        self.request_json(Method::POST, path, Some(body), Some(token))
            .await
    }
}

async fn create_benchmark_auth_with_base_url(
    base_url: &str,
    argon2_config: Argon2Config,
) -> Result<athena_auth::AthenaAuth<MemoryDatabaseAdapter>, String> {
    let config = AuthConfig::new(test_secret())
        .base_url(base_url.to_string())
        .password_min_length(8)
        .password_argon2_config(argon2_config)
        .trusted_origin(base_url.to_string());

    AuthBuilder::new(config)
        .rate_limit(RateLimitConfig::new().enabled(false))
        .database(MemoryDatabaseAdapter::new())
        .plugin(EmailPasswordPlugin::new().enable_signup(true))
        .plugin(SessionManagementPlugin::new())
        .plugin(PasswordManagementPlugin::new().require_current_password(true))
        .plugin(AccountManagementPlugin::new())
        .plugin(EmailVerificationPlugin::new())
        .plugin(ApiKeyPlugin::builder().build())
        .plugin(OAuthPlugin::new())
        .plugin(TwoFactorPlugin::new())
        .plugin(OrganizationPlugin::new())
        .plugin(
            PasskeyPlugin::new()
                .rp_id("localhost")
                .rp_name("Athena Auth Test")
                .origin(base_url.to_string())
                .allow_insecure_unverified_assertion(true),
        )
        .plugin(AdminPlugin::new())
        .plugin(
            UserManagementPlugin::new()
                .change_email_enabled(true)
                .delete_user_enabled(true),
        )
        .build()
        .await
        .map_err(|e| format!("build rust benchmark auth stack: {e}"))
}

fn benchmark_argon2_profile() -> Result<BenchmarkArgon2Profile, String> {
    let memory_cost = std::env::var("ATHENA_BENCH_ARGON2_MEMORY_COST")
        .ok()
        .map(|value| {
            value
                .parse::<u32>()
                .map_err(|error| format!("invalid ATHENA_BENCH_ARGON2_MEMORY_COST: {error}"))
        })
        .transpose()?;
    let time_cost = std::env::var("ATHENA_BENCH_ARGON2_TIME_COST")
        .ok()
        .map(|value| {
            value
                .parse::<u32>()
                .map_err(|error| format!("invalid ATHENA_BENCH_ARGON2_TIME_COST: {error}"))
        })
        .transpose()?;
    let parallelism = std::env::var("ATHENA_BENCH_ARGON2_PARALLELISM")
        .ok()
        .map(|value| {
            value
                .parse::<u32>()
                .map_err(|error| format!("invalid ATHENA_BENCH_ARGON2_PARALLELISM: {error}"))
        })
        .transpose()?;

    let mut argon2_config = Argon2Config::default();
    let override_active = memory_cost.is_some() || time_cost.is_some() || parallelism.is_some();
    if let Some(memory_cost) = memory_cost {
        argon2_config.memory_cost = memory_cost;
    }
    if let Some(time_cost) = time_cost {
        argon2_config.time_cost = time_cost;
    }
    if let Some(parallelism) = parallelism {
        argon2_config.parallelism = parallelism;
    }

    Ok(BenchmarkArgon2Profile {
        config: argon2_config,
        override_active,
    })
}

async fn setup_rust_axum_stack(
    argon2_config: Argon2Config,
) -> Result<(RustHttpClient, AxumJoinGuard), String> {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .map_err(|e| format!("bind rust benchmark listener: {e}"))?;
    let port = listener
        .local_addr()
        .map_err(|e| format!("resolve rust benchmark listener addr: {e}"))?
        .port();
    let origin = format!("http://127.0.0.1:{port}");
    let api_base = format!("{origin}/api/auth");

    let auth = Arc::new(create_benchmark_auth_with_base_url(&origin, argon2_config).await?);
    let auth_router = auth.clone().axum_router();
    let app = Router::new()
        .nest("/api/auth", auth_router)
        .with_state(auth);

    let join = tokio::spawn(async move { axum::serve(listener, app).await });
    tokio::time::sleep(Duration::from_millis(150)).await;

    let client = RustHttpClient::new(api_base, origin)?;
    Ok((client, AxumJoinGuard(join)))
}

#[derive(Debug, Clone, Copy)]
enum ScenarioKind {
    Ok,
    SignUpEmail,
    SignInEmail,
    GetSession,
    ListSessions,
    ListAccounts,
    UpdateUser,
    ChangePassword,
    RevokeSession,
    RevokeSessions,
    RevokeOtherSessions,
    SignOut,
    ChangeEmail,
    DeleteUser,
}

#[derive(Debug, Clone, Copy)]
struct Scenario {
    id: &'static str,
    label: &'static str,
    kind: ScenarioKind,
}

const SCENARIOS: &[Scenario] = &[
    Scenario {
        id: "E1",
        label: "GET /ok",
        kind: ScenarioKind::Ok,
    },
    Scenario {
        id: "E2",
        label: "POST /sign-up/email",
        kind: ScenarioKind::SignUpEmail,
    },
    Scenario {
        id: "E3",
        label: "POST /sign-in/email",
        kind: ScenarioKind::SignInEmail,
    },
    Scenario {
        id: "E4",
        label: "GET /get-session",
        kind: ScenarioKind::GetSession,
    },
    Scenario {
        id: "E5",
        label: "GET /list-sessions",
        kind: ScenarioKind::ListSessions,
    },
    Scenario {
        id: "E6",
        label: "GET /list-accounts",
        kind: ScenarioKind::ListAccounts,
    },
    Scenario {
        id: "E7",
        label: "POST /update-user",
        kind: ScenarioKind::UpdateUser,
    },
    Scenario {
        id: "E8",
        label: "POST /change-password",
        kind: ScenarioKind::ChangePassword,
    },
    Scenario {
        id: "E9",
        label: "POST /revoke-session",
        kind: ScenarioKind::RevokeSession,
    },
    Scenario {
        id: "E10",
        label: "POST /revoke-sessions",
        kind: ScenarioKind::RevokeSessions,
    },
    Scenario {
        id: "E11",
        label: "POST /revoke-other-sessions",
        kind: ScenarioKind::RevokeOtherSessions,
    },
    Scenario {
        id: "E12",
        label: "POST /sign-out",
        kind: ScenarioKind::SignOut,
    },
    Scenario {
        id: "E13",
        label: "POST /change-email",
        kind: ScenarioKind::ChangeEmail,
    },
    Scenario {
        id: "E14",
        label: "POST /delete-user",
        kind: ScenarioKind::DeleteUser,
    },
];

#[derive(Debug)]
struct AuthState {
    email: String,
    password: String,
    rust_primary_token: String,
    ref_primary_cookie: SessionCookie,
    rust_secondary_token: Option<String>,
    ref_secondary_token: Option<String>,
    ref_secondary_cookie: Option<SessionCookie>,
}

#[derive(Debug, Clone, Serialize)]
struct EndpointSample {
    rust_ms: f64,
    ref_ms: f64,
    rust_status: u16,
    ref_status: u16,
}

#[derive(Debug, Clone)]
struct EndpointMetrics {
    rust_avg_ms: f64,
    rust_p50_ms: f64,
    rust_p95_ms: f64,
    rust_min_ms: f64,
    rust_max_ms: f64,
    ref_avg_ms: f64,
    ref_p50_ms: f64,
    ref_p95_ms: f64,
    ref_min_ms: f64,
    ref_max_ms: f64,
    delta_avg_ms: f64,
    rust_status_counts: BTreeMap<u16, usize>,
    ref_status_counts: BTreeMap<u16, usize>,
}

#[derive(Debug)]
struct EndpointRunReport {
    run_index: usize,
    metrics: Option<EndpointMetrics>,
    samples: Vec<EndpointSample>,
    skip_reason: Option<String>,
}

#[derive(Debug)]
struct EndpointReport {
    scenario: Scenario,
    metrics: Option<EndpointMetrics>,
    samples: usize,
    completed_runs: usize,
    run_reports: Vec<EndpointRunReport>,
    skip_reasons: Vec<String>,
}

fn parse_iterations_from_env() -> usize {
    std::env::var("DUAL_SERVER_BENCH_ITERATIONS")
        .ok()
        .and_then(|v| v.parse::<usize>().ok())
        .filter(|v| *v > 0)
        .unwrap_or(DEFAULT_ITERATIONS)
}

fn parse_warmup_from_env() -> usize {
    std::env::var("DUAL_SERVER_BENCH_WARMUP")
        .ok()
        .and_then(|v| v.parse::<usize>().ok())
        .unwrap_or(DEFAULT_WARMUP_ITERATIONS)
}

fn parse_suite_repeats_from_env() -> usize {
    std::env::var("DUAL_SERVER_BENCH_REPEATS")
        .ok()
        .and_then(|v| v.parse::<usize>().ok())
        .filter(|v| *v > 0)
        .unwrap_or(DEFAULT_SUITE_REPEATS)
}

fn should_skip_status(status: u16) -> bool {
    matches!(status, 404 | 405 | 501)
}

fn extract_token(body: &Value, endpoint: &str) -> Result<String, String> {
    body["token"]
        .as_str()
        .map(|s| s.to_string())
        .ok_or_else(|| format!("{endpoint} response missing token: {body}"))
}

async fn setup_primary_auth_state(
    rust_http: &RustHttpClient,
    ref_client: &mut RefClient,
    prefix: &str,
) -> Result<AuthState, String> {
    let email = unique_email(prefix);
    let password = "password123".to_string();
    let signup_body = json!({
        "name": format!("Bench {prefix}"),
        "email": email,
        "password": password,
    });

    let (rust_status, rust_body) = rust_http.post_json("/sign-up/email", &signup_body).await?;
    if rust_status != 200 {
        return Err(format!(
            "Rust signup failed for {prefix}: status={rust_status}, body={rust_body}"
        ));
    }
    let _rust_signup_token = extract_token(&rust_body, "Rust /sign-up/email")?;

    let (ref_status, ref_body) = ref_client
        .post("/sign-up/email", &signup_body)
        .await
        .map_err(|e| format!("Reference signup failed for {prefix}: {e}"))?;
    if ref_status != 200 {
        return Err(format!(
            "Reference signup failed for {prefix}: status={ref_status}, body={ref_body}"
        ));
    }

    let signin_body = json!({
        "email": signup_body["email"].as_str().unwrap_or_default(),
        "password": signup_body["password"].as_str().unwrap_or_default(),
    });

    let (rust_signin_status, rust_signin_body) =
        rust_http.post_json("/sign-in/email", &signin_body).await?;
    if rust_signin_status != 200 {
        return Err(format!(
            "Rust sign-in failed after signup for {prefix}: status={rust_signin_status}, body={rust_signin_body}"
        ));
    }
    let rust_primary_token = extract_token(&rust_signin_body, "Rust /sign-in/email")?;

    let (ref_signin_status, ref_signin_body) = ref_client
        .post("/sign-in/email", &signin_body)
        .await
        .map_err(|e| format!("Reference sign-in failed for {prefix}: {e}"))?;
    if ref_signin_status != 200 {
        return Err(format!(
            "Reference sign-in failed after signup for {prefix}: status={ref_signin_status}, body={ref_signin_body}"
        ));
    }
    let _ref_primary_token = extract_token(&ref_signin_body, "Reference /sign-in/email")?;
    let ref_primary_cookie = ref_client.current_session_cookie().ok_or_else(|| {
        format!("Reference sign-in for {prefix} did not produce a session cookie")
    })?;

    Ok(AuthState {
        email: signin_body["email"]
            .as_str()
            .unwrap_or_default()
            .to_string(),
        password: signin_body["password"]
            .as_str()
            .unwrap_or_default()
            .to_string(),
        rust_primary_token,
        ref_primary_cookie,
        rust_secondary_token: None,
        ref_secondary_token: None,
        ref_secondary_cookie: None,
    })
}

async fn ensure_secondary_session(
    rust_http: &RustHttpClient,
    ref_client: &mut RefClient,
    state: &mut AuthState,
) -> Result<(), String> {
    if state.rust_secondary_token.is_some() && state.ref_secondary_token.is_some() {
        return Ok(());
    }

    let signin_body = json!({
        "email": state.email,
        "password": state.password,
    });

    let (rust_status, rust_body) = rust_http.post_json("/sign-in/email", &signin_body).await?;
    if rust_status != 200 {
        return Err(format!(
            "Rust secondary sign-in failed: status={rust_status}, body={rust_body}"
        ));
    }
    let rust_secondary_token = extract_token(&rust_body, "Rust /sign-in/email")?;

    let (ref_status, ref_body) = ref_client
        .post("/sign-in/email", &signin_body)
        .await
        .map_err(|e| format!("Reference secondary sign-in failed: {e}"))?;
    if ref_status != 200 {
        return Err(format!(
            "Reference secondary sign-in failed: status={ref_status}, body={ref_body}"
        ));
    }
    let ref_secondary_token = extract_token(&ref_body, "Reference /sign-in/email")?;
    let ref_secondary_cookie = ref_client.current_session_cookie().ok_or_else(|| {
        "Reference secondary sign-in did not produce a session cookie".to_string()
    })?;

    state.rust_secondary_token = Some(rust_secondary_token);
    state.ref_secondary_token = Some(ref_secondary_token);
    state.ref_secondary_cookie = Some(ref_secondary_cookie);
    Ok(())
}

fn elapsed_ms(start: Instant) -> f64 {
    start.elapsed().as_secs_f64() * 1_000.0
}

async fn invoke_rust_scenario(
    rust_http: &RustHttpClient,
    scenario: ScenarioKind,
    iteration: usize,
    auth: Option<&AuthState>,
) -> Result<(u16, Value), String> {
    match scenario {
        ScenarioKind::Ok => rust_http.get_json("/ok").await,
        ScenarioKind::SignUpEmail => {
            let body = json!({
                "name": format!("Rust SignUp {iteration}"),
                "email": unique_email("bench_signup_rust"),
                "password": "password123",
            });
            rust_http.post_json("/sign-up/email", &body).await
        }
        ScenarioKind::SignInEmail => {
            let auth = auth.ok_or_else(|| "missing auth state for sign-in scenario".to_string())?;
            let body = json!({
                "email": auth.email,
                "password": auth.password,
            });
            rust_http.post_json("/sign-in/email", &body).await
        }
        ScenarioKind::GetSession => {
            rust_http
                .get_bearer(
                    "/get-session",
                    &auth
                        .ok_or_else(|| "missing auth state for get-session".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::ListSessions => {
            rust_http
                .get_bearer(
                    "/list-sessions",
                    &auth
                        .ok_or_else(|| "missing auth state for list-sessions".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::ListAccounts => {
            rust_http
                .get_bearer(
                    "/list-accounts",
                    &auth
                        .ok_or_else(|| "missing auth state for list-accounts".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::UpdateUser => {
            let body = json!({
                "name": format!("Updated Rust User {iteration}"),
            });
            rust_http
                .post_json_bearer(
                    "/update-user",
                    &body,
                    &auth
                        .ok_or_else(|| "missing auth state for update-user".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::ChangePassword => {
            let new_password = format!("new_password_{iteration}_123");
            let auth = auth.ok_or_else(|| "missing auth state for change-password".to_string())?;
            let body = json!({
                "currentPassword": auth.password,
                "newPassword": new_password,
                "revokeOtherSessions": false,
            });
            rust_http
                .post_json_bearer("/change-password", &body, &auth.rust_primary_token)
                .await
        }
        ScenarioKind::RevokeSession => {
            let auth = auth.ok_or_else(|| "missing auth state for revoke-session".to_string())?;
            let secondary = auth.rust_secondary_token.as_ref().ok_or_else(|| {
                "missing secondary rust session token for revoke-session".to_string()
            })?;
            let body = json!({ "token": secondary });
            rust_http
                .post_json_bearer("/revoke-session", &body, &auth.rust_primary_token)
                .await
        }
        ScenarioKind::RevokeSessions => {
            rust_http
                .post_json_bearer(
                    "/revoke-sessions",
                    &json!({}),
                    &auth
                        .ok_or_else(|| "missing auth state for revoke-sessions".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::RevokeOtherSessions => {
            rust_http
                .post_json_bearer(
                    "/revoke-other-sessions",
                    &json!({}),
                    &auth
                        .ok_or_else(|| "missing auth state for revoke-other-sessions".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::SignOut => {
            rust_http
                .post_json_bearer(
                    "/sign-out",
                    &json!({}),
                    &auth
                        .ok_or_else(|| "missing auth state for sign-out".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::ChangeEmail => {
            let body = json!({
                "newEmail": unique_email("bench_change_email"),
            });
            rust_http
                .post_json_bearer(
                    "/change-email",
                    &body,
                    &auth
                        .ok_or_else(|| "missing auth state for change-email".to_string())?
                        .rust_primary_token,
                )
                .await
        }
        ScenarioKind::DeleteUser => {
            rust_http
                .post_json_bearer(
                    "/delete-user",
                    &json!({}),
                    &auth
                        .ok_or_else(|| "missing auth state for delete-user".to_string())?
                        .rust_primary_token,
                )
                .await
        }
    }
}

async fn invoke_ref_scenario(
    ref_client: &mut RefClient,
    scenario: ScenarioKind,
    iteration: usize,
    auth: Option<&AuthState>,
) -> Result<(u16, Value), String> {
    match scenario {
        ScenarioKind::Ok => ref_client.get("/ok").await,
        ScenarioKind::SignUpEmail => {
            let body = json!({
                "name": format!("Ref SignUp {iteration}"),
                "email": unique_email("bench_signup_ref"),
                "password": "password123",
            });
            ref_client.post("/sign-up/email", &body).await
        }
        ScenarioKind::SignInEmail => {
            let auth = auth.ok_or_else(|| "missing auth state for sign-in scenario".to_string())?;
            let body = json!({
                "email": auth.email,
                "password": auth.password,
            });
            ref_client.post("/sign-in/email", &body).await
        }
        ScenarioKind::GetSession => {
            let auth = auth.ok_or_else(|| "missing auth state for get-session".to_string())?;
            ref_client
                .get_with_cookie("/get-session", &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::ListSessions => {
            let auth = auth.ok_or_else(|| "missing auth state for list-sessions".to_string())?;
            ref_client
                .get_with_cookie("/list-sessions", &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::ListAccounts => {
            let auth = auth.ok_or_else(|| "missing auth state for list-accounts".to_string())?;
            ref_client
                .get_with_cookie("/list-accounts", &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::UpdateUser => {
            let body = json!({
                "name": format!("Updated Ref User {iteration}"),
            });
            let auth = auth.ok_or_else(|| "missing auth state for update-user".to_string())?;
            ref_client
                .post_with_cookie("/update-user", &body, &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::ChangePassword => {
            let new_password = format!("new_password_{iteration}_123");
            let auth = auth.ok_or_else(|| "missing auth state for change-password".to_string())?;
            let body = json!({
                "currentPassword": auth.password,
                "newPassword": new_password,
                "revokeOtherSessions": false,
            });
            ref_client
                .post_with_cookie("/change-password", &body, &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::RevokeSession => {
            let auth = auth.ok_or_else(|| "missing auth state for revoke-session".to_string())?;
            let secondary = auth.ref_secondary_token.as_ref().ok_or_else(|| {
                "missing secondary reference session token for revoke-session".to_string()
            })?;
            let body = json!({ "token": secondary });
            ref_client
                .post_with_cookie("/revoke-session", &body, &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::RevokeSessions => {
            let auth = auth.ok_or_else(|| "missing auth state for revoke-sessions".to_string())?;
            ref_client
                .post_with_cookie("/revoke-sessions", &json!({}), &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::RevokeOtherSessions => {
            let auth =
                auth.ok_or_else(|| "missing auth state for revoke-other-sessions".to_string())?;
            ref_client
                .post_with_cookie(
                    "/revoke-other-sessions",
                    &json!({}),
                    &auth.ref_primary_cookie,
                )
                .await
        }
        ScenarioKind::SignOut => {
            let auth = auth.ok_or_else(|| "missing auth state for sign-out".to_string())?;
            ref_client
                .post_with_cookie("/sign-out", &json!({}), &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::ChangeEmail => {
            let body = json!({
                "newEmail": unique_email("bench_change_email_ref"),
            });
            let auth = auth.ok_or_else(|| "missing auth state for change-email".to_string())?;
            ref_client
                .post_with_cookie("/change-email", &body, &auth.ref_primary_cookie)
                .await
        }
        ScenarioKind::DeleteUser => {
            let auth = auth.ok_or_else(|| "missing auth state for delete-user".to_string())?;
            ref_client
                .post_with_cookie("/delete-user", &json!({}), &auth.ref_primary_cookie)
                .await
        }
    }
}

async fn run_scenario_once(
    rust_http: &RustHttpClient,
    scenario: Scenario,
    iteration: usize,
) -> Result<EndpointSample, String> {
    let mut ref_client = RefClient::new();

    let needs_auth = !matches!(scenario.kind, ScenarioKind::Ok | ScenarioKind::SignUpEmail);
    let mut auth_state = if needs_auth {
        Some(
            setup_primary_auth_state(
                rust_http,
                &mut ref_client,
                &format!("{}_auth_{iteration}", scenario.id),
            )
            .await?,
        )
    } else {
        None
    };

    if matches!(
        scenario.kind,
        ScenarioKind::RevokeSession | ScenarioKind::RevokeOtherSessions
    ) && let Some(state) = auth_state.as_mut()
    {
        ensure_secondary_session(rust_http, &mut ref_client, state).await?;
    }

    let rust_first = iteration.is_multiple_of(2);
    let (rust_status, rust_body, rust_ms, ref_status, ref_body, ref_ms) = if rust_first {
        let rust_start = Instant::now();
        let (rust_status, rust_body) =
            invoke_rust_scenario(rust_http, scenario.kind, iteration, auth_state.as_ref()).await?;
        let rust_ms = elapsed_ms(rust_start);

        let ref_start = Instant::now();
        let (ref_status, ref_body) = invoke_ref_scenario(
            &mut ref_client,
            scenario.kind,
            iteration,
            auth_state.as_ref(),
        )
        .await?;
        let ref_ms = elapsed_ms(ref_start);
        (
            rust_status,
            rust_body,
            rust_ms,
            ref_status,
            ref_body,
            ref_ms,
        )
    } else {
        let ref_start = Instant::now();
        let (ref_status, ref_body) = invoke_ref_scenario(
            &mut ref_client,
            scenario.kind,
            iteration,
            auth_state.as_ref(),
        )
        .await?;
        let ref_ms = elapsed_ms(ref_start);

        let rust_start = Instant::now();
        let (rust_status, rust_body) =
            invoke_rust_scenario(rust_http, scenario.kind, iteration, auth_state.as_ref()).await?;
        let rust_ms = elapsed_ms(rust_start);
        (
            rust_status,
            rust_body,
            rust_ms,
            ref_status,
            ref_body,
            ref_ms,
        )
    };

    if rust_status >= 500 || ref_status >= 500 {
        return Err(format!(
            "{} produced 5xx: rust_status={} rust_body={} ref_status={} ref_body={}",
            scenario.label, rust_status, rust_body, ref_status, ref_body
        ));
    }

    Ok(EndpointSample {
        rust_ms,
        ref_ms,
        rust_status,
        ref_status,
    })
}

fn percentile(values: &[f64], p: f64) -> f64 {
    if values.is_empty() {
        return 0.0;
    }
    let mut sorted = values.to_vec();
    sorted.sort_by(f64::total_cmp);
    let rank = ((sorted.len() - 1) as f64 * p).round() as usize;
    sorted[rank]
}

fn compute_metrics(samples: &[EndpointSample]) -> EndpointMetrics {
    let rust_values: Vec<f64> = samples.iter().map(|s| s.rust_ms).collect();
    let ref_values: Vec<f64> = samples.iter().map(|s| s.ref_ms).collect();

    let rust_sum: f64 = rust_values.iter().sum();
    let ref_sum: f64 = ref_values.iter().sum();
    let n = samples.len() as f64;

    let rust_avg_ms = if n > 0.0 { rust_sum / n } else { 0.0 };
    let ref_avg_ms = if n > 0.0 { ref_sum / n } else { 0.0 };

    let mut rust_status_counts = BTreeMap::new();
    let mut ref_status_counts = BTreeMap::new();
    for s in samples {
        *rust_status_counts.entry(s.rust_status).or_insert(0) += 1;
        *ref_status_counts.entry(s.ref_status).or_insert(0) += 1;
    }

    EndpointMetrics {
        rust_avg_ms,
        rust_p50_ms: percentile(&rust_values, 0.50),
        rust_p95_ms: percentile(&rust_values, 0.95),
        rust_min_ms: rust_values.iter().cloned().fold(f64::INFINITY, f64::min),
        rust_max_ms: rust_values.iter().cloned().fold(0.0, f64::max),
        ref_avg_ms,
        ref_p50_ms: percentile(&ref_values, 0.50),
        ref_p95_ms: percentile(&ref_values, 0.95),
        ref_min_ms: ref_values.iter().cloned().fold(f64::INFINITY, f64::min),
        ref_max_ms: ref_values.iter().cloned().fold(0.0, f64::max),
        delta_avg_ms: rust_avg_ms - ref_avg_ms,
        rust_status_counts,
        ref_status_counts,
    }
}

fn report_palette() -> ReportPalette {
    ReportPalette {
        athena_hex: ATHENA_INDIGO_HEX.to_string(),
        better_hex: BETTER_AUTH_LIGHT_ORANGE_HEX.to_string(),
        delta_positive_hex: DELTA_POSITIVE_HEX.to_string(),
        delta_negative_hex: DELTA_NEGATIVE_HEX.to_string(),
        neutral_hex: NEUTRAL_HEX.to_string(),
        background_hex: BACKGROUND_HEX.to_string(),
    }
}

fn mermaid_compare_init_block() -> &'static str {
    "%%{init: {\"themeVariables\": {\"xyChart\": {\"plotColorPalette\": \"#3440D5, #FDBA74\"}}}}%%"
}

fn mermaid_delta_init_block() -> &'static str {
    "%%{init: {\"themeVariables\": {\"xyChart\": {\"plotColorPalette\": \"#3440D5\"}}}}%%"
}

fn metrics_to_artifact(metrics: &EndpointMetrics) -> BenchmarkMetricsArtifact {
    BenchmarkMetricsArtifact {
        rust_avg_ms: metrics.rust_avg_ms,
        rust_p50_ms: metrics.rust_p50_ms,
        rust_p95_ms: metrics.rust_p95_ms,
        rust_min_ms: metrics.rust_min_ms,
        rust_max_ms: metrics.rust_max_ms,
        reference_avg_ms: metrics.ref_avg_ms,
        reference_p50_ms: metrics.ref_p50_ms,
        reference_p95_ms: metrics.ref_p95_ms,
        reference_min_ms: metrics.ref_min_ms,
        reference_max_ms: metrics.ref_max_ms,
        delta_avg_ms: metrics.delta_avg_ms,
        rust_status_counts: metrics.rust_status_counts.clone(),
        reference_status_counts: metrics.ref_status_counts.clone(),
    }
}

fn build_suite_artifact(
    reports: &[EndpointReport],
    generated_at: &str,
    iterations: usize,
    warmup_iterations: usize,
    suite_repeats: usize,
    argon2_profile: &BenchmarkArgon2Profile,
) -> BenchmarkSuiteArtifact {
    let scenarios = reports
        .iter()
        .map(|report| BenchmarkScenarioArtifact {
            id: report.scenario.id.to_string(),
            label: report.scenario.label.to_string(),
            total_samples: report.samples,
            completed_runs: report.completed_runs,
            suite_repeats,
            aggregate_metrics: report.metrics.as_ref().map(metrics_to_artifact),
            skip_reasons: report.skip_reasons.clone(),
            runs: report
                .run_reports
                .iter()
                .map(|run| BenchmarkRunArtifact {
                    run_index: run.run_index,
                    samples: run.samples.len(),
                    metrics: run.metrics.as_ref().map(metrics_to_artifact),
                    skip_reason: run.skip_reason.clone(),
                    raw_samples: run
                        .samples
                        .iter()
                        .map(|sample| BenchmarkSampleArtifact {
                            rust_ms: sample.rust_ms,
                            reference_ms: sample.ref_ms,
                            rust_status: sample.rust_status,
                            reference_status: sample.ref_status,
                        })
                        .collect(),
                })
                .collect(),
        })
        .collect();

    BenchmarkSuiteArtifact {
        title: "Dual-Server Endpoint Latency Benchmark Report".to_string(),
        generated_at: generated_at.to_string(),
        iterations_per_run: iterations,
        warmup_iterations,
        suite_repeats,
        rust_server_label: "Athena Auth in-process Axum router (/api/auth/*)".to_string(),
        reference_server_label: "Better Auth compatibility server".to_string(),
        reference_origin: reference_transport_origin(),
        markdown_report_path: REPORT_PATH.to_string(),
        json_report_path: REPORT_JSON_PATH.to_string(),
        html_report_path: REPORT_HTML_PATH.to_string(),
        argon2_profile: BenchmarkArgon2ProfileArtifact {
            memory_cost: argon2_profile.config.memory_cost,
            time_cost: argon2_profile.config.time_cost,
            parallelism: argon2_profile.config.parallelism,
            source_label: argon2_profile.source_label().to_string(),
        },
        palette: report_palette(),
        scenarios,
    }
}

fn status_counts_to_string(counts: &BTreeMap<u16, usize>) -> String {
    counts
        .iter()
        .map(|(status, count)| format!("{status}:{count}"))
        .collect::<Vec<_>>()
        .join(", ")
}

fn chart_upper_bound(reports: &[EndpointReport]) -> f64 {
    let max_value = reports
        .iter()
        .filter_map(|r| r.metrics.as_ref())
        .flat_map(|m| [m.rust_p95_ms, m.ref_p95_ms, m.rust_max_ms, m.ref_max_ms])
        .fold(0.0, f64::max);
    if max_value <= 0.0 {
        10.0
    } else {
        (max_value * 1.2).ceil()
    }
}

fn delta_axis_range(reports: &[EndpointReport]) -> (f64, f64) {
    let min_delta = reports
        .iter()
        .filter_map(|r| r.metrics.as_ref())
        .map(|m| m.delta_avg_ms)
        .fold(0.0, f64::min)
        .floor();
    let max_delta = reports
        .iter()
        .filter_map(|r| r.metrics.as_ref())
        .map(|m| m.delta_avg_ms)
        .fold(0.0, f64::max)
        .ceil();
    if (max_delta - min_delta).abs() < f64::EPSILON {
        (min_delta.min(0.0), max_delta.max(1.0))
    } else {
        (min_delta, max_delta)
    }
}

fn render_report(
    reports: &[EndpointReport],
    generated_at: &str,
    iterations: usize,
    warmup_iterations: usize,
    suite_repeats: usize,
    argon2_profile: &BenchmarkArgon2Profile,
) -> Result<String, String> {
    let mut out = String::new();

    writeln!(out, "# Dual-Server Endpoint Latency Benchmark Report").unwrap();
    writeln!(out).unwrap();
    writeln!(out, "- Generated (UTC): `{generated_at}`").unwrap();
    writeln!(out, "- Iterations per endpoint: `{iterations}`").unwrap();
    writeln!(
        out,
        "- Warmup iterations per endpoint: `{warmup_iterations}`"
    )
    .unwrap();
    writeln!(out, "- Suite repeats per endpoint: `{suite_repeats}`").unwrap();
    writeln!(out, "- Rust server: in-process Axum router (`/api/auth/*`)").unwrap();
    writeln!(
        out,
        "- Athena Auth Argon2 profile: `memory_cost={}`, `time_cost={}`, `parallelism={}` ({})",
        argon2_profile.config.memory_cost,
        argon2_profile.config.time_cost,
        argon2_profile.config.parallelism,
        argon2_profile.source_label()
    )
    .unwrap();
    writeln!(
        out,
        "- Reference server: Node `better-auth` compatibility server (`{}`)",
        reference_transport_origin()
    )
    .unwrap();
    writeln!(out, "- Raw JSON artifact: `{REPORT_JSON_PATH}`").unwrap();
    writeln!(out, "- HTML dashboard target: `{REPORT_HTML_PATH}`").unwrap();
    writeln!(out).unwrap();

    writeln!(out, "## Endpoint Map").unwrap();
    writeln!(out).unwrap();
    for r in reports {
        writeln!(out, "- `{}` = {}", r.scenario.id, r.scenario.label).unwrap();
    }
    writeln!(out).unwrap();

    let feasible: Vec<&EndpointReport> = reports.iter().filter(|r| r.metrics.is_some()).collect();
    let infeasible: Vec<&EndpointReport> = reports.iter().filter(|r| r.metrics.is_none()).collect();

    writeln!(out, "## Summary").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "- Feasible endpoints benchmarked: `{}`",
        feasible.len()
    )
    .unwrap();
    writeln!(
        out,
        "- Endpoints with no successful runs: `{}`",
        infeasible.len()
    )
    .unwrap();
    writeln!(
        out,
        "- Successful endpoint runs: `{}`",
        reports.iter().map(|r| r.completed_runs).sum::<usize>()
    )
    .unwrap();
    writeln!(out).unwrap();

    let skipped_runs: Vec<(&EndpointReport, &String)> = reports
        .iter()
        .flat_map(|report| {
            report
                .skip_reasons
                .iter()
                .map(move |reason| (report, reason))
        })
        .collect();

    if !skipped_runs.is_empty() {
        writeln!(out, "### Skipped Runs").unwrap();
        writeln!(out).unwrap();
        for (report, reason) in skipped_runs {
            writeln!(
                out,
                "- `{}` {}: {}",
                report.scenario.id, report.scenario.label, reason
            )
            .unwrap();
        }
        writeln!(out).unwrap();
    }

    if !feasible.is_empty() {
        let y_max = chart_upper_bound(reports);
        let (delta_min, delta_max) = delta_axis_range(reports);
        let endpoint_ids = feasible
            .iter()
            .map(|r| format!("\"{}\"", r.scenario.id))
            .collect::<Vec<_>>()
            .join(", ");
        let rust_avg = feasible
            .iter()
            .map(|r| {
                format!(
                    "{:.3}",
                    r.metrics.as_ref().map(|m| m.rust_avg_ms).unwrap_or(0.0)
                )
            })
            .collect::<Vec<_>>()
            .join(", ");
        let ref_avg = feasible
            .iter()
            .map(|r| {
                format!(
                    "{:.3}",
                    r.metrics.as_ref().map(|m| m.ref_avg_ms).unwrap_or(0.0)
                )
            })
            .collect::<Vec<_>>()
            .join(", ");
        let delta_avg = feasible
            .iter()
            .map(|r| {
                format!(
                    "{:.3}",
                    r.metrics.as_ref().map(|m| m.delta_avg_ms).unwrap_or(0.0)
                )
            })
            .collect::<Vec<_>>()
            .join(", ");

        writeln!(out, "## Mermaid Charts").unwrap();
        writeln!(out).unwrap();
        writeln!(out, "### Average Latency by Endpoint").unwrap();
        writeln!(
            out,
            "Lower is better. Athena Auth bars are indigo; Better Auth bars are light orange."
        )
        .unwrap();
        writeln!(out).unwrap();
        writeln!(out, "```mermaid").unwrap();
        writeln!(out, "{}", mermaid_compare_init_block()).unwrap();
        writeln!(out, "xychart-beta").unwrap();
        writeln!(
            out,
            "    title \"Average Latency (ms): Athena Auth vs Better Auth (Lower is better)\""
        )
        .unwrap();
        writeln!(out, "    x-axis \"Endpoint\" [{endpoint_ids}]").unwrap();
        writeln!(out, "    y-axis \"Milliseconds\" 0 --> {y_max:.0}").unwrap();
        writeln!(out, "    bar \"Athena Auth\" [{rust_avg}]").unwrap();
        writeln!(out, "    bar \"Better Auth\" [{ref_avg}]").unwrap();
        writeln!(out, "```").unwrap();
        writeln!(out).unwrap();

        writeln!(out, "### Average Delta by Endpoint (Rust - Reference)").unwrap();
        writeln!(
            out,
            "Lower is better. Negative indigo bars mean Athena Auth is faster; values closer to zero are less favorable."
        )
        .unwrap();
        writeln!(out).unwrap();
        writeln!(out, "```mermaid").unwrap();
        writeln!(out, "{}", mermaid_delta_init_block()).unwrap();
        writeln!(out, "xychart-beta").unwrap();
        writeln!(
            out,
            "    title \"Average Delta (ms): Athena Auth minus Better Auth (Lower is better)\""
        )
        .unwrap();
        writeln!(out, "    x-axis \"Endpoint\" [{endpoint_ids}]").unwrap();
        writeln!(
            out,
            "    y-axis \"Milliseconds\" {delta_min:.0} --> {delta_max:.0}"
        )
        .unwrap();
        writeln!(out, "    bar \"Delta\" [{delta_avg}]").unwrap();
        writeln!(out, "```").unwrap();
        writeln!(out).unwrap();
    }

    writeln!(out, "## Detailed Results").unwrap();
    writeln!(out).unwrap();
    writeln!(
        out,
        "| ID | Endpoint | Runs OK/Total | Samples | Rust Avg ms | Ref Avg ms | Delta ms | Rust p95 ms | Ref p95 ms | Rust Statuses | Ref Statuses |"
    )
    .unwrap();
    writeln!(
        out,
        "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |"
    )
    .unwrap();

    for r in reports {
        if let Some(metrics) = &r.metrics {
            writeln!(
                out,
                "| {} | {} | {}/{} | {} | {:.3} | {:.3} | {:.3} | {:.3} | {:.3} | `{}` | `{}` |",
                r.scenario.id,
                r.scenario.label,
                r.completed_runs,
                suite_repeats,
                r.samples,
                metrics.rust_avg_ms,
                metrics.ref_avg_ms,
                metrics.delta_avg_ms,
                metrics.rust_p95_ms,
                metrics.ref_p95_ms,
                status_counts_to_string(&metrics.rust_status_counts),
                status_counts_to_string(&metrics.ref_status_counts),
            )
            .unwrap();
        } else {
            writeln!(
                out,
                "| {} | {} | 0/{} | 0 | - | - | - | - | - | - | - |",
                r.scenario.id, r.scenario.label, suite_repeats
            )
            .unwrap();
        }
    }
    writeln!(out).unwrap();

    for r in reports.iter().filter(|r| r.metrics.is_some()) {
        let metrics = r.metrics.as_ref().ok_or_else(|| {
            format!(
                "missing metrics while rendering details for {}",
                r.scenario.label
            )
        })?;
        let local_y_max = (metrics
            .rust_p95_ms
            .max(metrics.ref_p95_ms)
            .max(metrics.rust_max_ms)
            .max(metrics.ref_max_ms)
            * 1.2)
            .ceil()
            .max(10.0);
        writeln!(out, "### {} {}", r.scenario.id, r.scenario.label).unwrap();
        writeln!(out).unwrap();
        writeln!(
            out,
            "- Successful runs: `{}/{}`",
            r.completed_runs, suite_repeats
        )
        .unwrap();
        if !r.skip_reasons.is_empty() {
            writeln!(out, "- Skipped runs:").unwrap();
            for reason in &r.skip_reasons {
                writeln!(out, "  - `{reason}`").unwrap();
            }
        }
        writeln!(out, "- Rust: avg `{:.3}` ms, p50 `{:.3}` ms, p95 `{:.3}` ms, min `{:.3}` ms, max `{:.3}` ms", metrics.rust_avg_ms, metrics.rust_p50_ms, metrics.rust_p95_ms, metrics.rust_min_ms, metrics.rust_max_ms).unwrap();
        writeln!(out, "- Reference: avg `{:.3}` ms, p50 `{:.3}` ms, p95 `{:.3}` ms, min `{:.3}` ms, max `{:.3}` ms", metrics.ref_avg_ms, metrics.ref_p50_ms, metrics.ref_p95_ms, metrics.ref_min_ms, metrics.ref_max_ms).unwrap();
        writeln!(
            out,
            "- Delta (Rust - Reference): `{:.3}` ms",
            metrics.delta_avg_ms
        )
        .unwrap();
        writeln!(
            out,
            "- Lower is better. Athena Auth bars are indigo; Better Auth bars are light orange."
        )
        .unwrap();
        writeln!(out).unwrap();
        if r.run_reports.len() > 1 {
            writeln!(
                out,
                "| Run | Samples | Rust Avg ms | Ref Avg ms | Delta ms | Notes |"
            )
            .unwrap();
            writeln!(out, "| --- | ---: | ---: | ---: | ---: | --- |").unwrap();
            for run in &r.run_reports {
                match &run.metrics {
                    Some(run_metrics) => writeln!(
                        out,
                        "| {} | {} | {:.3} | {:.3} | {:.3} | ok |",
                        run.run_index + 1,
                        run.samples.len(),
                        run_metrics.rust_avg_ms,
                        run_metrics.ref_avg_ms,
                        run_metrics.delta_avg_ms
                    )
                    .unwrap(),
                    None => writeln!(
                        out,
                        "| {} | 0 | - | - | - | {} |",
                        run.run_index + 1,
                        run.skip_reason
                            .as_deref()
                            .unwrap_or("run skipped without a recorded reason")
                    )
                    .unwrap(),
                }
            }
            writeln!(out).unwrap();
        }
        writeln!(out, "```mermaid").unwrap();
        writeln!(out, "{}", mermaid_compare_init_block()).unwrap();
        writeln!(out, "xychart-beta").unwrap();
        writeln!(
            out,
            "    title \"{} {} latency profile (ms, Lower is better)\"",
            r.scenario.id, r.scenario.label
        )
        .unwrap();
        writeln!(out, "    x-axis \"Statistic\" [\"avg\", \"p50\", \"p95\"]").unwrap();
        writeln!(out, "    y-axis \"Milliseconds\" 0 --> {local_y_max:.0}").unwrap();
        writeln!(
            out,
            "    bar \"Athena Auth\" [{:.3}, {:.3}, {:.3}]",
            metrics.rust_avg_ms, metrics.rust_p50_ms, metrics.rust_p95_ms
        )
        .unwrap();
        writeln!(
            out,
            "    bar \"Better Auth\" [{:.3}, {:.3}, {:.3}]",
            metrics.ref_avg_ms, metrics.ref_p50_ms, metrics.ref_p95_ms
        )
        .unwrap();
        writeln!(out, "```").unwrap();
        writeln!(out).unwrap();
    }

    Ok(out)
}

#[tokio::test]
async fn dual_server_endpoint_latency_benchmark_report() {
    let _lock = SERIAL.lock().await;
    if !ensure_reference_server().await {
        eprintln!("[dual-bench] SKIPPED: reference server not available");
        return;
    }

    let iterations = parse_iterations_from_env();
    let warmup_iterations = parse_warmup_from_env();
    let suite_repeats = parse_suite_repeats_from_env();
    let argon2_profile = benchmark_argon2_profile().expect("resolve benchmark argon2 profile");
    let (rust_http, _guard) = setup_rust_axum_stack(argon2_profile.config.clone())
        .await
        .expect("setup rust axum stack for dual benchmark");
    let generated_at = chrono::Utc::now().to_rfc3339();

    let mut reports = Vec::new();

    for scenario in SCENARIOS {
        eprintln!("[dual-bench] benchmarking {} ...", scenario.label);
        let mut run_reports = Vec::new();

        for run_index in 0..suite_repeats {
            let mut samples = Vec::new();
            let mut skip_reason: Option<String> = None;

            for i in 0..(warmup_iterations + iterations) {
                let iteration_index = run_index * (warmup_iterations + iterations) + i;
                match run_scenario_once(&rust_http, *scenario, iteration_index).await {
                    Ok(sample) => {
                        if should_skip_status(sample.rust_status)
                            || should_skip_status(sample.ref_status)
                        {
                            skip_reason = Some(format!(
                                "run {} endpoint appears unavailable (Rust status {}, Ref status {})",
                                run_index + 1,
                                sample.rust_status,
                                sample.ref_status
                            ));
                            break;
                        }
                        if (sample.rust_status / 100) != (sample.ref_status / 100) {
                            skip_reason = Some(format!(
                                "run {} status class mismatch (Rust {}, Ref {})",
                                run_index + 1,
                                sample.rust_status,
                                sample.ref_status
                            ));
                            break;
                        }
                        if i >= warmup_iterations {
                            samples.push(sample);
                        }
                    }
                    Err(err) => {
                        skip_reason = Some(format!("run {} {err}", run_index + 1));
                        break;
                    }
                }
            }

            let metrics = if skip_reason.is_none() && !samples.is_empty() {
                Some(compute_metrics(&samples))
            } else {
                None
            };

            run_reports.push(EndpointRunReport {
                run_index,
                metrics,
                samples,
                skip_reason,
            });
        }

        let aggregate_samples: Vec<EndpointSample> = run_reports
            .iter()
            .flat_map(|run| run.samples.iter().cloned())
            .collect();
        let metrics = if aggregate_samples.is_empty() {
            None
        } else {
            Some(compute_metrics(&aggregate_samples))
        };
        reports.push(EndpointReport {
            scenario: *scenario,
            metrics,
            samples: aggregate_samples.len(),
            completed_runs: run_reports
                .iter()
                .filter(|run| run.metrics.is_some())
                .count(),
            skip_reasons: run_reports
                .iter()
                .filter_map(|run| run.skip_reason.clone())
                .collect(),
            run_reports,
        });
    }

    let report = render_report(
        &reports,
        &generated_at,
        iterations,
        warmup_iterations,
        suite_repeats,
        &argon2_profile,
    )
    .expect("render dual-server latency report");
    let report_artifact = build_suite_artifact(
        &reports,
        &generated_at,
        iterations,
        warmup_iterations,
        suite_repeats,
        &argon2_profile,
    );
    let report_dir = std::path::Path::new(REPORT_PATH)
        .parent()
        .expect("report path should have parent");
    std::fs::create_dir_all(report_dir).expect("create benchmark report directory");
    std::fs::write(REPORT_PATH, report).expect("write benchmark report file");
    std::fs::write(
        REPORT_JSON_PATH,
        serde_json::to_string_pretty(&report_artifact).expect("serialize benchmark report json"),
    )
    .expect("write benchmark report json file");

    eprintln!("[dual-bench] markdown report written to {REPORT_PATH}");
    eprintln!("[dual-bench] raw json written to {REPORT_JSON_PATH}");
}
