use alloc::sync::Arc;
use yggdrasil_abi::error::Error;

use crate::vfs::path::OwnedFilename;

use super::{Node, NodeRef};

impl Node {
    /// Returns the parent node of this node, or itself if no parent is present
    pub fn parent(self: &NodeRef) -> NodeRef {
        self.parent.lock().as_ref().unwrap_or(self).clone()
    }

    /// Returns `true` if this node is a root
    pub fn is_root(self: &NodeRef) -> bool {
        self.parent.lock().is_none()
    }

    /// Returns the count of entries in the directory
    pub fn children_len(&self) -> Result<usize, Error> {
        let directory = self.as_directory()?;
        Ok(directory.children.lock().len())
    }

    /// Adds an entry to the directory tree cache
    pub fn add_child<S: Into<OwnedFilename>>(
        self: &NodeRef,
        name: S,
        child: NodeRef,
    ) -> Result<(), Error> {
        let name = name.into();
        let directory = self.as_directory()?;
        let mut children = directory.children.lock();

        if children.iter().any(|(n, _)| name == *n) {
            return Err(Error::AlreadyExists);
        }

        assert!(child.parent.replace(Some(self.clone())).is_none());
        children.push((name, child));

        Ok(())
    }

    pub fn unlink_child(self: &NodeRef, child_node: &NodeRef) -> Result<(), Error> {
        let directory = self.as_directory()?;
        let mut children = directory.children.lock();

        let parent = child_node.parent.replace(None);
        let Some(parent) = parent else {
            unreachable!("Tried to unlink node from no parent");
        };
        assert!(Arc::ptr_eq(&parent, self));
        children.retain(|(_, n)| !Arc::ptr_eq(n, child_node));

        Ok(())
    }
}
