use crate::{PtrConst, PtrMut, PtrUninit, TypedPtrConst, TypedPtrMut, TypedPtrUninit};
use bitflags::bitflags;
use core::{cmp::Ordering, marker::PhantomData, mem};

use crate::Shape;

use super::UnsizedError;

//======== Type Information ========

/// A function that formats the name of a type.
///
/// This helps avoid allocations, and it takes options.
pub type TypeNameFn = fn(f: &mut core::fmt::Formatter, opts: TypeNameOpts) -> core::fmt::Result;

/// Options for formatting the name of a type
#[derive(Clone, Copy)]
pub struct TypeNameOpts {
    /// as long as this is > 0, keep formatting the type parameters
    /// when it reaches 0, format type parameters as `...`
    /// if negative, all type parameters are formatted
    pub recurse_ttl: isize,
}

impl Default for TypeNameOpts {
    #[inline]
    fn default() -> Self {
        Self { recurse_ttl: -1 }
    }
}

impl TypeNameOpts {
    /// Create a new `NameOpts` for which none of the type parameters are formatted
    #[inline]
    pub fn none() -> Self {
        Self { recurse_ttl: 0 }
    }

    /// Create a new `NameOpts` for which only the direct children are formatted
    #[inline]
    pub fn one() -> Self {
        Self { recurse_ttl: 1 }
    }

    /// Create a new `NameOpts` for which all type parameters are formatted
    #[inline]
    pub fn infinite() -> Self {
        Self { recurse_ttl: -1 }
    }

    /// Decrease the `recurse_ttl` — if it's != 0, returns options to pass when
    /// formatting children type parameters.
    ///
    /// If this returns `None` and you have type parameters, you should render a
    /// `…` (unicode ellipsis) character instead of your list of types.
    ///
    /// See the implementation for `Vec` for examples.
    #[inline]
    pub fn for_children(&self) -> Option<Self> {
        match self.recurse_ttl.cmp(&0) {
            Ordering::Greater => Some(Self {
                recurse_ttl: self.recurse_ttl - 1,
            }),
            Ordering::Less => Some(Self {
                recurse_ttl: self.recurse_ttl,
            }),
            Ordering::Equal => None,
        }
    }
}

//======== Invariants ========

/// Function to validate the invariants of a value. If it returns false, the value is considered invalid.
///
/// # Safety
///
/// The `value` parameter must point to aligned, initialized memory of the correct type.
pub type InvariantsFn = for<'mem> unsafe fn(value: PtrConst<'mem>) -> bool;

/// Function to validate the invariants of a value. If it returns false, the value is considered invalid.
pub type InvariantsFnTyped<T> = fn(value: TypedPtrConst<'_, T>) -> bool;

//======== Memory Management ========

/// Function to drop a value
///
/// # Safety
///
/// The `value` parameter must point to aligned, initialized memory of the correct type.
/// After calling this function, the memory pointed to by `value` should not be accessed again
/// until it is properly reinitialized.
pub type DropInPlaceFn = for<'mem> unsafe fn(value: PtrMut<'mem>) -> PtrUninit<'mem>;

/// Function to clone a value into another already-allocated value
///
/// # Safety
///
/// The `source` parameter must point to aligned, initialized memory of the correct type.
/// The `target` parameter has the correct layout and alignment, but points to
/// uninitialized memory. The function returns the same pointer wrapped in an [`PtrMut`].
pub type CloneIntoFn =
    for<'src, 'dst> unsafe fn(source: PtrConst<'src>, target: PtrUninit<'dst>) -> PtrMut<'dst>;

/// Function to clone a value into another already-allocated value
pub type CloneIntoFnTyped<T> = for<'src, 'dst> fn(
    source: TypedPtrConst<'src, T>,
    target: TypedPtrUninit<'dst, T>,
) -> TypedPtrMut<'dst, T>;

/// Function to set a value to its default in-place
///
/// # Safety
///
/// The `target` parameter has the correct layout and alignment, but points to
/// uninitialized memory. The function returns the same pointer wrapped in an [`PtrMut`].
pub type DefaultInPlaceFn = for<'mem> unsafe fn(target: PtrUninit<'mem>) -> PtrMut<'mem>;
/// Function to set a value to its default in-place
pub type DefaultInPlaceFnTyped<T> =
    for<'mem> fn(target: TypedPtrUninit<'mem, T>) -> TypedPtrMut<'mem, T>;

//======== Conversion ========

