//! v5.1 c6 — `selftest::multi::aggregate_results` 纯函数单元测。
//!
//! 5 case:
//! 1. 空 input → AllPass / 0 pass / 0 fail
//! 2. 单 sim 100% PASS → AllPass
//! 3. 双 sim,1 PASS 1 capability fail → SomeFail
//! 4. 三 sim:1 PASS / 1 result_missing / 1 capsule_unattributed
//! 5. summary serde round-trip — schema_version=1,字段稳

use std::fs;
use std::path::{Path, PathBuf};

use smix_sdk::selftest::multi::{MultiSimStatus, MultiSimSummary, PerSimInput, aggregate_results};

fn tmp_dir() -> PathBuf {
    let base = std::env::temp_dir().join(format!(
        "smix-c6-multi-{}-{}",
        std::process::id(),
        // 测试间不依赖时间戳:用计数器(原子)避免碰撞。
        next_seq()
    ));
    fs::create_dir_all(&base).unwrap();
    base
}

fn next_seq() -> u64 {
    use std::sync::atomic::{AtomicU64, Ordering};
    static C: AtomicU64 = AtomicU64::new(1);
    C.fetch_add(1, Ordering::SeqCst)
}

fn write_result(dir: &Path, name: &str, json_body: &str) -> PathBuf {
    let p = dir.join(name);
    fs::write(&p, json_body).unwrap();
    p
}

const PASSED_BODY: &str = r#"{
    "coverage_pct": 100.0,
    "capabilities": [
        {"name": "tap", "status": "passed"},
        {"name": "fill", "status": "passed"}
    ],
    "selector_spec_segments": [
        {"name": "spec1", "status": "passed"}
    ],
    "capsule_reconcile": {
        "issued_count": 4,
        "focus_change_count": 1,
        "attributed_count": 1,
        "unattributed_count": 0
    }
}"#;

const CAP_FAIL_BODY: &str = r#"{
    "coverage_pct": 95.0,
    "capabilities": [
        {"name": "tap", "status": "passed"},
        {"name": "focused", "status": "failed"}
    ],
    "selector_spec_segments": [
        {"name": "spec1", "status": "passed"}
    ],
    "capsule_reconcile": {
        "issued_count": 4,
        "focus_change_count": 1,
        "attributed_count": 1,
        "unattributed_count": 0
    }
}"#;

const CAPSULE_DIRTY_BODY: &str = r#"{
    "coverage_pct": 100.0,
    "capabilities": [
        {"name": "tap", "status": "passed"},
        {"name": "fill", "status": "passed"}
    ],
    "selector_spec_segments": [
        {"name": "spec1", "status": "passed"}
    ],
    "capsule_reconcile": {
        "issued_count": 4,
        "focus_change_count": 5,
        "attributed_count": 1,
        "unattributed_count": 4
    }
}"#;

#[test]
fn case01_empty_input_yields_all_pass_zero_zero() {
    let s = aggregate_results(&[]);
    assert_eq!(s.schema_version, 1);
    assert_eq!(s.worst_status, MultiSimStatus::AllPass);
    assert_eq!(s.pass_count, 0);
    assert_eq!(s.fail_count, 0);
    assert!(s.runs.is_empty());
}

#[test]
fn case02_single_passed_sim() {
    let dir = tmp_dir();
    let p = write_result(&dir, "result.json", PASSED_BODY);
    let inputs = [PerSimInput {
        udid: "AAAA",
        result_json_path: &p,
    }];
    let s = aggregate_results(&inputs);
    assert_eq!(s.worst_status, MultiSimStatus::AllPass);
    assert_eq!(s.pass_count, 1);
    assert_eq!(s.fail_count, 0);
    assert_eq!(s.runs.len(), 1);
    let row = &s.runs[0];
    assert_eq!(row.udid, "AAAA");
    assert_eq!(row.status, "passed");
    assert!((row.coverage_pct - 100.0).abs() < 1e-9);
    assert_eq!(row.capability_count, 2);
    assert_eq!(row.capsule_unattributed, 0);
}

#[test]
fn case03_two_sims_one_capability_fail() {
    let dir = tmp_dir();
    let pa = write_result(&dir, "a.json", PASSED_BODY);
    let pb = write_result(&dir, "b.json", CAP_FAIL_BODY);
    let inputs = [
        PerSimInput {
            udid: "AAAA",
            result_json_path: &pa,
        },
        PerSimInput {
            udid: "BBBB",
            result_json_path: &pb,
        },
    ];
    let s = aggregate_results(&inputs);
    assert_eq!(s.worst_status, MultiSimStatus::SomeFail);
    assert_eq!(s.pass_count, 1);
    assert_eq!(s.fail_count, 1);
    assert_eq!(s.runs.len(), 2);
    let row_a = s.runs.iter().find(|r| r.udid == "AAAA").unwrap();
    assert_eq!(row_a.status, "passed");
    let row_b = s.runs.iter().find(|r| r.udid == "BBBB").unwrap();
    assert_eq!(row_b.status, "failed_capability");
    assert_eq!(row_b.capability_count, 2);
}

#[test]
fn case04_three_sims_mixed_outcomes() {
    let dir = tmp_dir();
    let pa = write_result(&dir, "a.json", PASSED_BODY);
    // sim B 不写文件 — 模拟 selftest 没跑(scenario 早 panic / capsule down 没成等)
    let pb_missing = dir.join("b-missing.json");
    let pc = write_result(&dir, "c.json", CAPSULE_DIRTY_BODY);
    let inputs = [
        PerSimInput {
            udid: "AAAA",
            result_json_path: &pa,
        },
        PerSimInput {
            udid: "BBBB",
            result_json_path: &pb_missing,
        },
        PerSimInput {
            udid: "CCCC",
            result_json_path: &pc,
        },
    ];
    let s = aggregate_results(&inputs);
    assert_eq!(s.worst_status, MultiSimStatus::SomeFail);
    assert_eq!(s.pass_count, 1);
    assert_eq!(s.fail_count, 2);
    let by = |u: &str| s.runs.iter().find(|r| r.udid == u).unwrap();
    assert_eq!(by("AAAA").status, "passed");
    let b_row = by("BBBB");
    assert_eq!(b_row.status, "result_missing");
    assert_eq!(b_row.capability_count, 0);
    assert_eq!(b_row.capsule_unattributed, -1);
    let c_row = by("CCCC");
    assert_eq!(c_row.status, "capsule_unattributed");
    assert_eq!(c_row.capsule_unattributed, 4);
}

#[test]
fn case05_summary_serde_round_trip() {
    let summary = MultiSimSummary {
        schema_version: 1,
        worst_status: MultiSimStatus::SomeFail,
        pass_count: 2,
        fail_count: 1,
        runs: vec![smix_sdk::selftest::multi::PerSimRow {
            udid: "XYZ".to_string(),
            status: "passed".to_string(),
            coverage_pct: 100.0,
            capability_count: 36,
            capsule_unattributed: 0,
        }],
    };
    let s = serde_json::to_string(&summary).unwrap();
    assert!(s.contains("\"schema_version\":1"));
    assert!(s.contains("\"worst_status\":\"some_fail\""));
    assert!(s.contains("\"udid\":\"XYZ\""));
    let back: MultiSimSummary = serde_json::from_str(&s).unwrap();
    assert_eq!(back, summary);
}
