use std::path::{Path, PathBuf};
use super::{MountPoint, is_fuse, mounts_under, parse_mountinfo_line, unescape_mountinfo};

#[test]
fn unescape_plain_path_is_unchanged() {
    assert_eq!(unescape_mountinfo("/mnt/data"), "/mnt/data");
}

#[test]
fn unescape_space_octal() {
    assert_eq!(unescape_mountinfo("/mnt/my\\040dir"), "/mnt/my dir");
}

#[test]
fn unescape_multiple_sequences() {
    assert_eq!(unescape_mountinfo("a\\012b\\134c"), "a\nb\\c");
}

#[test]
fn unescape_incomplete_sequence_kept_verbatim() {
    assert_eq!(unescape_mountinfo("a\\04b"), "a\\04b");
}

#[test]
fn unescape_backslash_at_end_kept_verbatim() {
    assert_eq!(unescape_mountinfo("a\\"), "a\\");
}

fn make_mountinfo_line(mountpoint: &str, fstype: &str, source: &str) -> String {
    format!("42 1 8:1 / {mountpoint} rw,relatime - {fstype} {source} rw")
}

#[test]
fn parse_basic_ext4_line() {
    let line = make_mountinfo_line("/", "ext4", "/dev/sda1");
    let mp = parse_mountinfo_line(&line).expect("should parse");
    assert_eq!(mp.path, PathBuf::from("/"));
    assert_eq!(mp.fstype, "ext4");
}

#[test]
fn parse_fuse_sshfs_line() {
    let line = make_mountinfo_line("/mnt/remote", "fuse.sshfs", "user@host:/srv");
    let mp = parse_mountinfo_line(&line).expect("should parse");
    assert_eq!(mp.path, PathBuf::from("/mnt/remote"));
    assert_eq!(mp.fstype, "fuse.sshfs");
}

#[test]
fn parse_line_with_optional_fields() {
    let line = "42 1 8:1 / /mnt/x rw peer:123 shared:45 - tmpfs tmpfs rw";
    let mp = parse_mountinfo_line(line).expect("should parse with optional fields");
    assert_eq!(mp.path, PathBuf::from("/mnt/x"));
    assert_eq!(mp.fstype, "tmpfs");
}

#[test]
fn parse_mountpoint_with_encoded_space() {
    let line = make_mountinfo_line("/mnt/my\\040volume", "ext4", "/dev/sdb1");
    let mp = parse_mountinfo_line(&line).expect("should parse");
    assert_eq!(mp.path, PathBuf::from("/mnt/my volume"));
}

#[test]
fn parse_returns_none_for_malformed_line() {
    assert!(parse_mountinfo_line("not a valid mountinfo line").is_none());
    assert!(parse_mountinfo_line("").is_none());
}

#[test]
fn is_fuse_recognises_bare_fuse() {
    assert!(is_fuse("fuse"));
}

#[test]
fn is_fuse_recognises_fuseblk() {
    assert!(is_fuse("fuseblk"));
}

#[test]
fn is_fuse_recognises_prefixed_variants() {
    assert!(is_fuse("fuse.sshfs"));
    assert!(is_fuse("fuse.rclone"));
    assert!(is_fuse("fuse.gvfsd-fuse"));
}

#[test]
fn is_fuse_rejects_non_fuse() {
    assert!(!is_fuse("ext4"));
    assert!(!is_fuse("tmpfs"));
    assert!(!is_fuse("btrfs"));
    assert!(!is_fuse("nfs"));
}

#[test]
fn mounts_under_nonexistent_path_returns_empty() {
    let result = mounts_under(Path::new("/this/path/does/not/exist/obliterate-test"));
    assert!(result.is_ok());
    assert!(result.unwrap().is_empty());
}

#[test]
fn mounts_under_returns_deepest_first() {
    let mut mounts = vec![
        MountPoint { path: PathBuf::from("/a"), fstype: "ext4".into()},
        MountPoint { path: PathBuf::from("/a/b/c"), fstype: "tmpfs".into()},
        MountPoint { path: PathBuf::from("/a/b"), fstype: "ext4".into()},
    ];

    mounts.sort_by(|a, b| b.path.as_os_str().len().cmp(&a.path.as_os_str().len()));

    assert_eq!(mounts[0].path, PathBuf::from("/a/b/c"));
    assert_eq!(mounts[1].path, PathBuf::from("/a/b"));
    assert_eq!(mounts[2].path, PathBuf::from("/a"));
}