/// Function to parse a value from a string.
///
/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
///
/// # Safety
///
/// The `target` parameter has the correct layout and alignment, but points to
/// uninitialized memory. If this function succeeds, it should return `Ok` with the
/// same pointer wrapped in an [`PtrMut`]. If parsing fails, it returns `Err` with an error.
pub type ParseFn =
    for<'mem> unsafe fn(s: &str, target: PtrUninit<'mem>) -> Result<PtrMut<'mem>, ParseError>;

/// Function to parse a value from a string.
///
/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
pub type ParseFnTyped<T> = for<'mem> fn(
    s: &str,
    target: TypedPtrUninit<'mem, T>,
) -> Result<TypedPtrMut<'mem, T>, ParseError>;

/// Error returned by [`ParseFn`]
#[derive(Debug)]
pub enum ParseError {
    /// Generic error message
    Generic(&'static str),
}

impl core::fmt::Display for ParseError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            ParseError::Generic(msg) => write!(f, "Parse failed: {msg}"),
        }
    }
}

impl core::error::Error for ParseError {}

/// Function to try converting from another type
///
/// # Safety
///
/// The `target` parameter has the correct layout and alignment, but points to
/// uninitialized memory. If this function succeeds, it should return `Ok` with the
/// same pointer wrapped in an [`PtrMut`]. If conversion fails, it returns `Err` with an error.
pub type TryFromFn = for<'src, 'mem, 'shape> unsafe fn(
    source: PtrConst<'src>,
    source_shape: &'static Shape,
    target: PtrUninit<'mem>,
) -> Result<PtrMut<'mem>, TryFromError>;

/// Function to try converting from another type
pub type TryFromFnTyped<T> = for<'src, 'mem, 'shape> fn(
    source: TypedPtrConst<'src, T>,
    source_shape: &'static Shape,
    target: TypedPtrUninit<'mem, T>,
) -> Result<&'mem mut T, TryFromError>;

/// Error type for TryFrom conversion failures
#[derive(Debug, PartialEq, Clone)]
pub enum TryFromError {
    /// Generic conversion error
    Generic(&'static str),

    /// The target shape doesn't implement conversion from any source shape (no try_from in vtable)
    Unimplemented,

    /// The target shape has a conversion implementation, but it doesn't support converting from this specific source shape
    UnsupportedSourceShape {
        /// The source shape that failed to convert
        src_shape: &'static Shape,

        /// The shapes that the `TryFrom` implementation supports
        expected: &'static [&'static Shape],
    },

    /// `!Sized` type
    Unsized,
}

impl core::fmt::Display for TryFromError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            TryFromError::Generic(msg) => write!(f, "{msg}"),
            TryFromError::Unimplemented => write!(
                f,
                "Shape doesn't implement any conversions (no try_from function)",
            ),
            TryFromError::UnsupportedSourceShape {
                src_shape: source_shape,
                expected,
            } => {
                write!(f, "Incompatible types: {source_shape} (expected one of ")?;
                for (index, sh) in expected.iter().enumerate() {
                    if index > 0 {
                        write!(f, ", ")?;
                    }
                    write!(f, "{sh}")?;
                }
                write!(f, ")")?;
                Ok(())
            }
            TryFromError::Unsized => write!(f, "Unsized type"),
        }
    }
}

impl core::error::Error for TryFromError {}

impl From<UnsizedError> for TryFromError {
    #[inline]
    fn from(_value: UnsizedError) -> Self {
        Self::Unsized
    }
}

/// Function to convert a transparent/newtype wrapper into its inner type.
///
/// This is used for types that wrap another type (like smart pointers, newtypes, etc.)
/// where the wrapper can be unwrapped to access the inner value. Primarily used during serialization.
///
/// # Safety
///
/// This function is unsafe because it operates on raw pointers.
///
/// The `src_ptr` must point to a valid, initialized instance of the wrapper type.
/// The `dst` pointer must point to valid, uninitialized memory suitable for holding an instance
/// of the inner type.
///
/// The function will return a pointer to the initialized inner value.
pub type TryIntoInnerFn = for<'src, 'dst> unsafe fn(
    src_ptr: PtrMut<'src>,
    dst: PtrUninit<'dst>,
) -> Result<PtrMut<'dst>, TryIntoInnerError>;
/// Function to convert a transparent/newtype wrapper into its inner type.
///
/// This is used for types that wrap another type (like smart pointers, newtypes, etc.)
/// where the wrapper can be unwrapped to access the inner value. Primarily used during serialization.
pub type TryIntoInnerFnTyped<T> = for<'src, 'dst> fn(
    src_ptr: TypedPtrConst<'src, T>,
    dst: TypedPtrUninit<'dst, T>,
) -> Result<&'dst mut T, TryIntoInnerError>;

