use mount_fstab::*;

/// Tests verifying behavior matches libmount reference implementation

#[test]
fn defaults_option_is_single_item() {
    // libmount stores "defaults" as-is without expanding
    let opts = Options::parse("defaults").unwrap();
    assert_eq!(opts.iter().count(), 1);
    assert!(opts.has("defaults"));
}

#[test]
fn last_option_wins_per_spec() {
    // mount(8): "the last option wins if there are conflicting ones"
    assert!(Options::parse("rw,ro").unwrap().is_readonly());
    assert!(!Options::parse("ro,rw").unwrap().is_readonly());
    assert!(Options::parse("auto,noauto").unwrap().is_noauto());
    assert!(!Options::parse("noauto,auto").unwrap().is_noauto());
}

#[test]
fn fstab_no_prefix_has_no_effect() {
    // mount(8): "The prefix no has no effect when specified in an /etc/fstab entry"
    // This means "noext4" in fstab fstype doesn't negate — but we just parse it
    let ft = FsType::parse("noext4").unwrap();
    assert_eq!(ft.as_str(), "noext4");
}

#[test]
fn subtype_notation_detected() {
    // mount(8): "It's recommended to use subtype notation rather than add any prefix"
    let ft = FsType::parse("fuse.sshfs").unwrap();
    assert_eq!(ft.primary(), "fuse");
}

#[test]
fn swap_vfstype_recognized() {
    let ft = FsType::parse("swap").unwrap();
    assert!(ft.is_swap());
}

#[test]
fn bind_mount_vfstype_is_none() {
    let ft = FsType::parse("none").unwrap();
    assert!(ft.is_bind());
}

#[test]
fn comment_option_preserved() {
    // fstab(5): "comment or x-<name> for use by fstab-maintaining programs"
    let opts = Options::parse("defaults,comment=systemd.automount").unwrap();
    assert!(opts.has("comment"));
    assert_eq!(opts.get("comment"), Some("systemd.automount"));
}

#[test]
fn nofail_option_recognized() {
    assert!(Options::parse("nofail").unwrap().has_nofail());
}

#[test]
fn netdev_option_recognized() {
    assert!(Options::parse("_netdev").unwrap().is_netdev());
}

#[test]
fn freq_default_zero() {
    let input = "proc /proc proc defaults\n";
    let fstab = Fstab::parse_str(input).unwrap();
    assert_eq!(fstab.entries[0].freq, 0);
}

#[test]
fn passno_default_zero() {
    let input = "proc /proc proc defaults\n";
    let fstab = Fstab::parse_str(input).unwrap();
    assert_eq!(fstab.entries[0].passno, 0);
}

#[test]
fn root_passno_should_be_one() {
    // fstab(5): "The root filesystem should be specified with a fs_passno of 1"
    let diags = Fstab {
        entries: vec![Entry::new(
            Spec::Uuid("root".into()),
            MountPoint::new("/").unwrap(),
            FsType::parse("ext4").unwrap(),
            Options::parse("defaults").unwrap(),
        )],
        ..Default::default()
    }
    .validate();
    assert!(diags.iter().any(|d| d.severity == Severity::Error));
}

#[test]
fn other_passno_should_be_two() {
    // fstab(5): "Other filesystems should have a fs_passno of 2"
    // passno=0 means don't check, passno=2 means check after root
    // This is a convention, not enforced as error
    let _fstab = Fstab {
        entries: vec![Entry::new(
            Spec::Uuid("root".into()),
            MountPoint::new("/").unwrap(),
            FsType::parse("ext4").unwrap(),
            Options::parse("defaults").unwrap(),
        )],
        ..Default::default()
    };
    // passno=0 for root would produce error, but passno=2 for non-root is fine
}

#[test]
fn escape_sequences_per_getmntent_spec() {
    // getmntent(3): "octal escapes are used to represent space (\040), tab (\011),
    // newline (\012), and backslash (\\)"
    assert_eq!(escape::decode_escapes(r"\040"), " ");
    assert_eq!(escape::decode_escapes(r"\011"), "\t");
    assert_eq!(escape::decode_escapes(r"\012"), "\n");
    assert_eq!(escape::decode_escapes(r"\\"), "\\");
    assert_eq!(escape::decode_escapes(r"\134"), "\\");
}
