// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use super::util::{enum_variant_id, is_default_value_variant, is_skip_enum_variant};
use crate::object::misc;
use crate::object::read::gen_read_field;
use crate::object::util::{get_filtered_fields_iter, get_sorted_field_names};
use crate::object::write::gen_write_field;
use crate::util::{extract_fields, source_fields};
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{DataEnum, Fields};
fn temp_var_name(i: usize) -> String {
    format!("f{}", i)
}

/// For Union-compatible enums with data variants, return UNION TypeId in xlang mode.
pub fn gen_actual_type_id(data_enum: &DataEnum) -> TokenStream {
    let is_union_compatible = is_union_compatible_enum(data_enum);
    let has_data_variants = data_enum
        .variants
        .iter()
        .any(|v| !matches!(v.fields, Fields::Unit));

    if is_union_compatible && has_data_variants {
        // Union-compatible enum: use typed/named union IDs in xlang mode
        quote! {
            if xlang {
                if register_by_name {
                    fory_core::types::TypeId::NAMED_UNION as u32
                } else {
                    fory_core::types::TypeId::TYPED_UNION as u32
                }
            } else {
                fory_core::serializer::enum_::actual_type_id(type_id, register_by_name, compatible)
            }
        }
    } else {
        quote! {
            let _ = xlang;
            fory_core::serializer::enum_::actual_type_id(type_id, register_by_name, compatible)
        }
    }
}

pub fn gen_field_fields_info(_data_enum: &DataEnum) -> TokenStream {
    quote! {
        Ok(Vec::new())
    }
}

pub fn gen_variants_fields_info(enum_name: &syn::Ident, data_enum: &DataEnum) -> TokenStream {
    let variant_info: Vec<TokenStream> = data_enum
        .variants
        .iter()
        .map(|v| {
            let variant_name = v.ident.to_string();
            match &v.fields {
                Fields::Named(_fields_named) => {
                    // Generate meta type identifier for this named variant
                    let meta_type_ident = Ident::new(
                        &format!("{}_{}VariantMeta", enum_name, v.ident),
                        proc_macro2::Span::call_site()
                    );
                    quote! {
                        (
                            #variant_name.to_string(),
                            std::any::TypeId::of::<#meta_type_ident>(),
                            <#meta_type_ident as fory_core::serializer::enum_::NamedEnumVariantMetaTrait>::fory_fields_info(type_resolver)?
                        )
                    }
                }
                _ => {
                    // Unit or unnamed variants - return empty field info
                    quote! {
                        (
                            #variant_name.to_string(),
                            std::any::TypeId::of::<()>(), // Placeholder type ID
                            Vec::new()
                        )
                    }
                }
            }
        })
        .collect();

    quote! {
        Ok(vec![
            #(#variant_info),*
        ])
    }
}

pub fn gen_reserved_space() -> TokenStream {
    quote! {
       4
    }
}

/// Generate all variant meta types for an enum with the enum name
pub(crate) fn gen_all_variant_meta_types_with_enum_name(
    enum_name: &syn::Ident,
    data_enum: &DataEnum,
) -> Vec<TokenStream> {
    data_enum
        .variants
        .iter()
        .filter_map(|v| {
            if let Fields::Named(fields_named) = &v.fields {
                let ident = &v.ident;
                Some(gen_named_variant_meta_type_impl_with_enum_name(
                    enum_name,
                    ident,
                    fields_named,
                ))
            } else {
                None
            }
        })
        .collect()
}

/// Generate a meta type that implements NamedEnumVariantMetaTrait for a named variant
/// with enum name to avoid collisions
pub(crate) fn gen_named_variant_meta_type_impl_with_enum_name(
    enum_ident: &Ident,
    variant_ident: &Ident,
    fields: &syn::FieldsNamed,
) -> TokenStream {
    let fields_clone = syn::Fields::Named(fields.clone());
    let source_fields = source_fields(&fields_clone);
    let fields_slice = extract_fields(&source_fields);
    let filtered_fields: Vec<_> = get_filtered_fields_iter(&fields_slice).collect();
    let sorted_field_names_vec = get_sorted_field_names(&filtered_fields);

    // Generate individual field name literals
    let field_name_literals: Vec<_> = sorted_field_names_vec
        .iter()
        .map(|name| {
            quote! { #name }
        })
        .collect();

    let fields_info_ts = misc::gen_field_fields_info(&source_fields);

    // Include enum name to make meta type unique
    let meta_type_ident = Ident::new(
        &format!("{}_{}VariantMeta", enum_ident, variant_ident),
        proc_macro2::Span::call_site(),
    );

    quote! {
        struct #meta_type_ident;

        impl fory_core::serializer::enum_::NamedEnumVariantMetaTrait for #meta_type_ident {
            fn fory_get_sorted_field_names() -> &'static [&'static str] {
                &[#(#field_name_literals),*]
            }

            fn fory_fields_info(type_resolver: &fory_core::TypeResolver) -> Result<Vec<fory_core::meta::FieldInfo>, fory_core::error::Error> {
                #fields_info_ts
            }
        }
    }
}

