#[macro_export]
macro_rules! mock {
    (
        $mock: ident;

        impl $mocked_trait: ident for $mocked_trait_target: ident {
            $(
                fn $func: ident$(<
                    $($type_param: tt$(: ($($type_param_bound: tt +)+))?),*
                >)?(
                    self: ($($self_type: tt)+),
                    $($func_param: ident: $func_param_type: ty),*
                )$( -> $return_type: ty)?
                $(where $(
                    $where_param: ident: $first_where_param_bound: tt $(+ $where_param_bound: tt)*
                ),*)?;
            )*
        }
    ) => {
        $crate::__private::paste! {
        mod [<__ $mock>] {
        use super::*;

        pub struct $mock {
            $(
                [<$func _expectations>]: std::collections::HashMap<
                    Vec<$crate::__private::type_id::TypeID>,
                    [<$mock Expectation _ $func>]$(<$($crate::__to_unit!($type_param)),*>)?
                >,
            )*
        }

        impl $mock
        {
            pub fn new() -> Self
            {
                Self {
                    $(
                        [<$func _expectations>]: std::collections::HashMap::new()
                    ),*
                }
            }

            $(
                #[allow(unused)]
                pub(crate) fn [<expect_ $func>]$(<
                    $($type_param$(: $($type_param_bound +)*)?),*
                >)?(&mut self)
                    -> &mut [<$mock Expectation _ $func>]$(<$($type_param),*>)?
                $(where $(
                    $where_param: $first_where_param_bound $(+ $where_param_bound)*
                ),*)?
                {
                    let ids = vec![
                        $($($crate::__private::type_id::TypeID::of::<$type_param>()),*)?
                    ];

                    let expectation =
                        [<$mock Expectation _ $func>]$(::<$($type_param),*>)?::new()
                            .strip_type_params();

                    self.[<$func _expectations>].insert(ids.clone(), expectation);

                    self.[<$func _expectations>]
                        .get_mut(&ids)
                        .unwrap()
                        .with_type_params_mut()
                }
            )*
        }

        impl $mocked_trait for $mock {
            $(
                fn $func$(<$($type_param$(: $($type_param_bound +)+)?),*>)?(
                    self: $($self_type)+,
                    $($func_param: $func_param_type),*
                )$( -> $return_type)?
                $(where $(
                    $where_param: $first_where_param_bound $(+ $where_param_bound)*
                ),*)?
                {
                    let ids = vec![
                        $($($crate::__private::type_id::TypeID::of::<$type_param>()),*)?
                    ];

                    let expectation = self
                        .[<$func _expectations>]
                        .get(&ids)
                        .expect(concat!(
                            "No expectation found for function ",
                            stringify!($func)
                        ))
                        .with_type_params$(::<$($type_param),*>)?();

                    let Some(returning) = &expectation.returning else {
                        panic!(concat!(
                            "Expectation for function",
                            stringify!($func),
                            " is missing a function to call")
                        );
                    };

                    returning(self, $($func_param),*)
                }
            )*
        }

        $(
            #[allow(non_camel_case_types, non_snake_case)]
            pub struct [<$mock Expectation _ $func>]$(<$($type_param),*>)? {
                returning: Option<
                    fn(
                        $crate::__replace_ref_type!($($self_type)*, $mock),
                        $($func_param_type),*
                    )$( -> $return_type)?>,

                $(
                    $([<$type_param _phantom>]: std::marker::PhantomData<$type_param>,)*
                )?
            }

            impl$(<$($type_param),*>)? [<$mock Expectation _ $func>]$(<
                $($type_param),*
            >)? {
                #[allow(unused)]
                fn new() -> Self {
                    Self {
                        returning: None,
                        $(
                            $([<$type_param _phantom>]: std::marker::PhantomData,)*
                        )?
                    }
                }

                #[allow(unused)]
                pub fn returning(
                    &mut self,
                    func: fn(
                        $crate::__replace_ref_type!($($self_type)*, $mock),
                        $($func_param_type),*
                    )$( -> $return_type)?
                ) -> &mut Self
                {
                    self.returning = Some(func);

                    self
                }

                #[allow(unused)]
                fn strip_type_params(
                    self,
                ) -> [<$mock Expectation _ $func>]$(<$($crate::__to_unit!($type_param)),*>)?
                {
                    $crate::__if_empty_else!(($($($type_param)*)?); {
                        // No type parameters are present
                        self
                    }, {
                        // Type parameters are present
                        unsafe { std::mem::transmute(self) }
                    })

                }
            }

            impl [<$mock Expectation _ $func>]$(<$($crate::__to_unit!($type_param)),*>)? {
                fn with_type_params$(<$($type_param),*>)?(
                    &self,
                ) -> &[<$mock Expectation _ $func>]$(<$($type_param),*>)?
                {
                    unsafe { &*(self as *const Self).cast() }
                }

                #[allow(unused)]
                fn with_type_params_mut$(<$($type_param),*>)?(
                    &mut self,
                ) -> &mut [<$mock Expectation _ $func>]$(<$($type_param),*>)?
                {
                    unsafe { &mut *(self as *mut Self).cast() }
                }
            }
        )*
        }

        use [<__ $mock>]::$mock;
        }
    };
}

#[doc(hidden)]
pub mod __private
{
    pub use paste::paste;

    pub mod type_id
    {
        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
        pub struct TypeID
        {
            id: usize,
        }

        impl TypeID
        {
            #[inline]
            pub fn of<T>() -> Self
            {
                Self {
                    id: Self::of::<T> as usize,
                }
            }
        }
    }

    #[macro_export]
    #[doc(hidden)]
    macro_rules! __to_unit {
        ($anything: tt) => {
            ()
        };
    }

    #[macro_export]
    #[doc(hidden)]
    macro_rules! __replace_ref_type {
        (&$old_type: tt, $new_type: tt) => {
            &$new_type
        };

        (&mut $old_type: tt, $new_type: tt) => {
            &mut $new_type
        };
    }

    #[macro_export]
    #[doc(hidden)]
    macro_rules! __if_empty_else {
        (($($input: tt)*); $if_empty: block, $else: block) => {
            $crate::__if_empty_else!(@($($input)*); $if_empty, $else)
        };

        // Empty
        (@(); $if_empty: block, $else: block) => {
            $if_empty
        };

        // Not empty
        (@($($input: tt)+); $if_empty: block, $else: block) => {
            $else
        };
    }
}