/// Error type returned by [`TryIntoInnerFn`] when attempting to extract
/// the inner value from a wrapper type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TryIntoInnerError {
    /// Indicates that the inner value cannot be extracted at this time,
    /// such as when a mutable borrow is already active.
    Unavailable,
    /// Indicates that another unspecified error occurred during extraction.
    Other(&'static str),
}

impl core::fmt::Display for TryIntoInnerError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            TryIntoInnerError::Unavailable => {
                write!(f, "inner value is unavailable for extraction")
            }
            TryIntoInnerError::Other(msg) => write!(f, "{msg}"),
        }
    }
}

impl core::error::Error for TryIntoInnerError {}

/// Function to borrow the inner value from a transparent/newtype wrapper without copying.
///
/// This is used for types that wrap another type (like smart pointers, newtypes, etc.)
/// to efficiently access the inner value without transferring ownership.
///
/// # Safety
///
/// This function is unsafe because it operates on raw pointers.
///
/// The `src_ptr` must point to a valid, initialized instance of the wrapper type.
/// The returned pointer points to memory owned by the wrapper and remains valid
/// as long as the wrapper is valid and not mutated.
pub type TryBorrowInnerFn =
    for<'src> unsafe fn(src_ptr: PtrConst<'src>) -> Result<PtrConst<'src>, TryBorrowInnerError>;

/// Function to borrow the inner value from a transparent/newtype wrapper without copying.
///
/// This is used for types that wrap another type (like smart pointers, newtypes, etc.)
/// to efficiently access the inner value without transferring ownership.
pub type TryBorrowInnerFnTyped<T> =
    for<'src> fn(
        src_ptr: TypedPtrConst<'src, T>,
    ) -> Result<TypedPtrConst<'src, T>, TryBorrowInnerError>;

/// Error type returned by [`TryBorrowInnerFn`] when attempting to borrow
/// the inner value from a wrapper type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TryBorrowInnerError {
    /// Indicates that the inner value cannot be borrowed at this time,
    /// such as when a mutable borrow is already active.
    Unavailable,
    /// Indicates an other, unspecified error occurred during the borrow attempt.
    /// The contained string provides a description of the error.
    Other(&'static str),
}

impl core::fmt::Display for TryBorrowInnerError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            TryBorrowInnerError::Unavailable => {
                write!(f, "inner value is unavailable for borrowing")
            }
            TryBorrowInnerError::Other(msg) => {
                write!(f, "{msg}")
            }
        }
    }
}

impl core::error::Error for TryBorrowInnerError {}

//======== Comparison ========

/// Function to check if two values are partially equal
///
/// # Safety
///
/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
pub type PartialEqFn = for<'l, 'r> unsafe fn(left: PtrConst<'l>, right: PtrConst<'r>) -> bool;
/// Function to check if two values are partially equal
pub type PartialEqFnTyped<T> = fn(left: TypedPtrConst<'_, T>, right: TypedPtrConst<'_, T>) -> bool;

/// Function to compare two values and return their ordering if comparable
///
/// # Safety
///
/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
pub type PartialOrdFn =
    for<'l, 'r> unsafe fn(left: PtrConst<'l>, right: PtrConst<'r>) -> Option<Ordering>;
/// Function to compare two values and return their ordering if comparable
pub type PartialOrdFnTyped<T> =
    fn(left: TypedPtrConst<'_, T>, right: TypedPtrConst<'_, T>) -> Option<Ordering>;

/// Function to compare two values and return their ordering
///
/// # Safety
///
/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
pub type CmpFn = for<'l, 'r> unsafe fn(left: PtrConst<'l>, right: PtrConst<'r>) -> Ordering;
/// Function to compare two values and return their ordering
pub type CmpFnTyped<T> = fn(left: TypedPtrConst<'_, T>, right: TypedPtrConst<'_, T>) -> Ordering;

//======== Hashing ========

/// Function to hash a value
///
/// # Safety
///
/// The `value` parameter must point to aligned, initialized memory of the correct type.
/// The hasher pointer must be a valid pointer to a Hasher trait object.
pub type HashFn =
    for<'mem> unsafe fn(value: PtrConst<'mem>, hasher_this: &mut dyn core::hash::Hasher);

/// Function to hash a value
pub type HashFnTyped<T> =
    for<'mem> fn(value: TypedPtrConst<'mem, T>, hasher_this: &mut dyn core::hash::Hasher);

/// Function to write bytes to a hasher
///
/// # Safety
///
/// The `hasher_self` parameter must be a valid pointer to a hasher
pub type HasherWriteFn = for<'mem> unsafe fn(hasher_self: PtrMut<'mem>, bytes: &[u8]);
/// Function to write bytes to a hasher
pub type HasherWriteFnTyped<T> = for<'mem> fn(hasher_self: TypedPtrMut<'mem, T>, bytes: &[u8]);

