//! v5.1 c6 — `run_multi_selftest` 真跑入口烟测。
//!
//! - `bare_invocation_no_runner` — 非 ignored,不依赖 sim/runner/server。
//!   传 dummy UDID + 明显未占用的高端口,期望 run_multi_selftest **不**
//!   panic,**Ok** 返回(per-sim 任务连不上 runner 自然失败 →
//!   result_missing),summary 写出 `worst_status == some_fail`。
//!
//! - `e2e_two_sims` — `#[ignore]` 默认跳。需要 N 个 capsule up 好的 sim
//!   并设 env `SMIX_C6_TARGETS=<UDID1>:<PORT1>,<UDID2>:<PORT2>,...` 才跑。

use std::path::PathBuf;

use smix_sdk::selftest::multi::{MultiSimStatus, MultiSimTarget, run_multi_selftest};

fn tmp_runs_root(label: &str) -> PathBuf {
    let p = std::env::temp_dir().join(format!(
        "smix-c6-multi-e2e-{}-{}-{}",
        label,
        std::process::id(),
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.as_nanos())
            .unwrap_or(0)
    ));
    let _ = std::fs::create_dir_all(&p);
    p
}

#[tokio::test]
async fn bare_invocation_no_runner_returns_some_fail() {
    // 明显未占用的高端口段(privacy:都不在 smix default 22087-22099 范围里)。
    let targets = vec![
        MultiSimTarget {
            udid: "DEADBEEF-0000-0000-0000-000000000001".to_string(),
            runner_port: 59991,
        },
        MultiSimTarget {
            udid: "DEADBEEF-0000-0000-0000-000000000002".to_string(),
            runner_port: 59992,
        },
    ];
    let runs_root = tmp_runs_root("no-runner");
    let summary = run_multi_selftest(targets, runs_root.clone())
        .await
        .expect("run_multi_selftest should return Ok even when all sims fail");

    assert_eq!(summary.schema_version, 1);
    assert_eq!(summary.worst_status, MultiSimStatus::SomeFail);
    assert_eq!(summary.pass_count, 0);
    assert_eq!(summary.fail_count, 2);
    assert_eq!(summary.runs.len(), 2);
    for row in &summary.runs {
        assert_eq!(
            row.status, "result_missing",
            "no runner → write_result_json should not be called for {}",
            row.udid
        );
    }

    let summary_path = runs_root.join("summary.json");
    assert!(
        summary_path.is_file(),
        "summary.json must be written even on full fail at {summary_path:?}"
    );
}

#[tokio::test]
async fn empty_targets_returns_invalid_input() {
    let runs_root = tmp_runs_root("empty");
    let err = run_multi_selftest(vec![], runs_root)
        .await
        .expect_err("empty targets must Err");
    assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
}

#[tokio::test]
#[ignore = "needs N capsule-up'd sims + SMIX_C6_TARGETS=<UDID>:<PORT>,<UDID>:<PORT>,..."]
async fn e2e_two_sims() {
    let raw = std::env::var("SMIX_C6_TARGETS").expect("SMIX_C6_TARGETS env var");
    let targets: Vec<MultiSimTarget> = raw
        .split(',')
        .map(|s| {
            let (u, p) = s.split_once(':').expect("UDID:PORT");
            MultiSimTarget {
                udid: u.to_string(),
                runner_port: p.parse().expect("port u16"),
            }
        })
        .collect();
    assert!(targets.len() >= 2, "need ≥ 2 sims");

    let runs_root = tmp_runs_root("e2e");
    let summary = run_multi_selftest(targets.clone(), runs_root.clone())
        .await
        .expect("run_multi_selftest Ok");

    assert_eq!(summary.runs.len(), targets.len());
    for row in &summary.runs {
        eprintln!(
            "  e2e[{}] status={} coverage={} capsule_unattributed={}",
            row.udid, row.status, row.coverage_pct, row.capsule_unattributed
        );
    }
}