pub fn gen_write(_data_enum: &DataEnum) -> TokenStream {
    quote! {
        fory_core::serializer::enum_::write::<Self>(self, context, ref_mode, write_type_info)
    }
}

fn xlang_variant_branches(data_enum: &DataEnum, default_variant_value: u32) -> Vec<TokenStream> {
    let is_union_compatible = is_union_compatible_enum(data_enum);

    data_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, v)| {
            let ident = &v.ident;
            let mut tag_value = if is_union_compatible {
                enum_variant_id(v).unwrap_or(idx as u32)
            } else {
                idx as u32
            };
            if is_skip_enum_variant(v) {
                tag_value = default_variant_value;
            }

            match &v.fields {
                Fields::Unit => {
                    if is_union_compatible {
                        // Union-compatible: write tag + null flag (matches Java/C++ Union with null value)
                        quote! {
                            Self::#ident => {
                                context.writer.write_var_uint32(#tag_value);
                                // Write null flag for unit variant (no value)
                                context.writer.write_i8(fory_core::types::RefFlag::Null as i8);
                            }
                        }
                    } else {
                        quote! {
                            Self::#ident => {
                                context.writer.write_var_uint32(#tag_value);
                            }
                        }
                    }
                }
                Fields::Unnamed(fields_unnamed) => {
                    if is_union_compatible && fields_unnamed.unnamed.len() == 1 {
                        // Union-compatible single field: write tag + value with type info (like xwriteRef)
                        quote! {
                            Self::#ident(ref value) => {
                                context.writer.write_var_uint32(#tag_value);
                                use fory_core::serializer::Serializer;
                                value.fory_write(context, fory_core::types::RefMode::Tracking, true, false)?;
                            }
                        }
                    } else {
                        quote! {
                            Self::#ident(..) => {
                                context.writer.write_var_uint32(#tag_value);
                            }
                        }
                    }
                }
                Fields::Named(fields_named) => {
                    if is_union_compatible && fields_named.named.len() == 1 {
                        // Union-compatible single field: write tag + value with type info (like xwriteRef)
                        let field_ident =
                            fields_named.named.first().unwrap().ident.as_ref().unwrap();
                        quote! {
                            Self::#ident { ref #field_ident } => {
                                context.writer.write_var_uint32(#tag_value);
                                use fory_core::serializer::Serializer;
                                #field_ident.fory_write(context, fory_core::types::RefMode::Tracking, true, false)?;
                            }
                        }
                    } else {
                        quote! {
                            Self::#ident { .. } => {
                                context.writer.write_var_uint32(#tag_value);
                            }
                        }
                    }
                }
            }
        })
        .collect()
}