/// Provides an implementation of [`core::hash::Hasher`] for a given hasher pointer and write function
///
/// See [`HashFn`] for more details on the parameters.
pub struct HasherProxy<'a> {
    hasher_this: PtrMut<'a>,
    hasher_write_fn: HasherWriteFn,
}

impl<'a> HasherProxy<'a> {
    /// Create a new `HasherProxy` from a hasher pointer and a write function
    ///
    /// # Safety
    ///
    /// The `hasher_this` parameter must be a valid pointer to a Hasher trait object.
    /// The `hasher_write_fn` parameter must be a valid function pointer.
    #[inline]
    pub unsafe fn new(hasher_this: PtrMut<'a>, hasher_write_fn: HasherWriteFn) -> Self {
        Self {
            hasher_this,
            hasher_write_fn,
        }
    }
}

impl core::hash::Hasher for HasherProxy<'_> {
    fn finish(&self) -> u64 {
        unimplemented!("finish is not needed for this implementation")
    }
    #[inline]
    fn write(&mut self, bytes: &[u8]) {
        unsafe { (self.hasher_write_fn)(self.hasher_this, bytes) }
    }
}

//======== Marker Traits ========

bitflags! {
    /// Bitflags for common marker traits that a type may implement
    ///
    /// Note: facet is not able to see negative impls, so it might mistakenly think a struct `S`
    /// is `Send` because all its fields are, ignoring that there is an `impl !Send for S`.
    ///
    /// Similarly, it might think a struct `S` is `!Send` just because one of the fields is an
    /// `UnsafeCell` (which is `!Send`), ignoring that there is an `unsafe impl Send for S`.
    ///
    /// The reason facet's determination of `Send` or `!Send` is based on fields is because structs
    /// can be generic, in which case our specialization trick does not work.
    #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct MarkerTraits: u8 {
        /// Indicates that the type implements the [`Eq`] marker trait
        const EQ = 1 << 0;
        /// Indicates that the type implements the [`Send`] marker trait.
        ///
        /// Note: this is a best-effort guess, see the documentation about negative impls in [MarkerTraits].
        const SEND = 1 << 1;
        /// Indicates that the type implements the [`Sync`] marker trait
        ///
        /// Note: this is a best-effort guess, see the documentation about negative impls in [MarkerTraits].
        const SYNC = 1 << 2;
        /// Indicates that the type implements the [`Copy`] marker trait
        const COPY = 1 << 3;
        /// Indicates that the type implements the [`Unpin`] marker trait
        const UNPIN = 1 << 4;
        /// Indicates that the type implements the [`UnwindSafe`](core::panic::UnwindSafe) marker trait
        ///
        /// Note: this is a best-effort guess, see the documentation about negative impls in [MarkerTraits].
        const UNWIND_SAFE = 1 << 5;
        /// Indicates that the type implements the [`RefUnwindSafe`](core::panic::RefUnwindSafe) marker trait
        ///
        /// Note: this is a best-effort guess, see the documentation about negative impls in [MarkerTraits].
        const REF_UNWIND_SAFE = 1 << 6;
    }
}

//======== Display and Debug ========

/// Function to format a value for display
///
/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
///
/// # Safety
///
/// The `value` parameter must point to aligned, initialized memory of the correct type.
pub type DisplayFn =
    for<'mem> unsafe fn(value: PtrConst<'mem>, f: &mut core::fmt::Formatter) -> core::fmt::Result;

/// Function to format a value for display
///
/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
pub type DisplayFnTyped<T> =
    fn(value: TypedPtrConst<'_, T>, f: &mut core::fmt::Formatter) -> core::fmt::Result;

/// Function to format a value for debug.
/// If this returns None, the shape did not implement Debug.
pub type DebugFn =
    for<'mem> unsafe fn(value: PtrConst<'mem>, f: &mut core::fmt::Formatter) -> core::fmt::Result;

