use crate::path::Path;
use alloc::{
    borrow::{Borrow, ToOwned},
    format,
    string::String,
};
use core::ops::Deref;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PathBuf(String);

impl PathBuf {
    pub fn new() -> Self {
        Self(String::new())
    }

    pub fn from_str(s: &str) -> Self {
        Self(String::from(s))
    }

    pub fn push<P: AsRef<Path>>(&mut self, path: P) {
        if !self.is_empty() && !self.0.ends_with('/') {
            self.0.push('/');
        }
        self.0
            .push_str(path.as_ref().trim_start_separators().as_str());
    }

    pub fn pop(&mut self) {
        if self.0 == "/" {
            return;
        }
        /*
        /a/b -> /a
        /a -> /
        / -> /
        a/b -> a
        a ->
         */
        if self.is_absolute() {
            match self
                .0
                .trim_start_matches(Path::SEPARATOR)
                .rsplit_once(Path::SEPARATOR)
            {
                Some((head, _)) => {
                    self.0 = format!("/{head}");
                }
                None => {
                    self.0 = "/".into();
                }
            }
        } else {
            todo!()
        }
    }

    pub fn join<P: AsRef<Path>>(&mut self, path: P) -> Self {
        self.push(path);
        self.clone()
    }

    pub fn display(&self) -> &str {
        self.0.as_str()
    }
}

impl Deref for PathBuf {
    type Target = Path;

    fn deref(&self) -> &Self::Target {
        Path::from_str(self.0.as_str())
    }
}

impl AsRef<Path> for PathBuf {
    fn as_ref(&self) -> &Path {
        Path::from_str(self.0.as_str())
    }
}

impl From<&Path> for PathBuf {
    fn from(value: &Path) -> Self {
        Self::from_str(value.as_ref())
    }
}

impl From<&str> for PathBuf {
    fn from(value: &str) -> Self {
        Self::from_str(value)
    }
}

impl From<String> for PathBuf {
    fn from(value: String) -> Self {
        Self(value)
    }
}

impl ToOwned for Path {
    type Owned = PathBuf;

    fn to_owned(&self) -> Self::Owned {
        PathBuf::from(self)
    }
}

impl Borrow<Path> for PathBuf {
    fn borrow(&self) -> &Path {
        Path::from_str(self.0.as_str())
    }
}
