use core::sync::atomic::Ordering;

use alloc::sync::Arc;

use libk::vfs::{
    impls::fixed_path_symlink_ext, CommonImpl, CreateFileType, CreateInfo, DirectoryImpl,
    DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef,
};
use yggdrasil_abi::error::Error;

use crate::{block::BlockAllocator, file::FileNode, MemoryFilesystem, INO_COUNTER};

pub(crate) struct DirectoryNode<A: BlockAllocator> {
    fs: Arc<MemoryFilesystem<A>>,
}

impl<A: BlockAllocator> DirectoryNode<A> {
    pub fn new(fs: Arc<MemoryFilesystem<A>>, metadata: Metadata) -> NodeRef {
        Node::directory(
            Self { fs: fs.clone() },
            NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS,
            Some(metadata),
            Some(fs),
        )
    }
}

impl<A: BlockAllocator> CommonImpl for DirectoryNode<A> {}

impl<A: BlockAllocator> DirectoryImpl for DirectoryNode<A> {
    fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
        Ok(DirectoryOpenPosition::FromCache)
    }

    fn create_node(&self, _parent: &NodeRef, info: &CreateInfo) -> Result<NodeRef, Error> {
        let ino = INO_COUNTER.fetch_add(1, Ordering::Relaxed);
        let metadata = Metadata::now(info.uid, info.gid, info.mode, ino);
        match info.ty {
            CreateFileType::File => Ok(FileNode::<A>::new(self.fs.clone(), metadata)),
            CreateFileType::Directory => Ok(DirectoryNode::<A>::new(self.fs.clone(), metadata)),
            CreateFileType::Symlink(target) => {
                Ok(fixed_path_symlink_ext(target.to_string(), metadata))
            }
        }
    }
}