/// Function to format a value for debug.
/// If this returns None, the shape did not implement Debug.
pub type DebugFnTyped<T> =
    fn(value: TypedPtrConst<'_, T>, f: &mut core::fmt::Formatter) -> core::fmt::Result;

/// A vtable representing the operations that can be performed on a type,
/// either for sized or unsized types.
///
/// This enum encapsulates the specific vtables allowing generic type-agnostic
/// dynamic dispatch for core capabilities (clone, drop, compare, hash, etc).
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct ValueVTable {
    /// cf. [`TypeNameFn`]
    pub type_name: TypeNameFn,
    /// Marker traits implemented by the type
    pub marker_traits: MarkerTraits,

    /// cf. [`DropInPlaceFn`] — if None, drops without side-effects
    pub drop_in_place: Option<DropInPlaceFn>,

    /// cf. [`InvariantsFn`]
    pub invariants: Option<InvariantsFn>,

    /// cf. [`DisplayFn`]
    pub display: Option<DisplayFn>,

    /// cf. [`DebugFn`]
    pub debug: Option<DebugFn>,

    /// cf. [`DefaultInPlaceFn`]
    pub default_in_place: Option<DefaultInPlaceFn>,

    /// cf. [`CloneIntoFn`]
    pub clone_into: Option<CloneIntoFn>,

    /// cf. [`PartialEqFn`] for equality comparison
    pub partial_eq: Option<PartialEqFn>,

    /// cf. [`PartialOrdFn`] for partial ordering comparison
    pub partial_ord: Option<PartialOrdFn>,

    /// cf. [`CmpFn`] for total ordering
    pub ord: Option<CmpFn>,

    /// cf. [`HashFn`]
    pub hash: Option<HashFn>,

    /// cf. [`ParseFn`]
    pub parse: Option<ParseFn>,

    /// cf. [`TryFromFn`]
    ///
    /// This also acts as a "TryFromInner" — you can use it to go:
    ///
    ///   * `String` => `Utf8PathBuf`
    ///   * `String` => `Uuid`
    ///   * `T` => `Option<T>`
    ///   * `T` => `Arc<T>`
    ///   * `T` => `NonZero<T>`
    ///   * etc.
    ///
    pub try_from: Option<TryFromFn>,

    /// cf. [`TryIntoInnerFn`]
    ///
    /// This is used by transparent types to convert the wrapper type into its inner value.
    /// Primarily used during serialization.
    pub try_into_inner: Option<TryIntoInnerFn>,

    /// cf. [`TryBorrowInnerFn`]
    ///
    /// This is used by transparent types to efficiently access the inner value without copying.
    pub try_borrow_inner: Option<TryBorrowInnerFn>,
}

macro_rules! has_fn {
    ($self:expr, $name:tt) => {
        $self.$name.is_some()
    };
}

impl ValueVTable {
    /// Get the marker traits implemented for the type
    #[inline]
    pub const fn marker_traits(&self) -> MarkerTraits {
        self.marker_traits
    }

    /// Get the type name fn of the type
    #[inline]
    pub const fn type_name(&self) -> TypeNameFn {
        self.type_name
    }

    /// Check if the type implements the [`Eq`] marker trait
    #[inline]
    pub const fn is_eq(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::EQ)
    }

    /// Check if the type implements the [`Send`] marker trait
    #[inline]
    pub const fn is_send(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::SEND)
    }

    /// Check if the type implements the [`Sync`] marker trait
    #[inline]
    pub const fn is_sync(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::SYNC)
    }

    /// Check if the type implements the [`Copy`] marker trait
    #[inline]
    pub const fn is_copy(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::COPY)
    }

    /// Check if the type implements the [`Unpin`] marker trait
    #[inline]
    pub const fn is_unpin(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::UNPIN)
    }

    /// Check if the type implements the [`UnwindSafe`](core::panic::UnwindSafe) marker trait
    #[inline]
    pub const fn is_unwind_safe(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::UNWIND_SAFE)
    }

    /// Check if the type implements the [`RefUnwindSafe`](core::panic::RefUnwindSafe) marker trait
    #[inline]
    pub const fn is_ref_unwind_safe(&self) -> bool {
        self.marker_traits().contains(MarkerTraits::REF_UNWIND_SAFE)
    }

    /// Returns `true` if the type implements the [`Display`](core::fmt::Display) trait and the `display` function is available in the vtable.
    #[inline]
    pub const fn has_display(&self) -> bool {
        has_fn!(self, display)
    }

    /// Returns `true` if the type implements the [`Debug`] trait and the `debug` function is available in the vtable.
    #[inline]
    pub const fn has_debug(&self) -> bool {
        has_fn!(self, debug)
    }

    /// Returns `true` if the type implements the [`PartialEq`] trait and the `partial_eq` function is available in the vtable.
    #[inline]
    pub const fn has_partial_eq(&self) -> bool {
        has_fn!(self, partial_eq)
    }

    /// Returns `true` if the type implements the [`PartialOrd`] trait and the `partial_ord` function is available in the vtable.
    #[inline]
    pub const fn has_partial_ord(&self) -> bool {
        has_fn!(self, partial_ord)
    }

    /// Returns `true` if the type implements the [`Ord`] trait and the `ord` function is available in the vtable.
    #[inline]
    pub const fn has_ord(&self) -> bool {
        has_fn!(self, ord)
    }

    /// Returns `true` if the type implements the [`Hash`] trait and the `hash` function is available in the vtable.
    #[inline]
    pub const fn has_hash(&self) -> bool {
        has_fn!(self, hash)
    }

    /// Returns `true` if the type supports default-in-place construction via the vtable.
    #[inline]
    pub const fn has_default_in_place(&self) -> bool {
        has_fn!(self, default_in_place)
    }

    /// Returns `true` if the type supports in-place cloning via the vtable.
    #[inline]
    pub const fn has_clone_into(&self) -> bool {
        has_fn!(self, clone_into)
    }

    /// Returns `true` if the type supports parsing from a string via the vtable.
    #[inline]
    pub const fn has_parse(&self) -> bool {
        has_fn!(self, parse)
    }
    /// Creates a new [`ValueVTableBuilder`]
    pub const fn builder<T: ?Sized>() -> ValueVTableBuilder<T> {
        ValueVTableBuilder::new()
    }
}

