extern crate proc_macro2;
extern crate quote;
extern crate syn;

use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::{Attribute, DataEnum, Expr, Field, Fields, Generics};

use crate::{
    attributes,
    field_attrs::{extract_idx_attr, extract_with_attr},
    shared::build_ident,
};

pub fn enum_serializer(
    data_enum: &DataEnum,
    name: &Ident,
    static_size: Option<Expr>,
    min_size: Option<Expr>,
    generics: Generics,
    is_debug: bool,
    attrs: &[Attribute],
) -> proc_macro::TokenStream {
    let variants_len = data_enum.variants.len();

    let mut enum_sizes: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut min_sizes: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut size: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut check_bytes: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut from_slice: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut debug_from_slice: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut from_slice_unchecked: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut from_slice_unsafe: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut try_convert_to_bytes: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut convert_to_bytes: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut convert_to_bytes_ctx: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut from_slice_ctx: Vec<TokenStream> = Vec::with_capacity(variants_len);
    let mut size_ctx: Vec<TokenStream> = Vec::with_capacity(variants_len);

    let variants_iter = data_enum.variants.iter().enumerate().map(|(idx, variant)| {
        let id: u8 = extract_idx_attr(&variant.attrs)
            .map(|idx| idx as u8)
            .unwrap_or(idx as u8);

        (id, variant)
    });

    for (variant_id, variant) in variants_iter {
        let variant_name = &variant.ident;

        match &variant.fields {
            Fields::Unit => {
                from_slice.push(quote! {
                    #variant_id => {
                        Ok(#name::#variant_name)
                    }
                });
                check_bytes.push(quote! {
                    #variant_id => {}
                });
                from_slice_unchecked.push(quote! {
                    #variant_id => {
                        Ok(#name::#variant_name)
                    }
                });
                from_slice_unsafe.push(quote! {
                    #variant_id => {
                        Ok(#name::#variant_name)
                    }
                });
                try_convert_to_bytes.push(quote! {
                    #name::#variant_name => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                    }
                });
                convert_to_bytes.push(quote! {
                    #name::#variant_name => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                    }
                });
                size.push(quote! {
                    #name::#variant_name => 0,
                });
                enum_sizes.push(quote! {
                    None,
                });
                min_sizes.push(quote! {
                    0,
                });
                debug_from_slice.push(quote! {
                    #variant_id => {
                        println!("Variant: {}::{}", stringify!(#name), stringify!(#variant_name));
                        Ok(#name::#variant_name)
                    }
                });
                convert_to_bytes_ctx.push(quote! {
                    #name::#variant_name => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                    }
                });
                from_slice_ctx.push(quote! {
                    #variant_id => {
                        Ok(#name::#variant_name)
                    }
                });
                size_ctx.push(quote! {
                    #name::#variant_name => 0,
                });
            }
            Fields::Unnamed(fields) => {
                let len = fields.unnamed.len();
                let fields = &fields.unnamed;

                let mut field_names: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_static_sizes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_min_sizes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_size: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_debug_from_slice: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_check_bytes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice_unchecked: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice_unsafe: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_try_convert_to_bytes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_convert_to_bytes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_convert_to_bytes_ctx: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice_ctx: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_size_ctx: Vec<TokenStream> = Vec::with_capacity(len);

                for (idx, field) in fields.iter().enumerate() {
                    let typ = &field.ty;
                    let field_name = syn::Ident::new(&format!("arg{idx}"), Span::call_site());
                    let with_attr = extract_with_attr(&field.attrs);

                    field_names.push(quote! { #field_name, });

                    match with_attr {
                        Some(with_attr) => {
                            fields_static_sizes.push(quote! {
                                #with_attr::static_size(core::marker::PhantomData::<#typ>),
                            });
                            fields_min_sizes.push(quote! {
                                #with_attr::min_size(core::marker::PhantomData::<#typ>),
                            });
                            fields_size.push(
                                quote! { + (match #with_attr::static_size(core::marker::PhantomData::<#typ>) {
                                    Some(s) => s,
                                    None => #with_attr::size(#field_name)
                                }) },
                            );
                            fields_check_bytes.push(quote! {
                                #with_attr::check_bytes(core::marker::PhantomData::<#typ>, __rapira_slice)?;
                            });
                            fields_from_slice.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice(__rapira_slice)?;
                            });
                            fields_debug_from_slice.push(quote! {
                                let len = __rapira_slice.len();
                                println!("Field: unnamed (index {}), Type: {}", #idx, stringify!(#typ));
                                let res = #with_attr::from_slice(__rapira_slice).inspect(|v| {
                                    println!("len: {len}, unnamed (index {}): {v:?}", #idx);
                                }).inspect_err(|err| {
                                    println!("len: {len}, err: {err:?}");
                                });
                                let #field_name: #typ = res?;
                            });
                            fields_from_slice_unchecked.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice_unchecked(__rapira_slice)?;
                            });
                            fields_from_slice_unsafe.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice_unsafe(__rapira_slice)?;
                            });
                            fields_try_convert_to_bytes.push(quote! {
                                #with_attr::try_convert_to_bytes(#field_name, __rapira_slice, __rapira_cursor)?;
                            });
                            fields_convert_to_bytes.push(quote! {
                                #with_attr::convert_to_bytes(#field_name, __rapira_slice, __rapira_cursor);
                            });
                            fields_convert_to_bytes_ctx.push(quote! {
                                #with_attr::convert_to_bytes(#field_name, __rapira_slice, __rapira_cursor);
                            });
                            fields_from_slice_ctx.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice(__rapira_slice)?;
                            });
                            fields_size_ctx.push(quote! { + (match #with_attr::static_size(core::marker::PhantomData::<#typ>) {
                                Some(s) => s,
                                None => #with_attr::size(#field_name)
                            }) });
                        }
                        None => {
                            fields_static_sizes.push(quote! {
                                <#typ as rapira::Rapira>::STATIC_SIZE,
                            });
                            fields_min_sizes.push(quote! {
                                <#typ as rapira::Rapira>::MIN_SIZE,
                            });
                            fields_size.push(
                                quote! { + (match <#typ as rapira::Rapira>::STATIC_SIZE {
                                    Some(s) => s,
                                    None => #field_name.size()
                                }) },
                            );
                            fields_from_slice.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice(__rapira_slice)?;
                            });
                            fields_debug_from_slice.push(quote! {
                                let len = __rapira_slice.len();
                                println!("Field: unnamed (index {}), Type: {}", #idx, stringify!(#typ));
                                let res = <#typ as rapira::Rapira>::from_slice(__rapira_slice).inspect(|v| {
                                    println!("len: {len}, unnamed: {v:?}");
                                }).inspect_err(|err| {
                                    println!("len: {len}, err: {err:?}");
                                });
                                let #field_name: #typ = res?;
                            });
                            fields_check_bytes.push(quote! {
                                <#typ as rapira::Rapira>::check_bytes(__rapira_slice)?;
                            });
                            fields_from_slice_unchecked.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice_unchecked(__rapira_slice)?;
                            });
                            fields_from_slice_unsafe.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice_unsafe(__rapira_slice)?;
                            });
                            fields_try_convert_to_bytes.push(quote! {
                                #field_name.try_convert_to_bytes(__rapira_slice, __rapira_cursor)?;
                            });
                            fields_convert_to_bytes.push(quote! {
                                #field_name.convert_to_bytes(__rapira_slice, __rapira_cursor);
                            });
                            fields_convert_to_bytes_ctx.push(quote! {
                                #field_name.convert_to_bytes_ctx(__rapira_slice, __rapira_cursor, __rapira_flags);
                            });
                            fields_from_slice_ctx.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice_ctx(__rapira_slice, __rapira_flags)?;
                            });
                            fields_size_ctx.push(
                                quote! { + (match <#typ as rapira::Rapira>::STATIC_SIZE {
                                    Some(s) => s,
                                    None => #field_name.size_ctx(__rapira_flags)
                                }) },
                            );
                        }
                    }
                }

                size.push(quote! {
                    #name::#variant_name(#(#field_names)*) => {
                        0 #(#fields_size)*
                    },
                });
                enum_sizes.push(quote! {
                    rapira::static_size([#(#fields_static_sizes)*]),
                });
                min_sizes.push(quote! {
                    rapira::min_size(&[#(#fields_min_sizes)*]),
                });
                from_slice.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice)*
                        Ok(#name::#variant_name(#(#field_names)*))
                    }
                });
                debug_from_slice.push(quote! {
                    #variant_id => {
                        println!("Variant: {}::{}", stringify!(#name), stringify!(#variant_name));
                        #(#fields_debug_from_slice)*
                        Ok(#name::#variant_name(#(#field_names)*))
                    }
                });
                check_bytes.push(quote! {
                    #variant_id => {
                        #(#fields_check_bytes)*
                    }
                });
                from_slice_unchecked.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice_unchecked)*
                        Ok(#name::#variant_name(#(#field_names)*))
                    }
                });

                from_slice_unsafe.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice_unsafe)*
                        Ok(#name::#variant_name(#(#field_names)*))
                    }
                });

                try_convert_to_bytes.push(quote! {
                    #name::#variant_name(#(#field_names)*) => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                        #(#fields_try_convert_to_bytes)*
                    }
                });

                convert_to_bytes.push(quote! {
                    #name::#variant_name(#(#field_names)*) => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                        #(#fields_convert_to_bytes)*
                    }
                });

                convert_to_bytes_ctx.push(quote! {
                    #name::#variant_name(#(#field_names)*) => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                        #(#fields_convert_to_bytes_ctx)*
                    }
                });
                from_slice_ctx.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice_ctx)*
                        Ok(#name::#variant_name(#(#field_names)*))
                    }
                });
                size_ctx.push(quote! {
                    #name::#variant_name(#(#field_names)*) => {
                        0 #(#fields_size_ctx)*
                    },
                });
            }
            Fields::Named(fields) => {
                let len = fields.named.len();
                let fields = &fields.named;

                let mut fields_insert: Vec<(Field, u32)> = Vec::with_capacity(len);
                let mut seq = 0u32;

                for field in fields.iter() {
                    let field_idx = extract_idx_attr(&field.attrs);
                    let field_idx = field_idx.unwrap_or_else(|| {
                        let current_seq = seq;
                        seq += 1;
                        current_seq
                    });

                    fields_insert.push((field.clone(), field_idx));
                }

                fields_insert.sort_by_key(|(_, idx_a)| *idx_a);

                let mut field_names: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_check_bytes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice_unchecked: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice_unsafe: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_try_convert_to_bytes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_convert_to_bytes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_size: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_static_sizes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_min_sizes: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_debug_from_slice: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_convert_to_bytes_ctx: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_from_slice_ctx: Vec<TokenStream> = Vec::with_capacity(len);
                let mut fields_size_ctx: Vec<TokenStream> = Vec::with_capacity(len);

                for field in fields_insert.iter().map(|(f, _)| f) {
                    let typ = &field.ty;
                    let field_name = field.ident.as_ref().unwrap();
                    let with_attr = extract_with_attr(&field.attrs);

                    field_names.push(quote! { #field_name, });

                    match with_attr {
                        Some(with_attr) => {
                            fields_static_sizes.push(quote! {
                                #with_attr::static_size(core::marker::PhantomData::<#typ>),
                            });
                            fields_min_sizes.push(quote! {
                                #with_attr::min_size(core::marker::PhantomData::<#typ>),
                            });
                            fields_size.push(
                                quote! { + (match #with_attr::static_size(core::marker::PhantomData::<#typ>) {
                                    Some(s) => s,
                                    None => #with_attr::size(#field_name)
                                }) },
                            );
                            fields_check_bytes.push(quote! {
                                #with_attr::check_bytes(core::marker::PhantomData::<#typ>, __rapira_slice)?;
                            });
                            fields_from_slice.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice(__rapira_slice)?;
                            });
                            fields_debug_from_slice.push(quote! {
                                let len = __rapira_slice.len();
                                println!("Field: {}, Type: {}", stringify!(#field_name), stringify!(#typ));
                                let res = #with_attr::from_slice(__rapira_slice).inspect(|v| {
                                    println!("len: {len}, {}: {v:?}", stringify!(#field_name));
                                }).inspect_err(|err| {
                                    println!("len: {len}, err: {err:?}");
                                });
                                let #field_name: #typ = res?;
                            });
                            fields_from_slice_unchecked.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice_unchecked(__rapira_slice)?;
                            });
                            fields_from_slice_unsafe.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice_unsafe(__rapira_slice)?;
                            });
                            fields_try_convert_to_bytes.push(quote! {
                                #with_attr::try_convert_to_bytes(#field_name, __rapira_slice, __rapira_cursor)?;
                            });
                            fields_convert_to_bytes.push(quote! {
                                #with_attr::convert_to_bytes(#field_name, __rapira_slice, __rapira_cursor);
                            });
                            fields_convert_to_bytes_ctx.push(quote! {
                                #with_attr::convert_to_bytes(#field_name, __rapira_slice, __rapira_cursor);
                            });
                            fields_from_slice_ctx.push(quote! {
                                let #field_name: #typ = #with_attr::from_slice(__rapira_slice)?;
                            });
                            fields_size_ctx.push(quote! { + (match #with_attr::static_size(core::marker::PhantomData::<#typ>) {
                                Some(s) => s,
                                None => #with_attr::size(#field_name)
                            }) });
                        }
                        None => {
                            fields_from_slice.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice(__rapira_slice)?;
                            });
                            fields_debug_from_slice.push(quote! {
                                let len = __rapira_slice.len();
                                println!("Field: {}, Type: {}", stringify!(#field_name), stringify!(#typ));
                                let res = <#typ as rapira::Rapira>::from_slice(__rapira_slice).inspect(|v| {
                                    println!("len: {len}, {}: {v:?}", stringify!(#field_name));
                                }).inspect_err(|err| {
                                    println!("len: {len}, err: {err:?}");
                                });
                                let #field_name = res?;
                            });
                            fields_check_bytes.push(quote! {
                                <#typ as rapira::Rapira>::check_bytes(__rapira_slice)?;
                            });
                            fields_from_slice_unchecked.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice_unchecked(__rapira_slice)?;
                            });
                            fields_from_slice_unsafe.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice_unsafe(__rapira_slice)?;
                            });
                            fields_try_convert_to_bytes.push(quote! {
                                #field_name.try_convert_to_bytes(__rapira_slice, __rapira_cursor)?;
                            });
                            fields_convert_to_bytes.push(quote! {
                                #field_name.convert_to_bytes(__rapira_slice, __rapira_cursor);
                            });
                            fields_size.push(
                                quote! { + (match <#typ as rapira::Rapira>::STATIC_SIZE {
                                    Some(s) => s,
                                    None => #field_name.size()
                                }) },
                            );
                            fields_static_sizes.push(quote! {
                                <#typ as rapira::Rapira>::STATIC_SIZE,
                            });
                            fields_min_sizes.push(quote! {
                                <#typ as rapira::Rapira>::MIN_SIZE,
                            });
                            fields_convert_to_bytes_ctx.push(quote! {
                                #field_name.convert_to_bytes_ctx(__rapira_slice, __rapira_cursor, __rapira_flags);
                            });
                            fields_from_slice_ctx.push(quote! {
                                let #field_name = <#typ as rapira::Rapira>::from_slice_ctx(__rapira_slice, __rapira_flags)?;
                            });
                            fields_size_ctx.push(
                                quote! { + (match <#typ as rapira::Rapira>::STATIC_SIZE {
                                    Some(s) => s,
                                    None => #field_name.size_ctx(__rapira_flags)
                                }) },
                            );
                        }
                    }
                }

                size.push(quote! {
                    #name::#variant_name{#(#field_names)*} => {
                        0 #(#fields_size)*
                    },
                });
                enum_sizes.push(quote! {
                    rapira::static_size([#(#fields_static_sizes)*]),
                });
                min_sizes.push(quote! {
                    rapira::min_size(&[#(#fields_min_sizes)*]),
                });
                from_slice.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice)*
                        Ok(#name::#variant_name{#(#field_names)*})
                    }
                });
                debug_from_slice.push(quote! {
                    #variant_id => {
                        println!("Variant: {}::{}", stringify!(#name), stringify!(#variant_name));
                        #(#fields_debug_from_slice)*
                        Ok(#name::#variant_name{#(#field_names)*})
                    }
                });
                check_bytes.push(quote! {
                    #variant_id => {
                        #(#fields_check_bytes)*
                    }
                });
                from_slice_unchecked.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice_unchecked)*
                        Ok(#name::#variant_name{#(#field_names)*})
                    }
                });

                from_slice_unsafe.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice_unsafe)*
                        Ok(#name::#variant_name{#(#field_names)*})
                    }
                });

                try_convert_to_bytes.push(quote! {
                    #name::#variant_name{#(#field_names)*} => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                        #(#fields_try_convert_to_bytes)*
                    }
                });

                convert_to_bytes.push(quote! {
                    #name::#variant_name{#(#field_names)*} => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                        #(#fields_convert_to_bytes)*
                    }
                });

                convert_to_bytes_ctx.push(quote! {
                    #name::#variant_name{#(#field_names)*} => {
                        rapira::push(__rapira_slice, __rapira_cursor, #variant_id);
                        #(#fields_convert_to_bytes_ctx)*
                    }
                });
                from_slice_ctx.push(quote! {
                    #variant_id => {
                        #(#fields_from_slice_ctx)*
                        Ok(#name::#variant_name{#(#field_names)*})
                    }
                });
                size_ctx.push(quote! {
                    #name::#variant_name{#(#field_names)*} => {
                        0 #(#fields_size_ctx)*
                    },
                });
            }
        }
    }

    let static_size = match static_size {
        Some(static_size) => {
            quote! {
                #static_size
            }
        }
        None => {
            quote! {
                rapira::enum_size([#(#enum_sizes)*])
            }
        }
    };
    let min_size = match min_size {
        Some(min_size) => {
            quote! {
                #min_size
            }
        }
        None => {
            quote! {
                rapira::enum_min_size(&[#(#min_sizes)*])
            }
        }
    };

    #[cfg(feature = "schema")]
    let is_concrete = generics.params.is_empty();
    let name_with_generics = build_ident(name, generics);

    #[cfg(feature = "schema")]
    let peer_method = if is_concrete && attributes::peer_attr(attrs) {
        let string_name = name.to_string();
        use crate::peer::{EnumVariantPeer, gen_enum_peer};
        let schema_path = crate::crate_path::schema_crate_path();
        let variants: Vec<EnumVariantPeer<'_>> = data_enum
            .variants
            .iter()
            .map(|v| EnumVariantPeer {
                ident: &v.ident,
                name: v.ident.to_string(),
                fields: &v.fields,
            })
            .collect();
        gen_enum_peer(name, &string_name, &schema_path, &variants)
    } else {
        TokenStream::new()
    };

    #[cfg(not(feature = "schema"))]
    let peer_method = TokenStream::new();

    let debug_parse = if is_debug {
        quote! {
            /// Deserializes a value from a byte __rapira_slice with debug logging.
            /// This method logs the enum variant name, field names, types, and values during deserialization.
            /// Useful for debugging serialization/deserialization issues.
            #[inline]
            fn debug_from_slice(__rapira_slice: &mut &[u8]) -> rapira::Result<Self>
            where
                Self: Sized + std::fmt::Debug,
            {
                let val: u8 = rapira::byte_rapira::from_slice(__rapira_slice)?;
                match val {
                    #(#debug_from_slice)*
                    _ => Err(rapira::RapiraError::EnumVariant),
                }
            }
        }
    } else {
        quote!()
    };

    let res = quote! {
        #name_with_generics {
            const STATIC_SIZE: Option<usize> = #static_size;
            const MIN_SIZE: usize = #min_size;

            #[inline]
            fn from_slice(__rapira_slice: &mut &[u8]) -> rapira::Result<Self>
            where
                Self: Sized,
            {
                let val: u8 = rapira::byte_rapira::from_slice(__rapira_slice)?;
                match val {
                    #(#from_slice)*
                    _ => Err(rapira::RapiraError::EnumVariant),
                }
            }

            #debug_parse

            #[inline]
            fn check_bytes(__rapira_slice: &mut &[u8]) -> rapira::Result<()>
            where
                Self: Sized,
            {
                let val: u8 = rapira::byte_rapira::from_slice(__rapira_slice)?;
                match val {
                    #(#check_bytes)*
                    _ => return Err(rapira::RapiraError::EnumVariant),
                }
                Ok(())
            }

            #[inline]
            unsafe fn from_slice_unchecked(__rapira_slice: &mut &[u8]) -> rapira::Result<Self>
            where
                Self: Sized,
            {
                let val: u8 = rapira::byte_rapira::from_slice(__rapira_slice)?;
                match val {
                    #(#from_slice_unchecked)*
                    _ => Err(rapira::RapiraError::EnumVariant),
                }
            }

            #[inline]
            unsafe fn from_slice_unsafe(__rapira_slice: &mut &[u8]) -> rapira::Result<Self>
            where
                Self: Sized,
            {
                let val: u8 = rapira::byte_rapira::from_slice_unsafe(__rapira_slice)?;
                match val {
                    #(#from_slice_unsafe)*
                    _ => Err(rapira::RapiraError::EnumVariant),
                }
            }

            #[inline]
            fn try_convert_to_bytes(&self, __rapira_slice: &mut [u8], __rapira_cursor: &mut usize) -> rapira::Result<()> {
                match self {
                    #(#try_convert_to_bytes)*
                }
                Ok(())
            }

            #[inline]
            fn convert_to_bytes(&self, __rapira_slice: &mut [u8], __rapira_cursor: &mut usize) {
                match self {
                    #(#convert_to_bytes)*
                }
            }

            #[inline]
            fn size(&self) -> usize {
                1 + match self {
                    #(#size)*
                }
            }

            #[inline]
            fn convert_to_bytes_ctx(&self, __rapira_slice: &mut [u8], __rapira_cursor: &mut usize, __rapira_flags: rapira::RapiraFlags) {
                match self {
                    #(#convert_to_bytes_ctx)*
                }
            }

            #[inline]
            fn from_slice_ctx(__rapira_slice: &mut &[u8], __rapira_flags: rapira::RapiraFlags) -> rapira::Result<Self>
            where
                Self: Sized,
            {
                let val: u8 = rapira::byte_rapira::from_slice(__rapira_slice)?;
                match val {
                    #(#from_slice_ctx)*
                    _ => Err(rapira::RapiraError::EnumVariant),
                }
            }

            #[inline]
            fn size_ctx(&self, __rapira_flags: rapira::RapiraFlags) -> usize {
                1 + match self {
                    #(#size_ctx)*
                }
            }

            #peer_method
        }
    };

    proc_macro::TokenStream::from(res)
}
