use crate::cloudhv::devices::*;
use crate::cloudhv::errors::CloudHypervisorError;
use std::path::PathBuf;

#[test]
fn test_device_builder_disk() {
    let device = DeviceBuilder::new("disk0")
        .disk("/disk.img", false)
        .build()
        .unwrap();

    assert_eq!(device.id, "disk0");
    match device.device_type {
        crate::cloudhv::config::DeviceType::Disk(disk) => {
            assert_eq!(disk.path, PathBuf::from("/disk.img"));
            assert!(!disk.readonly);
            assert!(disk.direct);
        }
        _ => panic!("Expected Disk device type"),
    }
}

#[test]
fn test_device_builder_net() {
    let device = DeviceBuilder::new("net0")
        .net("tap0")
        .build()
        .unwrap();

    assert_eq!(device.id, "net0");
    match device.device_type {
        crate::cloudhv::config::DeviceType::Net(net) => {
            assert_eq!(net.tap, Some("tap0".to_string()));
        }
        _ => panic!("Expected Net device type"),
    }
}

#[test]
fn test_device_builder_fs() {
    let device = DeviceBuilder::new("fs0")
        .fs("myfs", "/tmp/virtiofs.sock")
        .build()
        .unwrap();

    assert_eq!(device.id, "fs0");
    match device.device_type {
        crate::cloudhv::config::DeviceType::Fs(fs) => {
            assert_eq!(fs.tag, "myfs");
            assert_eq!(fs.socket, PathBuf::from("/tmp/virtiofs.sock"));
        }
        _ => panic!("Expected Fs device type"),
    }
}

#[test]
fn test_device_builder_vhost_user_net() {
    let device = DeviceBuilder::new("vhost-net0")
        .vhost_user_net("/tmp/vhost-net.sock")
        .build()
        .unwrap();

    assert_eq!(device.id, "vhost-net0");
    match device.device_type {
        crate::cloudhv::config::DeviceType::VhostUserNet(net) => {
            assert_eq!(net.socket, PathBuf::from("/tmp/vhost-net.sock"));
        }
        _ => panic!("Expected VhostUserNet device type"),
    }
}

#[test]
fn test_device_builder_vhost_user_blk() {
    let device = DeviceBuilder::new("vhost-blk0")
        .vhost_user_blk("/tmp/vhost-blk.sock", true)
        .build()
        .unwrap();

    assert_eq!(device.id, "vhost-blk0");
    match device.device_type {
        crate::cloudhv::config::DeviceType::VhostUserBlk(blk) => {
            assert_eq!(blk.socket, PathBuf::from("/tmp/vhost-blk.sock"));
            assert!(blk.readonly);
        }
        _ => panic!("Expected VhostUserBlk device type"),
    }
}

#[test]
fn test_device_builder_vfio() {
    let device = DeviceBuilder::new("vfio0")
        .vfio("0000:00:02.0")
        .build()
        .unwrap();

    assert_eq!(device.id, "vfio0");
    match device.device_type {
        crate::cloudhv::config::DeviceType::Vfio(vfio) => {
            assert_eq!(vfio.device, "0000:00:02.0");
        }
        _ => panic!("Expected Vfio device type"),
    }
}

#[test]
fn test_device_builder_no_type() {
    let result = DeviceBuilder::new("device0").build();

    assert!(result.is_err());
    match result {
        Err(CloudHypervisorError::Device(msg)) => {
            assert!(msg.contains("Device type not specified"));
        }
        _ => panic!("Expected Device error"),
    }
}

#[test]
fn test_disk_builder() {
    let disk = DiskBuilder::new()
        .path("/disk.img")
        .readonly(true)
        .direct(false)
        .build()
        .unwrap();

    assert_eq!(disk.path, PathBuf::from("/disk.img"));
    assert!(disk.readonly);
    assert!(!disk.direct);
    assert!(!disk.vhost_user);
}

#[test]
fn test_disk_builder_vhost_user() {
    let disk = DiskBuilder::new()
        .path("/disk.img")
        .vhost_user("/tmp/vhost.sock")
        .build()
        .unwrap();

    assert_eq!(disk.path, PathBuf::from("/disk.img"));
    assert!(disk.vhost_user);
    assert_eq!(disk.socket, Some(PathBuf::from("/tmp/vhost.sock")));
}

#[test]
fn test_disk_builder_no_path() {
    let result = DiskBuilder::new().readonly(true).build();

    assert!(result.is_err());
    match result {
        Err(CloudHypervisorError::Device(msg)) => {
            assert!(msg.contains("Disk path not specified"));
        }
        _ => panic!("Expected Device error"),
    }
}

#[test]
fn test_net_builder_tap() {
    let net = NetBuilder::new().tap("tap0").build().unwrap();

    assert_eq!(net.tap, Some("tap0".to_string()));
    assert!(!net.vhost_user);
}

#[test]
fn test_net_builder_with_mac() {
    let net = NetBuilder::new()
        .tap("tap0")
        .mac("00:11:22:33:44:55")
        .build()
        .unwrap();

    assert_eq!(net.tap, Some("tap0".to_string()));
    assert_eq!(net.mac, Some("00:11:22:33:44:55".to_string()));
}

#[test]
fn test_net_builder_vhost_user() {
    let net = NetBuilder::new()
        .vhost_user("/tmp/vhost-net.sock")
        .build()
        .unwrap();

    assert!(net.vhost_user);
    assert_eq!(net.socket, Some(PathBuf::from("/tmp/vhost-net.sock")));
}

#[test]
fn test_net_builder_no_tap_or_vhost() {
    let result = NetBuilder::new().build();

    assert!(result.is_err());
    match result {
        Err(CloudHypervisorError::Device(msg)) => {
            assert!(msg.contains("Either tap or vhost_user socket must be specified"));
        }
        _ => panic!("Expected Device error"),
    }
}

#[test]
fn test_disk_builder_default() {
    let builder = DiskBuilder::default();
    let result = builder.build();
    assert!(result.is_err());
}

#[test]
fn test_net_builder_default() {
    let builder = NetBuilder::default();
    let result = builder.build();
    assert!(result.is_err());
}