/// A typed view of a [`ValueVTable`].
#[derive(Debug)]
pub struct VTableView<T: ?Sized>(&'static ValueVTable, PhantomData<T>);

impl<'a, T: crate::Facet<'a> + ?Sized> VTableView<&'a mut T> {
    /// Fetches the vtable for the type.
    pub fn of_deref() -> Self {
        Self(const { &T::SHAPE.vtable }, PhantomData)
    }
}

impl<'a, T: crate::Facet<'a> + ?Sized> VTableView<&'a T> {
    /// Fetches the vtable for the type.
    pub fn of_deref() -> Self {
        Self(const { &T::SHAPE.vtable }, PhantomData)
    }
}

macro_rules! get_fn {
    ($self:expr, $name:tt) => {
        $self.0.$name.map(|f| unsafe { mem::transmute(f) })
    };
}

impl<'a, T: crate::Facet<'a> + ?Sized> VTableView<T> {
    /// Fetches the vtable for the type.
    pub const fn of() -> Self {
        let this = Self(const { &T::SHAPE.vtable }, PhantomData);

        if const { core::mem::size_of::<*const T>() == core::mem::size_of::<*const ()>() } {
            assert!(T::SHAPE.layout.sized_layout().is_ok());
            assert!(core::mem::size_of::<*const T>() == core::mem::size_of::<*const ()>());
        } else {
            assert!(T::SHAPE.layout.sized_layout().is_err());
            assert!(core::mem::size_of::<*const T>() == 2 * core::mem::size_of::<*const ()>());
        }

        this
    }

    /// cf. [`TypeNameFn`]
    #[inline(always)]
    pub const fn type_name(&self) -> TypeNameFn {
        self.0.type_name()
    }

    /// cf. [`InvariantsFn`]
    #[inline(always)]
    pub fn invariants(&self) -> Option<InvariantsFnTyped<T>> {
        get_fn!(self, invariants)
    }

    /// cf. [`DisplayFn`]
    #[inline(always)]
    pub fn display(&self) -> Option<DisplayFnTyped<T>> {
        get_fn!(self, display)
    }

    /// cf. [`DebugFn`]
    #[inline(always)]
    pub fn debug(&self) -> Option<DebugFnTyped<T>> {
        get_fn!(self, debug)
    }

    /// cf. [`PartialEqFn`] for equality comparison
    #[inline(always)]
    pub fn partial_eq(&self) -> Option<PartialEqFnTyped<T>> {
        get_fn!(self, partial_eq)
    }

    /// cf. [`PartialOrdFn`] for partial ordering comparison
    #[inline(always)]
    pub fn partial_ord(&self) -> Option<PartialOrdFnTyped<T>> {
        get_fn!(self, partial_ord)
    }

    /// cf. [`CmpFn`] for total ordering
    #[inline(always)]
    pub fn ord(&self) -> Option<CmpFnTyped<T>> {
        get_fn!(self, ord)
    }

    /// cf. [`HashFn`]
    #[inline(always)]
    pub fn hash(&self) -> Option<HashFnTyped<T>> {
        get_fn!(self, hash)
    }

    /// cf. [`TryBorrowInnerFn`]
    ///
    /// This is used by transparent types to efficiently access the inner value without copying.
    #[inline(always)]
    pub fn try_borrow_inner(&self) -> Option<TryBorrowInnerFnTyped<T>> {
        get_fn!(self, try_borrow_inner)
    }
}

impl<'a, T: crate::Facet<'a>> VTableView<T> {
    /// cf. [`DefaultInPlaceFn`]
    #[inline(always)]
    pub fn default_in_place(&self) -> Option<DefaultInPlaceFnTyped<T>> {
        self.0.default_in_place.map(|default_in_place| unsafe {
            mem::transmute::<DefaultInPlaceFn, DefaultInPlaceFnTyped<T>>(default_in_place)
        })
    }

