Documentation
use alloc::format;
use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::future::Future;

use crate::linux::io_uring::{
    AT_FDCWD, AT_REMOVEDIR, AT_STATX_SYNC_AS_STAT, IoUring, S_IRUSR, S_IWUSR, S_IXUSR,
    STATX_BASIC_STATS, Statx,
};
use crate::linux::sys::Errno;
use crate::{fs, linux};

pub mod file;

pub struct FileSystem;

impl fs::FileSystem<linux::runtime::Runtime, linux::runtime::Share> for FileSystem {
    type File = file::File;

    fn read_dir(
        _path: &str,
    ) -> impl Future<Output = fs::Result<fs::ReadDir<linux::runtime::Runtime, linux::runtime::Share>>>
    {
        async move {
            // For now, return an error - readdir requires getdents64 syscall
            // which is not exposed through io_uring
            Err(fs::FileError::Io(
                "Directory listing not yet implemented".into(),
            ))
        }
    }

    fn create_dir(path: &str) -> impl Future<Output = fs::Result<()>> {
        async move {
            // Create io_uring instance
            let ring = Arc::new(
                IoUring::with_capacity(256)
                    .map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
            );

            // Convert path to bytes
            let path_bytes = path.as_bytes();
            let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
            path_with_null.extend_from_slice(path_bytes);
            path_with_null.push(0); // null terminator

            // Create directory with rwx permissions for owner
            ring.mkdirat(AT_FDCWD, &path_with_null, S_IRUSR | S_IWUSR | S_IXUSR)
                .await
                .await
                .map_err(|e| match e {
                    crate::linux::io_uring::IoUringError::System(errno) => {
                        let Some(x) = Errno::from_raw(errno) else {
                            return fs::FileError::Io(format!("Failed to parse directory errno"));
                        };
                        match x {
                            Errno::Exist => fs::FileError::AlreadyExists {
                                path: path.to_string(),
                            },
                            Errno::Acces => fs::FileError::PermissionDenied {
                                path: path.to_string(),
                            },
                            Errno::NoSpc => fs::FileError::DiskFull,
                            Errno::NotDir => fs::FileError::NotADirectory {
                                path: path.to_string(),
                            },
                            Errno::NoEnt => fs::FileError::NotFound {
                                path: path.to_string(),
                            },
                            _ => fs::FileError::Io(format!(
                                "Failed to create directory: errno {}",
                                errno
                            )),
                        }
                    }
                    _ => fs::FileError::Io(format!("Failed to create directory: {}", e)),
                })?;

            Ok(())
        }
    }

    fn remove_file(path: &str) -> impl Future<Output = fs::Result<()>> {
        async move {
            // Create io_uring instance
            let ring = Arc::new(
                IoUring::with_capacity(256)
                    .map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
            );

            // Convert path to bytes
            let path_bytes = path.as_bytes();
            let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
            path_with_null.extend_from_slice(path_bytes);
            path_with_null.push(0); // null terminator

            // Remove file (flags = 0 for regular file)
            ring.unlinkat(AT_FDCWD, &path_with_null, 0)
                .await
                .await
                .map_err(|e| match e {
                    crate::linux::io_uring::IoUringError::System(errno) => {
                        let Some(x) = Errno::from_raw(errno) else {
                            return fs::FileError::Io(format!("Failed to parse remove file errno"));
                        };
                        match x {
                            Errno::NoEnt => fs::FileError::NotFound {
                                path: path.to_string(),
                            },
                            Errno::Acces => fs::FileError::PermissionDenied {
                                path: path.to_string(),
                            },
                            Errno::IsDir => fs::FileError::IsADirectory {
                                path: path.to_string(),
                            },
                            _ => {
                                fs::FileError::Io(format!("Failed to remove file: errno {}", errno))
                            }
                        }
                    }
                    _ => fs::FileError::Io(format!("Failed to remove file: {}", e)),
                })?;

            Ok(())
        }
    }

    fn remove_dir(path: &str) -> impl Future<Output = fs::Result<()>> {
        async move {
            // Create io_uring instance
            let ring = Arc::new(
                IoUring::with_capacity(256)
                    .map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
            );

            // Convert path to bytes
            let path_bytes = path.as_bytes();
            let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
            path_with_null.extend_from_slice(path_bytes);
            path_with_null.push(0); // null terminator

            // Remove directory (AT_REMOVEDIR flag)
            ring.unlinkat(AT_FDCWD, &path_with_null, AT_REMOVEDIR)
                .await
                .await
                .map_err(|e| match e {
                    crate::linux::io_uring::IoUringError::System(errno) => {
                        let Some(x) = Errno::from_raw(errno) else {
                            return fs::FileError::Io(format!(
                                "Failed to parse remove directory errno"
                            ));
                        };
                        match x {
                            Errno::NoEnt => fs::FileError::NotFound {
                                path: path.to_string(),
                            },
                            Errno::Acces => fs::FileError::PermissionDenied {
                                path: path.to_string(),
                            },
                            Errno::NotDir => fs::FileError::NotADirectory {
                                path: path.to_string(),
                            },
                            Errno::NotEmpty => fs::FileError::Io("Directory not empty".into()),
                            _ => fs::FileError::Io(format!(
                                "Failed to remove directory: errno {}",
                                errno
                            )),
                        }
                    }
                    _ => fs::FileError::Io(format!("Failed to remove directory: {}", e)),
                })?;

            Ok(())
        }
    }