fn rust_variant_branches(data_enum: &DataEnum, default_variant_value: u32) -> Vec<TokenStream> {
    data_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, v)| {
            let ident = &v.ident;
            let mut tag_value = idx as u32;
            if is_skip_enum_variant(v) {
                tag_value = default_variant_value;
            }

            match &v.fields {
                Fields::Unit => {
                    quote! {
                        Self::#ident => {
                            context.writer.write_var_uint32(#tag_value);
                        }
                    }
                }
                Fields::Unnamed(fields_unnamed) => {
                    let field_idents: Vec<_> = (0..fields_unnamed.unnamed.len())
                        .map(|i| Ident::new(&temp_var_name(i), proc_macro2::Span::call_site()))
                        .collect();

                    let write_fields: Vec<_> = fields_unnamed
                        .unnamed
                        .iter()
                        .zip(field_idents.iter())
                        .map(|(f, ident)| gen_write_field(f, ident, false))
                        .collect();

                    quote! {
                        Self::#ident( #(#field_idents),* ) => {
                            context.writer.write_var_uint32(#tag_value);
                            #(#write_fields)*
                        }
                    }
                }
                Fields::Named(fields_named) => {
                    use crate::util::source_fields;

                    let fields_clone = syn::Fields::Named(fields_named.clone());
                    let source_fields = source_fields(&fields_clone);

                    let field_idents: Vec<_> = source_fields
                        .iter()
                        .map(|sf| sf.field.ident.as_ref().unwrap())
                        .collect();

                    let write_fields: Vec<_> = source_fields
                        .iter()
                        .zip(field_idents.iter())
                        .map(|(sf, ident)| gen_write_field(sf.field, ident, false))
                        .collect();

                    quote! {
                        Self::#ident { #(#field_idents),* } => {
                            context.writer.write_var_uint32(#tag_value) ;
                            #(#write_fields)*
                        }
                    }
                }
            }
        })
        .collect()
}

