use std::path::Path;

/// Disk space usage information.
pub struct DiskUsage {
    pub available_bytes: u64,
    pub total_bytes: u64,
}

impl DiskUsage {
    /// Returns disk usage as an integer percentage (0–100).
    pub fn used_pct(&self) -> u32 {
        if self.total_bytes == 0 {
            return 0;
        }
        ((self.total_bytes - self.available_bytes) * 100 / self.total_bytes) as u32
    }
}

/// Query available disk space using statvfs (Unix).
#[cfg(unix)]
pub fn disk_usage(path: &Path) -> std::io::Result<DiskUsage> {
    let c_path = std::ffi::CString::new(
        path.to_str()
            .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::InvalidInput))?,
    )
    .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidInput))?;

    let mut stat: libc::statvfs = unsafe { std::mem::zeroed() };
    if unsafe { libc::statvfs(c_path.as_ptr(), &mut stat) } != 0 {
        return Err(std::io::Error::last_os_error());
    }

    Ok(DiskUsage {
        available_bytes: stat.f_bavail as u64 * stat.f_frsize as u64,
        total_bytes: stat.f_blocks as u64 * stat.f_frsize as u64,
    })
}

/// Non-Unix stub — always reports unlimited space.
#[cfg(not(unix))]
pub fn disk_usage(_path: &Path) -> std::io::Result<DiskUsage> {
    Ok(DiskUsage {
        available_bytes: u64::MAX,
        total_bytes: u64::MAX,
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn disk_usage_returns_valid_data() {
        let usage = disk_usage(Path::new("/")).expect("statvfs should succeed on /");
        assert!(usage.total_bytes > 0, "total_bytes should be > 0");
        assert!(
            usage.available_bytes <= usage.total_bytes,
            "available should not exceed total"
        );
        let pct = usage.used_pct();
        assert!(pct <= 100, "used_pct should be <= 100");
    }

    #[test]
    fn disk_usage_pct_zero_total() {
        let usage = DiskUsage {
            available_bytes: 0,
            total_bytes: 0,
        };
        assert_eq!(usage.used_pct(), 0);
    }

    #[test]
    fn disk_usage_pct_half_used() {
        let usage = DiskUsage {
            available_bytes: 500,
            total_bytes: 1000,
        };
        assert_eq!(usage.used_pct(), 50);
    }
}