    fn rename(from: &str, to: &str) -> impl Future<Output = fs::Result<()>> {
        async move {
            // Create io_uring instance
            let ring = Arc::new(
                IoUring::with_capacity(256)
                    .map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
            );

            // Convert paths to bytes
            let from_bytes = from.as_bytes();
            let mut from_with_null = Vec::with_capacity(from_bytes.len() + 1);
            from_with_null.extend_from_slice(from_bytes);
            from_with_null.push(0); // null terminator

            let to_bytes = to.as_bytes();
            let mut to_with_null = Vec::with_capacity(to_bytes.len() + 1);
            to_with_null.extend_from_slice(to_bytes);
            to_with_null.push(0); // null terminator

            // Rename file/directory
            ring.renameat(AT_FDCWD, &from_with_null, AT_FDCWD, &to_with_null)
                .await
                .await
                .map_err(|e| match e {
                    crate::linux::io_uring::IoUringError::System(errno) => {
                        let Some(x) = Errno::from_raw(errno) else {
                            return fs::FileError::Io(format!("Failed to parse rename errno"));
                        };
                        match x {
                            Errno::NoEnt => fs::FileError::NotFound {
                                path: from.to_string(),
                            },
                            Errno::Acces => fs::FileError::PermissionDenied {
                                path: from.to_string(),
                            },
                            Errno::Exist => fs::FileError::AlreadyExists {
                                path: to.to_string(),
                            },
                            Errno::NoSpc => fs::FileError::DiskFull,
                            _ => fs::FileError::Io(format!("Failed to rename: errno {}", errno)),
                        }
                    }
                    _ => fs::FileError::Io(format!("Failed to rename: {}", e)),
                })?;

            Ok(())
        }
    }

    fn metadata(path: &str) -> impl Future<Output = fs::Result<fs::file::Metadata>> {
        async move {
            // Create io_uring instance
            let ring = Arc::new(
                IoUring::with_capacity(256)
                    .map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
            );

            let mut statx_buf = unsafe { core::mem::zeroed::<Statx>() };

            // Convert path to bytes
            let path_bytes = path.as_bytes();
            let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
            path_with_null.extend_from_slice(path_bytes);
            path_with_null.push(0); // null terminator

            // Perform statx syscall
            ring.statx(
                AT_FDCWD,
                &path_with_null,
                AT_STATX_SYNC_AS_STAT,
                STATX_BASIC_STATS,
                &mut statx_buf,
            )
            .await
            .await
            .map_err(|e| match e {
                crate::linux::io_uring::IoUringError::System(errno) => {
                    let Some(x) = Errno::from_raw(errno) else {
                        return fs::FileError::Io(format!("Failed to parse metadata errno"));
                    };
                    match x {
                        Errno::NoEnt => fs::FileError::NotFound {
                            path: path.to_string(),
                        },
                        Errno::Acces => fs::FileError::PermissionDenied {
                            path: path.to_string(),
                        },
                        _ => fs::FileError::Io(format!("Failed to get metadata: errno {}", errno)),
                    }
                }
                _ => fs::FileError::Io(format!("Failed to get metadata: {}", e)),
            })?;

            // Convert statx to Metadata
            Ok(fs::file::Metadata {
                len: statx_buf.stx_size,
                is_file: (statx_buf.stx_mode & 0o170000) == 0o100000, // S_IFREG
                is_dir: (statx_buf.stx_mode & 0o170000) == 0o040000,  // S_IFDIR
                created: if statx_buf.stx_mask & crate::linux::io_uring::STATX_CTIME != 0 {
                    Some(statx_buf.stx_ctime.tv_sec as u64)
                } else {
                    None
                },
                modified: if statx_buf.stx_mask & crate::linux::io_uring::STATX_MTIME != 0 {
                    Some(statx_buf.stx_mtime.tv_sec as u64)
                } else {
                    None
                },
                accessed: if statx_buf.stx_mask & crate::linux::io_uring::STATX_ATIME != 0 {
                    Some(statx_buf.stx_atime.tv_sec as u64)
                } else {
                    None
                },
            })
        }
    }
}