    /// cf. [`CloneIntoFn`]
    #[inline(always)]
    pub fn clone_into(&self) -> Option<CloneIntoFnTyped<T>> {
        self.0.clone_into.map(|clone_into| unsafe {
            mem::transmute::<CloneIntoFn, CloneIntoFnTyped<T>>(clone_into)
        })
    }

    /// cf. [`ParseFn`]
    #[inline(always)]
    pub fn parse(&self) -> Option<ParseFnTyped<T>> {
        self.0
            .parse
            .map(|parse| unsafe { mem::transmute::<ParseFn, ParseFnTyped<T>>(parse) })
    }

    /// cf. [`TryFromFn`]
    ///
    /// This also acts as a "TryFromInner" — you can use it to go:
    ///
    ///   * `String` => `Utf8PathBuf`
    ///   * `String` => `Uuid`
    ///   * `T` => `Option<T>`
    ///   * `T` => `Arc<T>`
    ///   * `T` => `NonZero<T>`
    ///   * etc.
    ///
    #[inline(always)]
    pub fn try_from(&self) -> Option<TryFromFnTyped<T>> {
        self.0
            .try_from
            .map(|try_from| unsafe { mem::transmute::<TryFromFn, TryFromFnTyped<T>>(try_from) })
    }

    /// cf. [`TryIntoInnerFn`]
    ///
    /// This is used by transparent types to convert the wrapper type into its inner value.
    /// Primarily used during serialization.
    #[inline(always)]
    pub fn try_into_inner(&self) -> Option<TryIntoInnerFnTyped<T>> {
        self.0.try_into_inner.map(|try_into_inner| unsafe {
            mem::transmute::<TryIntoInnerFn, TryIntoInnerFnTyped<T>>(try_into_inner)
        })
    }
}
/// Builds a [`ValueVTable`]
pub struct ValueVTableBuilder<T: ?Sized> {
    type_name: Option<TypeNameFn>,
    display: Option<DisplayFn>,
    debug: Option<DebugFn>,
    default_in_place: Option<DefaultInPlaceFn>,
    clone_into: Option<CloneIntoFn>,
    marker_traits: MarkerTraits,
    partial_eq: Option<PartialEqFn>,
    partial_ord: Option<PartialOrdFn>,
    ord: Option<CmpFn>,
    hash: Option<HashFn>,
    drop_in_place: Option<DropInPlaceFn>,
    invariants: Option<InvariantsFn>,
    parse: Option<ParseFn>,
    try_from: Option<TryFromFn>,
    try_into_inner: Option<TryIntoInnerFn>,
    try_borrow_inner: Option<TryBorrowInnerFn>,
    _pd: PhantomData<T>,
}

impl<T: ?Sized> ValueVTableBuilder<T> {
    /// Creates a new [`ValueVTableBuilder`] with all fields set to `None`.
    #[allow(clippy::new_without_default)]
    pub const fn new() -> Self {
        Self {
            type_name: None,
            display: None,
            debug: None,
            default_in_place: None,
            clone_into: None,
            marker_traits: MarkerTraits::empty(),
            partial_eq: None,
            partial_ord: None,
            ord: None,
            hash: None,
            drop_in_place: if mem::needs_drop::<T>() {
                Some(|value| unsafe { value.drop_in_place::<T>() })
            } else {
                None
            },
            invariants: None,
            parse: None,
            try_from: None,
            try_into_inner: None,
            try_borrow_inner: None,
            _pd: PhantomData,
        }
    }

    /// Sets the type name function for this builder.
    pub const fn type_name(mut self, type_name: TypeNameFn) -> Self {
        self.type_name = Some(type_name);
        self
    }

    /// Sets the display function for this builder.
    pub const fn display(mut self, display: Option<DisplayFnTyped<T>>) -> Self {
        self.display =
            unsafe { mem::transmute::<Option<DisplayFnTyped<T>>, Option<DisplayFn>>(display) };
        self
    }

    /// Sets the debug function for this builder.
    pub const fn debug(mut self, debug: Option<DebugFnTyped<T>>) -> Self {
        self.debug = unsafe { mem::transmute::<Option<DebugFnTyped<T>>, Option<DebugFn>>(debug) };
        self
    }

    /// Sets the default_in_place function for this builder.
    pub const fn default_in_place(
        mut self,
        default_in_place: Option<DefaultInPlaceFnTyped<T>>,
    ) -> Self {
        self.default_in_place = unsafe {
            mem::transmute::<Option<DefaultInPlaceFnTyped<T>>, Option<DefaultInPlaceFn>>(
                default_in_place,
            )
        };
        self
    }