fn rust_compatible_variant_write_branches(
    data_enum: &DataEnum,
    default_variant_value: u32,
) -> Vec<TokenStream> {
    use crate::object::util::get_struct_name;
    let enum_name = get_struct_name().expect("enum context not set");

    data_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, v)| {
            let ident = &v.ident;
            let mut tag_value = idx as u32;
            if is_skip_enum_variant(v) {
                tag_value = default_variant_value;
            }

            match &v.fields {
                Fields::Unit => {
                    quote! {
                        Self::#ident => {
                            context.writer.write_var_uint32((#tag_value << 2) | 0b0);
                        }
                    }
                }
                Fields::Unnamed(fields_unnamed) => {
                    // For unnamed enum variants, write using collection format (same protocol as tuple)
                    let field_idents: Vec<_> = (0..fields_unnamed.unnamed.len())
                        .map(|i| Ident::new(&temp_var_name(i), proc_macro2::Span::call_site()))
                        .collect();

                    let field_count = fields_unnamed.unnamed.len();

                    quote! {
                        Self::#ident( #(ref #field_idents),* ) => {
                            context.writer.write_var_uint32((#tag_value << 2) | 0b1);
                            // Write as collection format (same as tuple)
                            context.writer.write_var_uint32(#field_count as u32);
                            let header = 0u8; // No IS_SAME_TYPE flag
                            context.writer.write_u8(header);
                            use fory_core::serializer::Serializer;
                            #(
                                #field_idents.fory_write(context, fory_core::RefMode::NullOnly, true, false)?;
                            )*
                        }
                    }
                }
                Fields::Named(fields_named) => {
                    // Generate meta type identifier for this named variant
                    let meta_type_ident = Ident::new(
                        &format!("{}_{}VariantMeta", enum_name, ident),
                        proc_macro2::Span::call_site()
                    );
                    let fields_clone = syn::Fields::Named(fields_named.clone());
                    let source_fields = source_fields(&fields_clone);
                    let field_idents: Vec<_> = source_fields
                        .iter()
                        .map(|sf| sf.field.ident.as_ref().unwrap())
                        .collect();

                    let write_fields: Vec<_> = source_fields
                        .iter()
                        .zip(field_idents.iter())
                        .map(|(sf, ident)| gen_write_field(sf.field, ident, false))
                        .collect();

                    quote! {
                        Self::#ident { #(#field_idents),* } => {
                            context.writer.write_var_uint32((#tag_value << 2) | 0b10);
                            // Write type meta inline using streaming protocol
                            context.write_type_meta(std::any::TypeId::of::<#meta_type_ident>())?;
                            // Write fields same as struct
                            #(#write_fields)*
                        }
                    }
                }
            }
        })
        .collect()
}

pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
    let default_variant_value = data_enum
        .variants
        .iter()
        .position(is_default_value_variant)
        .unwrap_or(0) as u32;

    let xlang_variant_branches: Vec<TokenStream> =
        xlang_variant_branches(data_enum, default_variant_value);
    let rust_variant_branches: Vec<TokenStream> =
        rust_variant_branches(data_enum, default_variant_value);
    let rust_compatible_variant_branches: Vec<TokenStream> =
        rust_compatible_variant_write_branches(data_enum, default_variant_value);

    quote! {
        if context.is_xlang() {
            match self {
                #(#xlang_variant_branches)*
            }
            Ok(())
        } else {
            if context.is_compatible() {
                match self {
                    #(#rust_compatible_variant_branches)*
                }
                Ok(())
            } else {
                match self {
                    #(#rust_variant_branches)*
                }
                Ok(())
            }
        }
    }
}

pub fn gen_write_type_info(data_enum: &DataEnum) -> TokenStream {
    let is_union_compatible = is_union_compatible_enum(data_enum);
    let has_data_variants = data_enum
        .variants
        .iter()
        .any(|v| !matches!(v.fields, Fields::Unit));

    if is_union_compatible && has_data_variants {
        // Union-compatible with data: write typed/named union type info in xlang mode
        quote! {
            if context.is_xlang() {
                let rs_type_id = std::any::TypeId::of::<Self>();
                context.write_any_type_info(fory_core::types::UNKNOWN, rs_type_id)?;
                Ok(())
            } else {
                fory_core::serializer::enum_::write_type_info::<Self>(context)
            }
        }
    } else {
        quote! {
            fory_core::serializer::enum_::write_type_info::<Self>(context)
        }
    }
}

pub fn gen_read(_: &DataEnum) -> TokenStream {
    quote! {
        fory_core::serializer::enum_::read::<Self>(context, ref_mode, read_type_info)
    }
}

pub fn gen_read_with_type_info(_: &DataEnum) -> TokenStream {
    quote! {
        fory_core::serializer::enum_::read::<Self>(context, ref_mode, false)
    }
}

/// Check if enum is Union-compatible:
/// - Must have at least one data-carrying variant (single-field)
/// - All variants must be either unit or single-field
fn is_union_compatible_enum(data_enum: &DataEnum) -> bool {
    let has_data_variant = data_enum
        .variants
        .iter()
        .any(|v| !matches!(v.fields, Fields::Unit));
    let all_variants_compatible = data_enum.variants.iter().all(|v| match &v.fields {
        Fields::Unit => true,
        Fields::Unnamed(f) => f.unnamed.len() == 1,
        Fields::Named(f) => f.named.len() == 1,
    });

    has_data_variant && all_variants_compatible
}

/// Generate the static TypeId for enum.
/// For Union-compatible enums with data variants, return UNION TypeId
/// to ensure correct type info handling in xlang mode struct field read/write.
pub fn gen_static_type_id(data_enum: &DataEnum) -> TokenStream {
    let is_union_compatible = is_union_compatible_enum(data_enum);
    let has_data_variants = data_enum
        .variants
        .iter()
        .any(|v| !matches!(v.fields, Fields::Unit));

    if is_union_compatible && has_data_variants {
        quote! { fory_core::TypeId::UNION }
    } else {
        quote! { fory_core::TypeId::ENUM }
    }
}

fn xlang_variant_read_branches(
    data_enum: &DataEnum,
    default_variant_value: u32,
) -> Vec<TokenStream> {
    let is_union_compatible = is_union_compatible_enum(data_enum);

    data_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, v)| {
            let ident = &v.ident;
            let mut tag_value = if is_union_compatible {
                enum_variant_id(v).unwrap_or(idx as u32)
            } else {
                idx as u32
            };
            if is_skip_enum_variant(v) {
                tag_value = default_variant_value;
            }

            match &v.fields {
                Fields::Unit => {
                    if is_union_compatible {
                        // Union-compatible: read null flag (matches Java/C++ Union with null value)
                        quote! {
                            #tag_value => {
                                let _ = context.reader.read_i8()?;
                                Ok(Self::#ident)
                            }
                        }
                    } else {
                        quote! {
                            #tag_value => Ok(Self::#ident),
                        }
                    }
                }
                Fields::Unnamed(fields_unnamed) => {
                    if is_union_compatible && fields_unnamed.unnamed.len() == 1 {
                        // Union-compatible single field: read value with ref_info=Tracking, type_info=true
                        let field_ty = &fields_unnamed.unnamed.first().unwrap().ty;
                        quote! {
                            #tag_value => {
                                use fory_core::serializer::Serializer;
                                let value = <#field_ty as Serializer>::fory_read(context, fory_core::types::RefMode::Tracking, true)?;
                                Ok(Self::#ident(value))
                            }
                        }
                    } else {
                        let default_fields: Vec<TokenStream> = fields_unnamed
                            .unnamed
                            .iter()
                            .map(|f| {
                                let ty = &f.ty;
                                quote! { <#ty as fory_core::ForyDefault>::fory_default() }
                            })
                            .collect();
                        quote! {
                            #tag_value => Ok(Self::#ident( #(#default_fields),* )),
                        }
                    }
                }
                Fields::Named(fields_named) => {
                    if is_union_compatible && fields_named.named.len() == 1 {
                        // Union-compatible single field: read value with ref_info=Tracking, type_info=true
                        let field = fields_named.named.first().unwrap();
                        let field_ident = field.ident.as_ref().unwrap();
                        let field_ty = &field.ty;
                        quote! {
                            #tag_value => {
                                use fory_core::serializer::Serializer;
                                let value = <#field_ty as Serializer>::fory_read(context, fory_core::types::RefMode::Tracking, true)?;
                                Ok(Self::#ident { #field_ident: value })
                            }
                        }
                    } else {
                        let default_fields: Vec<TokenStream> = fields_named
                            .named
                            .iter()
                            .map(|f| {
                                let field_ident = f.ident.as_ref().unwrap();
                                let ty = &f.ty;
                                quote! { #field_ident: <#ty as fory_core::ForyDefault>::fory_default() }
                            })
                            .collect();
                        quote! {
                            #tag_value => Ok(Self::#ident { #(#default_fields),* }),
                        }
                    }
                }
            }
        })
        .collect()
}

fn rust_variant_read_branches(
    data_enum: &DataEnum,
    default_variant_value: u32,
) -> Vec<TokenStream> {
    data_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, v)| {
            let ident = &v.ident;
            let mut tag_value = idx as u32;
            if is_skip_enum_variant(v) {
                tag_value = default_variant_value;
            }

            match &v.fields {
                Fields::Unit => {
                    quote! {
                        #tag_value => Ok(Self::#ident),
                    }
                }
                Fields::Unnamed(fields_unnamed) => {
                    let field_idents: Vec<Ident> = (0..fields_unnamed.unnamed.len())
                        .map(|i| Ident::new(&temp_var_name(i), proc_macro2::Span::call_site()))
                        .collect();

                    let read_fields: Vec<TokenStream> = fields_unnamed
                        .unnamed
                        .iter()
                        .enumerate()
                        .zip(field_idents.iter())
                        .map(|((idx, f), ident)| {
                            let field_name = idx.to_string();
                            gen_read_field(f, ident, &field_name)
                        })
                        .collect();

                    quote! {
                        #tag_value => {
                            #(#read_fields;)*
                            Ok(Self::#ident( #(#field_idents),* ))
                        }
                    }
                }
                Fields::Named(fields_named) => {
                    let fields_clone = syn::Fields::Named(fields_named.clone());
                    let source_fields = source_fields(&fields_clone);

                    let field_idents: Vec<_> = source_fields
                        .iter()
                        .map(|sf| sf.field.ident.as_ref().unwrap())
                        .collect();

                    let read_fields: Vec<_> = source_fields
                        .iter()
                        .zip(field_idents.iter())
                        .map(|(sf, ident)| {
                            let field_name = ident.to_string();
                            gen_read_field(sf.field, ident, &field_name)
                        })
                        .collect();

                    quote! {
                        #tag_value => {
                            #(#read_fields;)*
                            Ok(Self::#ident { #(#field_idents),* })
                        }
                    }
                }
            }
        })
        .collect()
}

