#![cfg(windows)]
//! Memory and Performance Exploits Tests
//!
//! This suite tests for vulnerabilities related to memory exhaustion, denial of service (DoS),
//! and algorithmic complexity attacks in ADS parsing.

use soft_canonicalize::soft_canonicalize;
use std::fs;
use std::io::{self, ErrorKind};
use std::time::Instant;

fn expect_invalid(res: io::Result<impl std::fmt::Debug>, pattern: &str) {
    match res {
        Ok(v) => panic!("Expected InvalidInput for pattern '{pattern}', got Ok({v:?})"),
        Err(e) => assert_eq!(
            e.kind(),
            ErrorKind::InvalidInput,
            "Expected InvalidInput for pattern '{pattern}', got {e:?}"
        ),
    }
}

#[test]
fn test_ads_memory_exhaustion() -> io::Result<()> {
    let tmp = tempfile::tempdir()?;
    let base = tmp.path().join("file.txt");
    fs::write(base, b"test")?;

    // Test very long stream names to exhaust parsing buffers
    let long_stream = "a".repeat(1024 * 1024); // 1MB stream name
    let pattern = format!("file.txt:{long_stream}");
    let path = tmp.path().join(&pattern);
    expect_invalid(soft_canonicalize(path), &pattern);

    Ok(())
}

#[test]
fn test_nested_ads_dos() -> io::Result<()> {
    let tmp = tempfile::tempdir()?;
    let base = tmp.path().join("file.txt");
    fs::write(base, b"test")?;

    // Test nested ADS DoS
    let nested_ads = (0..100).map(|_| "a").collect::<Vec<_>>().join(":");
    let pattern = format!("file:{nested_ads}");
    let path = tmp.path().join(&pattern);
    expect_invalid(soft_canonicalize(path), &pattern);

    Ok(())
}

#[test]
fn test_algorithmic_complexity() -> io::Result<()> {
    let tmp = tempfile::tempdir()?;
    let base = tmp.path().join("file.txt");
    fs::write(base, b"test")?;

    // Test patterns that trigger worst-case parsing behavior
    // Alternating valid and invalid components
    let complex_pattern = (0..50)
        .map(|i| format!("stream{i}:..\\..\\evil.exe"))
        .collect::<Vec<_>>()
        .join(":");
    let pattern = format!("file.txt:{complex_pattern}");
    let path = tmp.path().join(&pattern);
    expect_invalid(soft_canonicalize(path), &pattern);

    Ok(())
}

#[test]
fn test_cache_poisoning() -> io::Result<()> {
    let tmp = tempfile::tempdir()?;
    let base = tmp.path().join("file.txt");
    fs::write(&base, b"test")?;

    // This test is difficult to implement reliably without specific knowledge of internal caches.
    // The `soft_canonicalize` function does not appear to have its own cache for non-existent paths.
    // We can, however, test for performance degradation by repeatedly calling the function with similar patterns.

    let mut patterns = Vec::new();
    for i in 0..100 {
        patterns.push(format!("file.txt:stream{i}:..\\..\\evil.exe"));
    }

    let start = Instant::now();
    for pattern in &patterns {
        let path = tmp.path().join(pattern);
        expect_invalid(soft_canonicalize(&path), pattern);
    }
    let duration = start.elapsed();
    println!("Duration for 100 calls: {duration:?}");

    // A very basic check to ensure the duration is within a reasonable limit.
    // This is not a perfect test for cache poisoning, but it can catch severe performance regressions.
    assert!(
        duration.as_millis() < 1000,
        "Possible performance degradation"
    );

    Ok(())
}
