use mount_fstab::spec::Spec;
use std::path::PathBuf;

#[test]
fn parse_device_path() {
    let spec = Spec::parse("/dev/sda1").unwrap();
    assert_eq!(spec, Spec::Device(PathBuf::from("/dev/sda1")));
}

#[test]
fn parse_device_by_uuid_path() {
    let spec = Spec::parse("/dev/disk/by-uuid/abc-123").unwrap();
    assert_eq!(
        spec,
        Spec::Device(PathBuf::from("/dev/disk/by-uuid/abc-123"))
    );
}

#[test]
fn parse_label() {
    let spec = Spec::parse("LABEL=Boot").unwrap();
    assert_eq!(spec, Spec::Label("Boot".to_owned()));
}

#[test]
fn parse_label_with_escape() {
    // parse() accepts decoded strings; value is stored as-is.
    let spec = Spec::parse(r"LABEL=My\040Drive").unwrap();
    assert_eq!(spec, Spec::Label(r"My\040Drive".to_owned()));
}

#[test]
fn parse_label_with_space() {
    // Already-decoded label passes through.
    let spec = Spec::parse("LABEL=My Drive").unwrap();
    assert_eq!(spec, Spec::Label("My Drive".to_owned()));
}

#[test]
fn parse_uuid() {
    let spec = Spec::parse("UUID=3e6be9de-8139-11d1-9106-a43f08d823a6").unwrap();
    assert_eq!(
        spec,
        Spec::Uuid("3e6be9de-8139-11d1-9106-a43f08d823a6".to_owned())
    );
}

#[test]
fn parse_uuid_fat_uppercase_preserved() {
    let spec = Spec::parse("UUID=A40D-85E7").unwrap();
    assert_eq!(spec, Spec::Uuid("A40D-85E7".to_owned()));
}

#[test]
fn parse_partlabel() {
    let spec = Spec::parse("PARTLABEL=System").unwrap();
    assert_eq!(spec, Spec::PartLabel("System".to_owned()));
}

#[test]
fn parse_partuuid() {
    let spec = Spec::parse("PARTUUID=d091fd20-162a-43e1-ad50-0e3ce36ab051").unwrap();
    assert_eq!(
        spec,
        Spec::PartUuid("d091fd20-162a-43e1-ad50-0e3ce36ab051".to_owned())
    );
}

#[test]
#[allow(deprecated)]
fn parse_id() {
    let spec = Spec::parse("ID=ata-SAMSUNG_SSD_1234").unwrap();
    assert_eq!(spec, Spec::Id("ata-SAMSUNG_SSD_1234".to_owned()));
}

#[test]
fn parse_nfs_mount() {
    let spec = Spec::parse("server.example.com:/exports/data").unwrap();
    assert_eq!(
        spec,
        Spec::NetworkMount {
            host: "server.example.com".to_owned(),
            path: PathBuf::from("/exports/data"),
        }
    );
}

#[test]
fn parse_nfs_mount_ip() {
    let spec = Spec::parse("192.168.1.1:/nfs/share").unwrap();
    assert_eq!(
        spec,
        Spec::NetworkMount {
            host: "192.168.1.1".to_owned(),
            path: PathBuf::from("/nfs/share"),
        }
    );
}

#[test]
fn parse_keyword_proc() {
    let spec = Spec::parse("proc").unwrap();
    assert_eq!(spec, Spec::Keyword("proc".to_owned()));
}

#[test]
fn parse_keyword_none() {
    let spec = Spec::parse("none").unwrap();
    assert_eq!(spec, Spec::Keyword("none".to_owned()));
}

#[test]
fn parse_keyword_tmpfs() {
    let spec = Spec::parse("tmpfs").unwrap();
    assert_eq!(spec, Spec::Keyword("tmpfs".to_owned()));
}

#[test]
fn parse_empty_is_error() {
    assert!(Spec::parse("").is_err());
}

#[test]
#[allow(deprecated)]
fn spec_is_tag() {
    assert!(Spec::Label("test".into()).is_tag());
    assert!(Spec::Uuid("test".into()).is_tag());
    assert!(Spec::PartLabel("test".into()).is_tag());
    assert!(Spec::PartUuid("test".into()).is_tag());
    assert!(Spec::Id("test".into()).is_tag());
    assert!(!Spec::Device(PathBuf::from("/dev/sda")).is_tag());
    assert!(!Spec::Keyword("proc".into()).is_tag());
}

#[test]
fn parse_nfs_with_ipv6_brackets() {
    // [::1]:/path — bracket after first char, find(:) finds the one after ]
    let spec = Spec::parse("[::1]:/nfs/share").unwrap();
    assert_eq!(
        spec,
        Spec::NetworkMount {
            host: "[::1]".into(),
            path: PathBuf::from("/nfs/share"),
        }
    );
}

#[test]
fn parse_escaped_label_with_backslash() {
    // parse() accepts decoded strings; escape sequences are not decoded.
    let spec = Spec::parse(r"LABEL=foo\\bar").unwrap();
    assert_eq!(spec, Spec::Label(r"foo\\bar".into()));
}

#[test]
fn parse_uuid_with_escaped_hyphen() {
    // parse() accepts decoded strings; escape sequences are not decoded.
    let spec = Spec::parse(r"UUID=abc\055def").unwrap();
    assert_eq!(spec, Spec::Uuid(r"abc\055def".into()));
}

#[test]
fn parse_uuid_decoded() {
    // Already-decoded UUID is stored as-is.
    let spec = Spec::parse("UUID=abc-def").unwrap();
    assert_eq!(spec, Spec::Uuid("abc-def".into()));
}

#[test]
fn parse_device_with_escaped_chars() {
    // parse() accepts decoded strings; escape sequences are not decoded.
    let spec = Spec::parse(r"/mnt/My\040Drive").unwrap();
    assert_eq!(spec, Spec::Device(PathBuf::from(r"/mnt/My\040Drive")));
}

#[test]
fn parse_device_decoded() {
    // Already-decoded device path is stored as-is.
    let spec = Spec::parse("/mnt/My Drive").unwrap();
    assert_eq!(spec, Spec::Device(PathBuf::from("/mnt/My Drive")));
}

#[test]
fn parse_keyword_with_leading_underscore() {
    let spec = Spec::parse("_netdev").unwrap();
    assert_eq!(spec, Spec::Keyword("_netdev".into()));
}

#[test]
fn parse_colon_in_device_path_not_network() {
    // /dev/disk/by-id/ata-XXX:YYY has a colon but starts with /
    let spec = Spec::parse("/dev/disk/by-id/ata-XXX:YYY").unwrap();
    assert_eq!(
        spec,
        Spec::Device(PathBuf::from("/dev/disk/by-id/ata-XXX:YYY"))
    );
}

#[test]
fn parse_label_with_encoded_colon() {
    // parse() accepts decoded strings; escape sequences are not decoded.
    let spec = Spec::parse(r"LABEL=root\072data").unwrap();
    assert_eq!(spec, Spec::Label(r"root\072data".into()));
}

#[test]
fn parse_tag_case_insensitivity() {
    // Tag prefixes are case-insensitive per mount(8).
    let spec = Spec::parse("label=Boot").unwrap();
    assert_eq!(spec, Spec::Label("Boot".into()));
}

#[test]
fn spec_is_pseudo() {
    assert!(Spec::Keyword("proc".into()).is_pseudo());
    assert!(Spec::Keyword("tmpfs".into()).is_pseudo());
    assert!(!Spec::Device(PathBuf::from("/dev/sda1")).is_pseudo());
    assert!(!Spec::Uuid("abc".into()).is_pseudo());
}
