// Copyright 2026 PingCAP, Inc.

use std::path::PathBuf;

use health_controller::space_usage::DiskUsageChecker;
use tikv_util::sys::disk;

#[test]
fn test_disk_usage_checker() {
    let kvdb_path = PathBuf::from("/tmp/tikv-kvdb");
    let raft_path = PathBuf::from("/tmp/tikv-raft");
    let raft_spill_path = PathBuf::from("/tmp/tikv-raft/spill");

    // Case 1: mock the kvdb and raft engine are not separated.
    fail::cfg("mock_disk_space_stats", "return(10000,5000)").unwrap();
    let disk_usage_checker = DiskUsageChecker::new(
        vec![kvdb_path.clone()],
        raft_path.clone(),
        Some(raft_spill_path.clone()),
        false,
        true,
        false,
        100,
        100,
        1000,
    );
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(disk_status, disk::DiskUsage::AlreadyFull);
    assert_eq!(kvdb_status, disk::DiskUsage::AlreadyFull);
    assert_eq!(raft_status, disk::DiskUsage::Normal);

    let disk_usage_checker = DiskUsageChecker::new(
        vec![kvdb_path.clone()],
        raft_path.clone(),
        Some(raft_spill_path.clone()),
        false,
        true,
        false,
        100,
        100,
        5100,
    );
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::AlmostFull);
    assert_eq!(disk_status, disk::DiskUsage::AlmostFull);
    fail::remove("mock_disk_space_stats");
    fail::cfg("mock_disk_space_stats", "return(10000,5001)").unwrap();
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::Normal);
    fail::remove("mock_disk_space_stats");

    // Case 1.1: mock the checker works with multiple kvdb paths.
    fail::cfg("mock_disk_space_stats", "return(8000,4000)").unwrap();
    let disk_usage_checker = DiskUsageChecker::new(
        vec![kvdb_path.clone(), PathBuf::from("/tmp/tikv-kvdb-2")],
        raft_path.clone(),
        Some(raft_spill_path.clone()),
        false,
        true,
        false,
        100,
        100,
        0,
    );
    let (disk_status, kvdb_status, raft_status, capacity, available) =
        disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::Normal);
    assert_eq!(capacity, 8000);
    assert_eq!(available, 4000);
    fail::remove("mock_disk_space_stats");

    // Case 2: mock the kvdb and raft engine are separated.
    fail::cfg(
            "mock_disk_space_stats",
            "1*return(500,50)->1*return(5000,2000)->1*return(500,100)->1*return(5000,2000)->1*return(500,101)->1*return(5000,2000)",
        )
        .unwrap();
    let disk_usage_checker = DiskUsageChecker::new(
        vec![kvdb_path.clone()],
        raft_path.clone(),
        Some(raft_spill_path.clone()),
        true,
        true,
        false,
        100,
        100,
        6000,
    );
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::AlreadyFull);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::AlreadyFull);
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::AlmostFull);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::AlmostFull);
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::Normal);
    fail::remove("mock_disk_space_stats");

    fail::cfg(
            "mock_disk_space_stats",
            "1*return(500,200)->1*return(5000,2000)->1*return(500,200)->1*return(5000,2000)->1*return(500,200)->1*return(5000,100)",
        )
        .unwrap();
    let disk_usage_checker = DiskUsageChecker::new(
        vec![kvdb_path.clone()],
        raft_path.clone(),
        Some(raft_spill_path.clone()),
        true,
        false,
        false,
        100,
        100,
        6000,
    );
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::Normal);
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::Normal);
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::AlmostFull);
    assert_eq!(disk_status, disk::DiskUsage::AlmostFull);
    fail::remove("mock_disk_space_stats");

    // Case 3: mock the kvdb and raft engine are separated and the auxiliary
    // directory of raft engine is separated from the main directory of
    // raft.
    fail::cfg(
        "mock_disk_space_stats",
        "1*return(500,200)->1*return(100,20)->1*return(5000,2000)",
    )
    .unwrap();
    let disk_usage_checker = DiskUsageChecker::new(
        vec![kvdb_path.clone()],
        raft_path.clone(),
        Some(raft_spill_path.clone()),
        true,
        true,
        true,
        100,
        100,
        6000,
    );
    let (disk_status, kvdb_status, raft_status, ..) = disk_usage_checker.inspect().unwrap();
    assert_eq!(raft_status, disk::DiskUsage::Normal);
    assert_eq!(kvdb_status, disk::DiskUsage::Normal);
    assert_eq!(disk_status, disk::DiskUsage::Normal);
    fail::remove("mock_disk_space_stats");
}

#[test]
fn test_disk_usage_checker_returns_none_when_stat_failed() {
    fail::remove("mock_disk_space_stats");
    let missing_kv_path = PathBuf::from(format!(
        "/tmp/tikv-disk-usage-checker-missing-kv-path-for-test-{}/no-such-dir",
        std::process::id()
    ));

    let disk_usage_checker = DiskUsageChecker::new(
        vec![missing_kv_path],
        PathBuf::from("/tmp/tikv-raft"),
        Some(PathBuf::from("/tmp/tikv-raft/spill")),
        false,
        true,
        false,
        100,
        100,
        0,
    );

    assert!(disk_usage_checker.inspect().is_none());
}
