use alloc::{boxed::Box, string::String};
use core::{any::Any, mem::MaybeUninit};

use yggdrasil_abi::{
    error::Error,
    io::{DirectoryEntry, OpenOptions},
};

use crate::vfs::{
    file::{DirectoryOpenPosition, InstanceData},
    Filename,
};

use super::{CreateInfo, Metadata, NodeRef};

/// Common interface shared by all filesystem nodes
pub trait CommonImpl: Send + Sync {
    /// Returns `&self` as a reference to `dyn Any`
    fn as_any(&self) -> &dyn Any {
        unimplemented!();
    }

    /// Fetches the metadata of the file from underlying storage
    fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
        let _ = node;
        unreachable!("Kernel bug: .metadata() not implemented and no IN_MEMORY_PROPS set")
    }

    fn set_metadata(&self, node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
        let _ = node;
        let _ = metadata;
        unreachable!("Kernel bug: .set_metadata() not implemented and no IN_MEMORY_PROPS set")
    }

    /// Fetches the size of the file from underlying storage
    fn size(&self, node: &NodeRef) -> Result<u64, Error> {
        let _ = node;
        Err(Error::NotImplemented)
    }
}

/// Regular file interface
pub trait RegularImpl: CommonImpl {
    /// Opens the file for reading/writing and returns a `(start position, instance data)` tuple
    fn open(
        &self,
        node: &NodeRef,
        opts: OpenOptions,
    ) -> Result<(u64, Option<InstanceData>), Error> {
        let _ = node;
        let _ = opts;
        Err(Error::NotImplemented)
    }

    /// Closes a file
    fn close(&self, node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
        let _ = node;
        let _ = instance;
        Ok(())
    }

    /// Reads data from the file into given buffer
    fn read(
        &self,
        node: &NodeRef,
        instance: Option<&InstanceData>,
        pos: u64,
        buf: &mut [u8],
    ) -> Result<usize, Error> {
        let _ = node;
        let _ = instance;
        let _ = pos;
        let _ = buf;
        Err(Error::NotImplemented)
    }

    /// Writes data to the file from given buffer
    fn write(
        &self,
        node: &NodeRef,
        instance: Option<&InstanceData>,
        pos: u64,
        buf: &[u8],
    ) -> Result<usize, Error> {
        let _ = node;
        let _ = instance;
        let _ = pos;
        let _ = buf;
        Err(Error::NotImplemented)
    }

    /// Resizes the file to requested size
    fn truncate(&self, node: &NodeRef, new_size: u64) -> Result<(), Error> {
        let _ = node;
        let _ = new_size;
        Err(Error::NotImplemented)
    }
}

/// Directory implementation
pub trait DirectoryImpl: CommonImpl {
    /// Opens a directory for reading its entries. Returns [DirectoryOpenPosition] to specify the
    /// starting position.
    fn open(&self, node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
        let _ = node;
        Err(Error::NotImplemented)
    }

    /// Fetches entries from a directory into given buffer
    fn read_entries(
        &self,
        node: &NodeRef,
        pos: u64,
        entries: &mut [MaybeUninit<DirectoryEntry>],
    ) -> Result<(usize, u64), Error> {
        let _ = node;
        let _ = pos;
        let _ = entries;
        Err(Error::NotImplemented)
    }

    /// Creates a child node, but does not associate it with the directory yet
    fn create_node(&self, parent: &NodeRef, info: &CreateInfo) -> Result<NodeRef, Error> {
        let _ = parent;
        let _ = info;
        Err(Error::ReadOnly)
    }

    /// Associates the given node with the directory, creating an entry for it inside
    fn attach_node(&self, parent: &NodeRef, child: &NodeRef, name: &Filename) -> Result<(), Error> {
        let _ = parent;
        let _ = child;
        let _ = name;
        Err(Error::NotImplemented)
    }

    /// Removes an entry of the directory with given name
    fn unlink_node(&self, parent: &NodeRef, child: &NodeRef, name: &Filename) -> Result<(), Error> {
        let _ = parent;
        let _ = child;
        let _ = name;
        Err(Error::NotImplemented)
    }

    /// Fetches the child of the directory with given name
    fn lookup(&self, node: &NodeRef, name: &Filename) -> Result<NodeRef, Error> {
        let _ = node;
        let _ = name;
        Err(Error::NotImplemented)
    }

    fn contains(&self, node: &NodeRef, name: &Filename) -> bool {
        match self.lookup(node, name) {
            Ok(_) => true,
            Err(_) => false,
        }
    }

    /// Returns the "length" of the directory in entries
    fn len(&self, node: &NodeRef) -> Result<usize, Error> {
        let _ = node;
        Err(Error::NotImplemented)
    }
}

/// Symbolic link interface
pub trait SymlinkImpl: CommonImpl {
    // /// Returns the target node (if such is available directly) of the link
    // fn target(&self, node: &NodeRef) -> Result<NodeRef, Error> {
    //     Err(Error::NotImplemented)
    // }

    /// Fetches the contents of the symlink into a [String]
    fn read_to_string(&self) -> Result<String, Error> {
        let mut data = Box::new([0; 512]);
        let len = self.read_link(data.as_mut())?;
        if len == data.len() {
            return Err(Error::InvalidFile);
        }
        let str = core::str::from_utf8(&data[..len]).map_err(|_| Error::InvalidFile)?;
        Ok(String::from(str))
    }

    /// Fetches the contents of the symlink into a buffer
    fn read_link(&self, buf: &mut [u8]) -> Result<usize, Error> {
        let _ = buf;
        Err(Error::NotImplemented)
    }
}

/// Hard link interface
pub trait HardlinkImpl: Sync + Send {
    fn target(&self, node: &NodeRef) -> Result<NodeRef, Error> {
        let _ = node;
        Err(Error::NotImplemented)
    }
}