fn rust_compatible_variant_read_branches(
    data_enum: &DataEnum,
    default_variant_value: u32,
) -> Vec<TokenStream> {
    data_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, v)| {
            let ident = &v.ident;
            let mut tag_value = idx as u32;
            if is_skip_enum_variant(v) {
                tag_value = default_variant_value;
            }

            match &v.fields {
                Fields::Unit => {
                    // Generate default value for this variant
                    let default_value = quote! { Self::#ident };

                    quote! {
                        #tag_value => {
                            // Unit variant should have variant_type == 0b0
                            if variant_type != 0b0 {
                                // Variant type mismatch: skip the data and use default
                                use fory_core::serializer::skip::skip_enum_variant;
                                skip_enum_variant(context, variant_type, &None)?;
                                return Ok(#default_value);
                            }
                            Ok(Self::#ident)
                        }
                    }
                }
                Fields::Unnamed(fields_unnamed) => {
                    // For unnamed enum variants, read using collection format (same protocol as tuple)
                    let field_idents: Vec<_> = (0..fields_unnamed.unnamed.len())
                        .map(|i| Ident::new(&temp_var_name(i), proc_macro2::Span::call_site()))
                        .collect();

                    let field_count = fields_unnamed.unnamed.len();

                    let read_fields: Vec<TokenStream> = fields_unnamed
                        .unnamed
                        .iter()
                        .enumerate()
                        .map(|(i, field)| {
                            let field_ident = &field_idents[i];
                            let field_ty = &field.ty;
                            quote! {
                                let #field_ident = if #i < len {
                                    use fory_core::serializer::Serializer;
                                    <#field_ty>::fory_read(context, fory_core::RefMode::NullOnly, true)?
                                } else {
                                    <#field_ty as fory_core::ForyDefault>::fory_default()
                                }
                            }
                        })
                        .collect();

                    // Generate default value for this variant
                    let default_fields: Vec<TokenStream> = fields_unnamed
                        .unnamed
                        .iter()
                        .map(|f| {
                            let ty = &f.ty;
                            quote! { <#ty as fory_core::ForyDefault>::fory_default() }
                        })
                        .collect();
                    let default_value = quote! { Self::#ident( #(#default_fields),* ) };

                    quote! {
                        #tag_value => {
                            // Unnamed variant should have variant_type == 0b1
                            if variant_type != 0b1 {
                                // Variant type mismatch: skip the data and use default
                                use fory_core::serializer::skip::skip_enum_variant;
                                skip_enum_variant(context, variant_type, &None)?;
                                return Ok(#default_value);
                            }
                            // Read collection format (same as tuple)
                            let len = context.reader.read_varuint32()? as usize;
                            let _header = context.reader.read_u8()?;

                            #(#read_fields;)*

                            // Skip any extra elements
                            use fory_core::serializer::skip::skip_any_value;
                            for _ in #field_count..len {
                                skip_any_value(context, true)?;
                            }

                            Ok(Self::#ident( #(#field_idents),* ))
                        }
                    }
                }
                Fields::Named(fields_named) => {
                    use crate::util::source_fields;

                    // Sort fields to match the meta type generation
                    let fields_clone = syn::Fields::Named(fields_named.clone());
                    let source_fields = source_fields(&fields_clone);

                    // Generate compatible read logic using gen_read_compatible_with_construction
                    let compatible_read_body =
                        crate::object::read::gen_read_compatible_with_construction(
                            &source_fields,
                            Some(ident),
                        );

                    // Generate default value for this variant
                    let default_fields: Vec<TokenStream> = fields_named
                        .named
                        .iter()
                        .map(|f| {
                            let field_ident = f.ident.as_ref().unwrap();
                            let ty = &f.ty;
                            quote! { #field_ident: <#ty as fory_core::ForyDefault>::fory_default() }
                        })
                        .collect();
                    let default_value = quote! { Self::#ident { #(#default_fields),* } };

                    quote! {
                        #tag_value => {
                            if variant_type != 0b10 {
                                // Variant type mismatch: peer didn't write meta for non-named variant
                                // Skip the data and use default
                                use fory_core::serializer::skip::skip_enum_variant;
                                skip_enum_variant(context, variant_type, &None)?;
                                return Ok(#default_value);
                            }
                            // Named variant should have variant_type == 0b10
                            // Read type meta inline using streaming protocol
                            let type_info = context.read_type_meta()?;
                            // Use gen_read_compatible logic
                            #compatible_read_body
                        }
                    }
                }
            }
        })
        .collect()
}

pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
    let is_union_compatible = is_union_compatible_enum(data_enum);
    let has_data_variants = data_enum
        .variants
        .iter()
        .any(|v| !matches!(v.fields, Fields::Unit));
    let default_variant_value = data_enum
        .variants
        .iter()
        .position(is_default_value_variant)
        .unwrap_or(0) as u32;

    let xlang_variant_branches: Vec<TokenStream> =
        xlang_variant_read_branches(data_enum, default_variant_value);
    let rust_variant_branches: Vec<TokenStream> =
        rust_variant_read_branches(data_enum, default_variant_value);
    let rust_compatible_variant_branches: Vec<TokenStream> =
        rust_compatible_variant_read_branches(data_enum, default_variant_value);

    // Get the default variant for compatible mode fallback
    let default_variant = data_enum
        .variants
        .iter()
        .nth(default_variant_value as usize)
        .or_else(|| data_enum.variants.first())
        .unwrap();

    let default_variant_ident = &default_variant.ident;
    let default_variant_construction = match &default_variant.fields {
        Fields::Unit => {
            quote! { Self::#default_variant_ident }
        }
        Fields::Unnamed(fields_unnamed) => {
            let default_fields: Vec<TokenStream> = fields_unnamed
                .unnamed
                .iter()
                .map(|f| {
                    let ty = &f.ty;
                    quote! { <#ty as fory_core::ForyDefault>::fory_default() }
                })
                .collect();
            quote! { Self::#default_variant_ident( #(#default_fields),* ) }
        }
        Fields::Named(fields_named) => {
            let default_fields: Vec<TokenStream> = fields_named
                .named
                .iter()
                .map(|f| {
                    let field_ident = f.ident.as_ref().unwrap();
                    let ty = &f.ty;
                    quote! { #field_ident: <#ty as fory_core::ForyDefault>::fory_default() }
                })
                .collect();
            quote! { Self::#default_variant_ident { #(#default_fields),* } }
        }
    };

    let unknown_xlang_branch = if is_union_compatible && has_data_variants {
        quote! {
            _ => {
                use fory_core::serializer::skip::skip_any_value;
                skip_any_value(context, true)?;
                if context.is_compatible() {
                    Ok(#default_variant_construction)
                } else {
                    return Err(fory_core::error::Error::unknown_enum("unknown enum value"));
                }
            }
        }
    } else {
        quote! {
            _ => {
                // Unknown variant: in compatible mode, return default; otherwise error
                if context.is_compatible() {
                    Ok(#default_variant_construction)
                } else {
                    return Err(fory_core::error::Error::unknown_enum("unknown enum value"));
                }
            }
        }
    };

    quote! {
        if context.is_xlang() {
            let ordinal = context.reader.read_varuint32()?;
            match ordinal {
                #(#xlang_variant_branches)*
                #unknown_xlang_branch
            }
        } else {
            if context.is_compatible() {
                let encoded_tag = context.reader.read_varuint32()?;
                let tag = encoded_tag >> 2;
                let variant_type = encoded_tag & 0b11;

                match tag {
                    #(#rust_compatible_variant_branches)*
                    _ => {
                        // Unknown variant in compatible mode: skip the data and use default variant
                        // variant_type: 0b0 = Unit, 0b1 = Unnamed, 0b10 = Named
                        use fory_core::serializer::skip::skip_enum_variant;
                        // For named variants, we don't have type_info yet, so pass None
                        // skip_enum_variant will read it from the stream
                        skip_enum_variant(context, variant_type, &None)?;
                        Ok(#default_variant_construction)
                    }
                }
            } else {
                let tag = context.reader.read_varuint32()?;
                match tag {
                    #(#rust_variant_branches)*
                    _ => return Err(fory_core::error::Error::unknown_enum("unknown enum value")),
                }
            }
        }
    }
}

pub fn gen_read_type_info(data_enum: &DataEnum) -> TokenStream {
    // Only use UNION TypeId for Union-compatible enums (unit or single-field variants)
    let is_union_compatible = is_union_compatible_enum(data_enum);
    let has_data_variants = data_enum
        .variants
        .iter()
        .any(|v| !matches!(v.fields, Fields::Unit));

    if is_union_compatible && has_data_variants {
        // Union-compatible with data: read typed/named union type info in xlang mode
        quote! {
            if context.is_xlang() {
                let expected_type_id = Self::fory_get_type_id(context.get_type_resolver())?;
                let type_info = context.read_any_type_info()?;
                let remote_type_id = type_info.get_type_id();
                if remote_type_id != expected_type_id {
                    return Err(fory_core::error::Error::type_mismatch(
                        expected_type_id as u32,
                        remote_type_id as u32,
                    ));
                }
                Ok(())
            } else {
                fory_core::serializer::enum_::read_type_info::<Self>(context)
            }
        }
    } else {
        quote! {
            fory_core::serializer::enum_::read_type_info::<Self>(context)
        }
    }
}