    /// Sets the clone_into function for this builder.
    pub const fn clone_into(mut self, clone_into: Option<CloneIntoFnTyped<T>>) -> Self {
        self.clone_into = unsafe {
            mem::transmute::<Option<CloneIntoFnTyped<T>>, Option<CloneIntoFn>>(clone_into)
        };
        self
    }

    /// Sets the marker traits for this builder.
    pub const fn marker_traits(mut self, marker_traits: MarkerTraits) -> Self {
        self.marker_traits = marker_traits;
        self
    }

    /// Sets the partial_eq function for this builder.
    pub const fn partial_eq(mut self, partial_eq: Option<PartialEqFnTyped<T>>) -> Self {
        self.partial_eq = unsafe {
            mem::transmute::<Option<PartialEqFnTyped<T>>, Option<PartialEqFn>>(partial_eq)
        };
        self
    }

    /// Sets the partial_ord function for this builder.
    pub const fn partial_ord(mut self, partial_ord: Option<PartialOrdFnTyped<T>>) -> Self {
        self.partial_ord = unsafe {
            mem::transmute::<Option<PartialOrdFnTyped<T>>, Option<PartialOrdFn>>(partial_ord)
        };
        self
    }

    /// Sets the ord function for this builder.
    pub const fn ord(mut self, ord: Option<CmpFnTyped<T>>) -> Self {
        self.ord = unsafe { mem::transmute::<Option<CmpFnTyped<T>>, Option<CmpFn>>(ord) };
        self
    }

    /// Sets the hash function for this builder.
    pub const fn hash(mut self, hash: Option<HashFnTyped<T>>) -> Self {
        self.hash = unsafe { mem::transmute::<Option<HashFnTyped<T>>, Option<HashFn>>(hash) };
        self
    }

    /// Overwrites the drop_in_place function for this builder.
    ///
    /// This is usually not necessary, the builder builder will default this to the appropriate type.
    pub const fn drop_in_place(mut self, drop_in_place: Option<DropInPlaceFn>) -> Self {
        self.drop_in_place = drop_in_place;
        self
    }

    /// Sets the invariants function for this builder.
    pub const fn invariants(mut self, invariants: Option<InvariantsFnTyped<T>>) -> Self {
        self.invariants = unsafe {
            mem::transmute::<Option<InvariantsFnTyped<T>>, Option<InvariantsFn>>(invariants)
        };
        self
    }

    /// Sets the parse function for this builder.
    pub const fn parse(mut self, parse: Option<ParseFnTyped<T>>) -> Self {
        self.parse = unsafe { mem::transmute::<Option<ParseFnTyped<T>>, Option<ParseFn>>(parse) };
        self
    }

    /// Sets the try_from function for this builder.
    pub const fn try_from(mut self, try_from: Option<TryFromFnTyped<T>>) -> Self {
        self.try_from =
            unsafe { mem::transmute::<Option<TryFromFnTyped<T>>, Option<TryFromFn>>(try_from) };
        self
    }

    /// Sets the try_into_inner function for this builder.
    pub const fn try_into_inner(mut self, try_into_inner: Option<TryIntoInnerFnTyped<T>>) -> Self {
        self.try_into_inner = unsafe {
            mem::transmute::<Option<TryIntoInnerFnTyped<T>>, Option<TryIntoInnerFn>>(try_into_inner)
        };
        self
    }

    /// Sets the borrow_inner function for this builder.
    pub const fn try_borrow_inner(
        mut self,
        try_borrow_inner: Option<TryBorrowInnerFnTyped<T>>,
    ) -> Self {
        self.try_borrow_inner = unsafe {
            mem::transmute::<Option<TryBorrowInnerFnTyped<T>>, Option<TryBorrowInnerFn>>(
                try_borrow_inner,
            )
        };
        self
    }

    /// Builds the [`ValueVTable`] from the current state of the builder.
    pub const fn build(self) -> ValueVTable {
        ValueVTable {
            type_name: self.type_name.unwrap(),
            marker_traits: self.marker_traits,
            invariants: self.invariants,
            display: self.display,
            debug: self.debug,
            default_in_place: self.default_in_place,
            clone_into: self.clone_into,
            partial_eq: self.partial_eq,
            partial_ord: self.partial_ord,
            ord: self.ord,
            hash: self.hash,
            parse: self.parse,
            try_from: self.try_from,
            try_into_inner: self.try_into_inner,
            try_borrow_inner: self.try_borrow_inner,
            drop_in_place: self.drop_in_place,
        }
    }
}
