use std::fs;
use std::os::unix::fs::FileTypeExt;
use std::os::unix::net::UnixStream;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
use anyhow::anyhow;
use base::safe_descriptor_from_cmdline_fd;
use base::AsRawDescriptor;
use base::RawDescriptor;
use cros_async::Executor;
use futures::Future;
use futures::FutureExt;
use vmm_vhost::BackendServer;
use vmm_vhost::Connection;
use crate::virtio::vhost_user_backend::connection::VhostUserConnectionTrait;
use crate::virtio::vhost_user_backend::handler::sys::linux::run_handler;
pub struct VhostUserStream(UnixStream);
fn path_is_socket(path: &Path) -> bool {
    match fs::metadata(path) {
        Ok(metadata) => metadata.file_type().is_socket(),
        Err(_) => false, }
}
impl VhostUserStream {
    pub fn new_socket_from_fd(socket_fd: RawDescriptor) -> anyhow::Result<Self> {
        let path = PathBuf::from(format!("/proc/self/fd/{}", socket_fd));
        if !path_is_socket(&path) {
            return Err(anyhow!("fd {} is not a socket", socket_fd));
        }
        let safe_fd = safe_descriptor_from_cmdline_fd(&socket_fd)?;
        let stream = UnixStream::from(safe_fd);
        Ok(VhostUserStream(stream))
    }
}
impl VhostUserConnectionTrait for VhostUserStream {
    fn run_req_handler<'e>(
        self,
        handler: Box<dyn vmm_vhost::Backend>,
        ex: &'e Executor,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + 'e>> {
        async { stream_run_with_handler(self.0, handler, ex).await }.boxed_local()
    }
}
impl AsRawDescriptor for VhostUserStream {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.0.as_raw_descriptor()
    }
}
async fn stream_run_with_handler(
    stream: UnixStream,
    handler: Box<dyn vmm_vhost::Backend>,
    ex: &Executor,
) -> anyhow::Result<()> {
    let req_handler = BackendServer::new(Connection::try_from(stream)?, handler);
    run_handler(req_handler, ex).await
}