use core::{borrow::Borrow, fmt, mem, ops::Deref};

use alloc::{boxed::Box, string::String};

use yggdrasil_abi::error::Error;

#[derive(PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Filename(str);

#[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct OwnedFilename(Box<str>);

impl Filename {
    pub const DOT: &'static Filename = unsafe { Self::from_str_unchecked(".") };
    pub const DOT_DOT: &'static Filename = unsafe { Self::from_str_unchecked("..") };

    pub fn is_dot_or_dotdot(&self) -> bool {
        matches!(&self.0, "." | "..")
    }

    pub fn new(name: &str) -> Result<&Self, Error> {
        if !name.is_empty() && !name.contains('/') {
            Ok(unsafe { Self::from_str_unchecked(name) })
        } else {
            Err(Error::InvalidArgument)
        }
    }

    /// # Safety
    ///
    /// The function is memory safe, but is still marked unsafe because it does not check
    /// the filename validity invariants.
    #[inline(always)]
    pub const unsafe fn from_str_unchecked(name: &str) -> &Self {
        mem::transmute(name)
    }
}

impl AsRef<str> for Filename {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for Filename {
    type Target = str;

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

impl fmt::Display for Filename {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl OwnedFilename {
    pub fn new<S: AsRef<str>>(filename: S) -> Result<Self, Error> {
        Filename::new(filename.as_ref()).map(Self::from)
    }
}

impl AsRef<str> for OwnedFilename {
    #[inline]
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl AsRef<Filename> for OwnedFilename {
    #[inline]
    fn as_ref(&self) -> &Filename {
        unsafe { Filename::from_str_unchecked(&self.0) }
    }
}

impl Deref for OwnedFilename {
    type Target = str;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TryFrom<&str> for OwnedFilename {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        Self::new(value)
    }
}

impl TryFrom<String> for OwnedFilename {
    type Error = Error;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        Self::new(&value)
    }
}

impl From<&Filename> for OwnedFilename {
    #[inline]
    fn from(value: &Filename) -> Self {
        Self(value.0.into())
    }
}

impl Borrow<Filename> for OwnedFilename {
    fn borrow(&self) -> &Filename {
        unsafe { Filename::from_str_unchecked(&self.0) }
    }
}

impl PartialEq<Filename> for OwnedFilename {
    fn eq(&self, other: &Filename) -> bool {
        *self.0 == other.0
    }
}

impl fmt::Display for OwnedFilename {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&*self.0, f)
    }
}

// impl<'a> TryFrom<&'a str> for Filename<'a> {
//     type Error = Error;
//
//     fn try_from(value: &'a str) -> Result<Self, Self::Error> {
//         if !value.contains('/') && !value.is_empty() {
//             Ok(Self(value))
//         } else {
//             Err(Error::InvalidArgument)
//         }
//     }
// }
//
// impl Deref for Filename<'_> {
//     type Target = str;
//
//     fn deref(&self) -> &Self::Target {
//         self.0
//     }
// }
//
// impl OwnedFilename {
//     pub fn dot() -> Self {
//         Self(".".into())
//     }
//
//     pub fn dotdot() -> Self {
//         Self("..".into())
//     }
// }
//
// impl Deref for OwnedFilename {
//     type Target = str;
//
//     fn deref(&self) -> &Self::Target {
//         &*self.0
//     }
// }
