use darling::FromDeriveInput;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::{Data, Error, Generics, Ident, Path};
use syn::{
    DataEnum, DeriveInput, Fields, GenericArgument, PathArguments, Type, TypePath, WhereClause, parse::Parse,
    parse_macro_input, parse_quote,
};

use crate::dtype_variant_path;

/// Parses the top-level `#[dtype(...)]` attribute applied to the enum.
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(dtype), supports(enum_any))] // Specify attribute name and support only enums
struct DTypeMacroArgs {
    /// The identifier of the enum (e.g., `MyEnum`).
    ident: Ident,
    /// The generic parameters of the enum (e.g., `<'a, T: Bound>`).
    generics: Generics,
    /// The body of the enum (variants and fields).
    /// Uses darling::ast::Data for potential future integration with variant/field attributes.
    // data: DarlingData<NestedVariant, NestedField>,

    // --- Configuration Fields ---
    /// **Required.** Path to the module/enum generated by `build_dtype_tokens`.
    /// Example: `"my_crate::tokens::MyTokens"`
    #[darling(default)]
    tokens: Option<String>,

    /// Optional. Identifier of the container type wrapping variant payloads.
    /// Example: `"Vec"`, `"Box"`
    #[darling(default)]
    container: Option<String>,

    /// Optional. Path to a trait that inner types of variants must implement.
    /// Example: `"my_crate::constraints::MyConstraint"`
    #[darling(default)]
    constraint: Option<String>,

    /// Optional. Identifier for the name of the generated closure-based matcher method.
    /// Example: `"match_variant"`
    #[darling(default)]
    matcher: Option<String>,
}

#[derive(Debug)]
struct ParsedVariantInfo {
    /// Identifier of the enum variant (e.g., `MyVariant`).
    variant_ident: Ident,
    /// Identifier of the corresponding token (e.g., `MyVariantVariant`).
    token_ident: Ident,
    /// The full type of the field in a tuple variant (e.g., `Vec<u16>` or `f64`).
    /// `None` for unit variants.
    full_field_type: Option<Type>,
    /// The inner payload type (e.g., `u16` from `Vec<u16>`, or `f64` if no container).
    /// `None` for unit variants.
    inner_type: Option<Type>,
    /// True if this is a unit variant (e.g., `MyUnitVariant`).
    is_unit: bool,
}

pub fn dtype_derive_impl(input: TokenStream) -> TokenStream {
    let dtype_variant_path = dtype_variant_path();

    // Parse the input token stream into a syn::DeriveInput AST node.
    let derive_input = parse_macro_input!(input as DeriveInput);

    // Parse the #[dtype(...)] attributes using darling.
    let args = match DTypeMacroArgs::from_derive_input(&derive_input) {
        Ok(args) => args,
        Err(e) => return e.write_errors().into(),
    };

    // Perform initial validation: ensure it's an enum.
    let enum_data = match &derive_input.data {
        Data::Enum(data) => data,
        _ => {
            return Error::new_spanned(
                &derive_input.ident,
                "DTypeEnum derive macro can only be used on enums.",
            )
            .to_compile_error()
            .into();
        }
    };

    // Parse string paths from darling config into syn types.
    let parse_result = parse_config_paths(&args);
    let ParsedPaths {
        tokens_path_opt,
        container_ident_opt,
        constraint_path_opt,
        matcher_ident_opt,
    } = match parse_result {
        Ok(paths) => paths,
        Err(e) => return e.to_compile_error().into(),
    };

    // Validate required 'tokens' path is present.
    let tokens_path = match tokens_path_opt {
        Some(path) => path,
        None => {
            return Error::new_spanned(
                &derive_input.ident,
                "Missing required `tokens` attribute path in #[dtype(...)]. \
                     Specify the path to the module generated by `build_dtype_tokens`.",
            )
            .to_compile_error()
            .into();
        }
    };

    // Parse enum variants and extract necessary info.
    let variant_parse_result = parse_variants(enum_data, &container_ident_opt);
    let parsed_variants = match variant_parse_result {
        Ok(variants) => variants,
        Err(e) => return e.to_compile_error().into(),
    };

    // Extract components for code generation.
    let enum_name = &args.ident;
    let generics = &args.generics;

    // Generate the different code blocks using helper functions.
    let token_validation_code = generate_token_validation(&tokens_path, &parsed_variants);
    let target_impls = generate_enum_variant_downcast(
        &dtype_variant_path,
        enum_name,
        generics,
        &parsed_variants,
        &tokens_path,
    );
    let constraint_impls = generate_enum_variant_constraint(
        &dtype_variant_path,
        enum_name,
        generics,
        &parsed_variants,
        &constraint_path_opt,
        &tokens_path, // Pass tokens_path
    );
    let from_impls = generate_from_impls(
        enum_name,
        generics,
        &parsed_variants,
        container_ident_opt.is_some(),
        &tokens_path,
    );
    let downcast_methods = generate_downcast_methods(&dtype_variant_path, enum_name, generics, &parsed_variants);
    let matcher_method = generate_matcher_method(
        &dtype_variant_path,
        enum_name,
        generics,
        &parsed_variants,
        &matcher_ident_opt,
        &tokens_path,
    );

    // Combine generated code fragments.
    let final_code = quote! {
        // Compile-time validation of token existence.
        #token_validation_code

        // Implementations of #dtype_variant_path::EnumVariantTarget trait.
        #target_impls

        // Implementations of the user-specified constraint trait.
        #constraint_impls

        // Implementations of From<PayloadType> for the enum.
        #from_impls

        // Implementation block containing downcast methods.
        #downcast_methods

        // Implementation block containing the matcher method.
        #matcher_method
    };

    // Return the final generated code.
    final_code.into()
}

//----------------------------------------------------------------------------
// 4. Helper Functions for Parsing and Validation
//----------------------------------------------------------------------------

/// Parses string paths/identifiers from the darling config struct into syn types.
#[derive(Default)]
struct ParsedPaths {
    tokens_path_opt: Option<Path>,
    container_ident_opt: Option<Ident>,
    constraint_path_opt: Option<Path>,
    matcher_ident_opt: Option<Ident>,
}

fn parse_config_paths(args: &DTypeMacroArgs) -> Result<ParsedPaths, Error> {
    // Helper closure to parse a string into a syn type T that implements Parse.
    // Associates errors with the span of the enum identifier for context.
    fn parse_string<T: Parse>(args: &DTypeMacroArgs, s_opt: &Option<String>, name: &str) -> Result<Option<T>, Error> {
        s_opt
            .as_ref()
            .map(|s| {
                syn::parse_str(s).map_err(|e| {
                    Error::new(
                        args.ident.span(),
                        format!("Failed to parse `{}` string \"{}\": {}", name, s, e),
                    )
                })
            })
            .transpose()
    }

    let tokens_path: Option<Path> = parse_string(args, &args.tokens, "tokens")?;
    let container_ident: Option<Ident> = parse_string(args, &args.container, "container")?;
    let constraint_path: Option<Path> = parse_string(args, &args.constraint, "constraint")?;
    let matcher_ident: Option<Ident> = parse_string(args, &args.matcher, "matcher")?;

    Ok(ParsedPaths {
        tokens_path_opt: tokens_path,
        container_ident_opt: container_ident,
        constraint_path_opt: constraint_path,
        matcher_ident_opt: matcher_ident,
    })
}
/// Parses enum variants, extracting types and validating structure.
fn parse_variants(enum_data: &DataEnum, container_ident: &Option<Ident>) -> Result<Vec<ParsedVariantInfo>, Error> {
    let mut variants_info = Vec::new();

    for variant in &enum_data.variants {
        let variant_ident = variant.ident.clone();
        // Assume token identifier is VariantNameVariant (adjust if needed).
        let token_ident = format_ident!("{}Variant", variant_ident, span = variant_ident.span());

        match &variant.fields {
            Fields::Unit => {
                variants_info.push(ParsedVariantInfo {
                    variant_ident,
                    token_ident,
                    full_field_type: None,
                    inner_type: None,
                    is_unit: true,
                });
            }
            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
                let field = fields.unnamed.first().unwrap();
                let full_field_type = field.ty.clone();
                let inner_type: Type;

                if let Some(container) = container_ident {
                    // Attempt to extract T from Container<T>
                    if let Type::Path(TypePath { path, .. }) = &full_field_type {
                        if let Some(segment) = path.segments.last() {
                            if segment.ident == *container {
                                if let PathArguments::AngleBracketed(args) = &segment.arguments {
                                    if args.args.len() == 1 {
                                        if let Some(GenericArgument::Type(ty)) = args.args.first() {
                                            inner_type = ty.clone();
                                        } else {
                                            return Err(Error::new_spanned(args, "Container expects a type argument"));
                                        }
                                    } else {
                                        return Err(Error::new_spanned(
                                            args,
                                            "Container expects exactly one type argument",
                                        ));
                                    }
                                } else {
                                    return Err(Error::new_spanned(
                                        segment,
                                        "Container expects angle bracketed arguments",
                                    ));
                                }
                            } else {
                                return Err(Error::new_spanned(
                                    segment,
                                    format!(
                                        "Expected container type `{}`, found `{}`",
                                        container, segment.ident
                                    ),
                                ));
                            }
                        } else {
                            return Err(Error::new_spanned(path, "Cannot get path segment"));
                        }
                    } else {
                        return Err(Error::new_spanned(
                            &full_field_type,
                            format!("Expected a path type matching container `{}`", container),
                        ));
                    }
                } else {
                    // No container specified, the full type is the inner type
                    inner_type = full_field_type.clone();
                }

                variants_info.push(ParsedVariantInfo {
                    variant_ident,
                    token_ident,
                    full_field_type: Some(full_field_type),
                    inner_type: Some(inner_type),
                    is_unit: false,
                });
            }
            Fields::Named(_) => {
                return Err(Error::new_spanned(
                    &variant.fields,
                    "DTypeEnum does not support variants with named fields.",
                ));
            }
            Fields::Unnamed(_) => {
                return Err(Error::new_spanned(
                    &variant.fields,
                    "DTypeEnum only supports tuple variants with exactly one field.",
                ));
            }
        }
    }
    Ok(variants_info)
}

//----------------------------------------------------------------------------
// 5. Helper Functions for Code Generation (`quote!`)
//----------------------------------------------------------------------------

/// Generates compile-time checks for token existence.
fn generate_token_validation(tokens_path: &Path, parsed_variants: &[ParsedVariantInfo]) -> TokenStream2 {
    let validation_checks = parsed_variants.iter().map(|v| {
        let token_ident = &v.token_ident;
        // This code runs at compile time inside the const block.
        // If the path #tokens_path::#token_ident is invalid, compilation fails here.
        quote! { let _ = #tokens_path::#token_ident; }
    });

    quote! {
        // This const block forces compile-time evaluation of the checks inside.
        const _: () = {
            // Ensure the compiler knows about the crate containing the tokens.
            // This might require adjustment based on how tokens_path is structured.
            // use #tokens_path; // This might be too broad or incorrect depending on path structure

            #(#validation_checks)*
        };
    }
}

/// Generates `impl #dtype_variant_path::EnumVariantTarget<...>` blocks.
fn generate_enum_variant_downcast(
    dtype_variant_path: &Path,
    enum_name: &Ident,
    generics: &Generics,
    parsed_variants: &[ParsedVariantInfo],
    tokens_path: &Path, // Add tokens_path parameter
) -> TokenStream2 {
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    let downcast_impls = parsed_variants.iter().filter_map(|v| {
        if v.is_unit {
            return None;
        } // Skip unit variants
        let variant_ident = &v.variant_ident;
        let token_ident = &v.token_ident;
        let full_field_type = v.full_field_type.as_ref()?; // Should always be Some for non-unit

        // Add bounds ensuring the inner type and original lifetimes outlive the target lifetime
        let where_clause_with_bounds = where_clause.cloned().unwrap_or_else(|| WhereClause {
            where_token: Default::default(),
            predicates: Default::default(),
        });

        // Use the full path to the token
        Some(quote! {
            // Implement the new #dtype_variant_path::EnumVariantDowncast trait
            impl #impl_generics #dtype_variant_path::EnumVariantDowncast<#tokens_path::#token_ident>
                for #enum_name #ty_generics #where_clause_with_bounds
            {
                type Target = #full_field_type;

                fn downcast_ref(&self) -> Option<&Self::Target> {
                    match self {
                        Self::#variant_ident(field_payload) => Some(field_payload),
                        _ => None,
                    }
                }

                fn downcast_mut(&mut self) -> Option<&mut Self::Target> {
                    match self {
                        Self::#variant_ident(field_payload) => Some(field_payload),
                        _ => None,
                    }
                }

                fn downcast(self) -> Option<Self::Target> {
                    match self {
                        Self::#variant_ident(field_payload) => Some(field_payload),
                        _ => None,
                    }
                }
            }
        })
    });

    quote! { #(#downcast_impls)* }
}
/// Generates `impl #dtype_variant_path::EnumVariantConstraint<...>` blocks if `constraint` is specified.
fn generate_enum_variant_constraint(
    dtype_variant_path: &Path,
    enum_name: &Ident,
    generics: &Generics,
    parsed_variants: &[ParsedVariantInfo],
    constraint_path: &Option<Path>,
    tokens_path: &Path, // Add tokens_path parameter
) -> TokenStream2 {
    let constraint_path = match constraint_path {
        Some(path) => path,
        None => return quote! {}, // No constraint specified
    };

    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    let constraint_impls = parsed_variants.iter().filter_map(|v| {
        if v.is_unit {
            return None;
        } // Skip unit variants
        let token_ident = &v.token_ident;
        let inner_type = v.inner_type.as_ref()?;

        // Add the constraint bound to the where clause
        let mut where_clause_with_bounds = where_clause.cloned().unwrap_or_else(|| WhereClause {
            where_token: Default::default(),
            predicates: Default::default(),
        });

        // Add constraint that inner_type implements the constraint_path
        where_clause_with_bounds
            .predicates
            .push(parse_quote!(#inner_type: #constraint_path));

        Some(quote! {
            // Implement #dtype_variant_path::EnumVariantConstraint for the enum with the token type
            // Use the full path to the token
            impl #impl_generics #dtype_variant_path::EnumVariantConstraint<#tokens_path::#token_ident>
                for #enum_name #ty_generics #where_clause_with_bounds
            {
                // Set the Constraint associated type to the inner type
                type Constraint = #inner_type;
            }
        })
    });

    quote! { #(#constraint_impls)* }
}

/// Generates `impl From<FieldType>` blocks.
fn generate_from_impls(
    enum_name: &Ident,
    generics: &Generics,
    parsed_variants: &[ParsedVariantInfo],
    has_container: bool,
    tokens_path: &Path,
) -> TokenStream2 {
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    let payload_from_impls = parsed_variants.iter().filter_map(|v| {
        if v.is_unit {
            return None;
        } // Skip unit variants
        let variant_ident = &v.variant_ident;
        let full_field_type = v.full_field_type.as_ref()?; // Type in the variant tuple, e.g., Vec<u16> or f64
        let inner_type = v.inner_type.as_ref()?; // Inner type, e.g., u16 or f64

        if has_container {
            // When container is used, generate From<FullFieldType> -> EnumName
            Some(quote! {
                impl<#impl_generics> From<#full_field_type> for #enum_name #ty_generics #where_clause {
                    fn from(value: #full_field_type) -> Self {
                        Self::#variant_ident(value)
                    }
                }
            })
        } else {
            // When no container is used, full_field_type and inner_type are the same
            // So only generate one implementation
            Some(quote! {
                impl<#impl_generics> From<#inner_type> for #enum_name #ty_generics #where_clause {
                    fn from(value: #inner_type) -> Self {
                        Self::#variant_ident(value)
                    }
                }
            })
        }
    });

    // Generate From impls for unit variants converting from token types
    let unit_from_impls = parsed_variants.iter().filter_map(|v| {
        if !v.is_unit {
            return None;
        }
        let variant_ident = &v.variant_ident;
        let token_ident = &v.token_ident;

        Some(quote! {
            impl #impl_generics From<#tokens_path::#token_ident> for #enum_name #ty_generics #where_clause {
                fn from(_: #tokens_path::#token_ident) -> Self {
                    Self::#variant_ident
                }
            }
        })
    });

    // Check if all variants are unit types
    let all_unit_variants = parsed_variants.iter().all(|v| v.is_unit);

    // Generate from_variant implementation if all variants are unit types
    let from_variant_impl = if all_unit_variants {
        quote! {
            impl #impl_generics #enum_name #ty_generics #where_clause {
                /// Creates a new instance of the enum from a variant token.
                /// The variant token must implement Default.
                pub fn from_variant<V>() -> Self
                where
                    Self: From<V>,
                    V: Default,
                {
                    Self::from(V::default())
                }
            }
        }
    } else {
        quote! {}
    };

    quote! {
        #(#payload_from_impls)*
        #(#unit_from_impls)*
        #from_variant_impl
    }
}

/// Generates `downcast_ref`, `downcast_mut`, `downcast` methods using the #dtype_variant_path::EnumVariantDowncast trait.
fn generate_downcast_methods(
    dtype_variant_path: &Path,
    enum_name: &Ident,
    generics: &Generics,
    _parsed_variants: &[ParsedVariantInfo],
) -> TokenStream2 {
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    quote! {
        impl<#impl_generics> #enum_name #ty_generics #where_clause {
            /// Attempts to downcast to a shared reference to the target type if the enum holds the
            /// variant corresponding to token type `Token`.
            pub fn downcast_ref<Token>(&self) -> Option<&<Self as #dtype_variant_path::EnumVariantDowncast<Token>>::Target>
            where
                Self: #dtype_variant_path::EnumVariantDowncast<Token>
            {
                <Self as #dtype_variant_path::EnumVariantDowncast<Token>>::downcast_ref(self)
            }

            /// Attempts to downcast to a mutable reference to the target type if the enum holds the
            /// variant corresponding to token type `Token`.
            pub fn downcast_mut<Token>(&mut self) -> Option<&mut <Self as #dtype_variant_path::EnumVariantDowncast<Token>>::Target>
            where
                Self: #dtype_variant_path::EnumVariantDowncast<Token>
            {
                <Self as #dtype_variant_path::EnumVariantDowncast<Token>>::downcast_mut(self)
            }

            /// Attempts to downcast to an owned target type if the enum holds the
            /// variant corresponding to token type `Token`, consuming the enum.
            /// Returns `Some(Target)` on success, or `None` if the enum doesn't hold the expected variant.
            pub fn downcast<Token>(self) -> Option<<Self as #dtype_variant_path::EnumVariantDowncast<Token>>::Target>
            where
                Self: #dtype_variant_path::EnumVariantDowncast<Token>,
                Self: Sized // Required for moving self
            {
                <Self as #dtype_variant_path::EnumVariantDowncast<Token>>::downcast(self)
            }
        }
    }
}

/// Generates a macro for pattern matching on enum variants if `matcher` name is provided.
fn generate_matcher_method(
    dtype_variant_path: &Path,
    enum_name: &Ident,
    _generics: &Generics,
    parsed_variants: &[ParsedVariantInfo],
    matcher_ident: &Option<Ident>,
    tokens_path: &Path,
) -> TokenStream2 {
    let matcher_name = match matcher_ident {
        Some(ident) => ident,
        None => return quote! {}, // No matcher name specified
    };

    // Check if all variants are unit variants
    let all_unit_variants = parsed_variants.iter().all(|v| v.is_unit);

    // Generate the hidden internal macro name with underscore prefix
    let internal_matcher_name = format_ident!("_{}", matcher_name);

    // Check if tokens_path starts with "crate" to determine if we should use $crate
    let use_token_path_crate_macro = tokens_path
        .segments
        .first()
        .map(|seg| seg.ident == "crate")
        .unwrap_or(false);

    // Check if dtype_variant_path starts with "crate" to determine if we should use $crate
    let use_dtype_variant_path_crate_macro = dtype_variant_path
        .segments
        .first()
        .map(|seg| seg.ident == "crate")
        .unwrap_or(false);

    // Generate match arms for the macro
    let generate_arms = |include_inner: bool, include_dest: bool, dest_constraint: bool| {
        parsed_variants.iter().map(move |v| {
            let variant_ident = &v.variant_ident;
            let token_ident = &v.token_ident;
            let inner_type = v.inner_type.as_ref().map(|ty| quote! { #ty }).unwrap_or(quote! { () });

            // Generate the token type with proper path
            let token_type_path = if use_token_path_crate_macro {
                // Extract the rest of the path after "crate::"
                let mut rest_path = TokenStream2::new();
                for segment in tokens_path.segments.iter().skip(1) {
                    let ident = &segment.ident;
                    let args = &segment.arguments;
                    rest_path.extend(quote! { :: #ident #args });
                }

                // Use $crate directly in the quoted code
                quote! { $crate #rest_path :: #token_ident }
            } else {
                quote! { #tokens_path :: #token_ident }
            };

            let dtype_variant_path = if use_dtype_variant_path_crate_macro {
                // Extract the rest of the path after "crate::"
                let mut rest_path = TokenStream2::new();
                for segment in dtype_variant_path.segments.iter().skip(1) {
                    let ident = &segment.ident;
                    let args = &segment.arguments;
                    rest_path.extend(quote! { :: #ident #args });
                }

                // Use $crate directly in the quoted code
                quote! { $crate #rest_path  }
            } else {
                quote! { #dtype_variant_path  }
            };

            // Common type declarations - skip generic type for all-unit variants
            let type_declarations = if all_unit_variants {
                quote! {
                    #[allow(unused)]
                    type $token_type = #token_type_path;
                }
            } else {
                let inner = include_inner.then_some(quote!{
                    #[allow(unused)]
                    type $generic = #inner_type;
                }).unwrap_or_default();

                quote! {
                    #inner
                    #[allow(unused)]
                    type $token_type = #token_type_path;
                }
            };

            let pattern = match (include_inner, v.is_unit) {
                (_, true) => quote! { #enum_name::#variant_ident },
                (false, false) => quote! { #enum_name::#variant_ident(_) },
                (true, false) => quote! { #enum_name::#variant_ident($inner) },
            };

            let inner_decl = match (include_inner, v.is_unit) {
                (false, _) | (true, false) => quote! {},
                (true, true) => (!all_unit_variants)
                    .then_some(quote! {
                        #[allow(unused)]
                        let $inner = #inner_type;
                    })
                    .unwrap_or_default(),
            };

            let dest_generic = include_dest
                .then_some(quote! {
                    #[allow(unused)]
                    type $dest_generic = <$dest_enum as #dtype_variant_path::EnumVariantDowncast<#token_type_path>>::Target;
                })
                .unwrap_or_default();

            let dest_constraint = dest_constraint
                .then_some(quote! {
                    #[allow(unused)]
                    type $dest_constraint = <$dest_enum as #dtype_variant_path::EnumVariantConstraint<#token_type_path>>::Constraint;
                })
                .unwrap_or_default();

            quote! {
                #pattern => {
                    #inner_decl
                    #type_declarations
                    #dest_generic
                    #dest_constraint

                    #[allow(unused_braces)]
                    $body
                }
            }
        })
    };

    let generate_macro_arms = |include_inner: bool, include_dest: bool, dest_constraint: bool| {
        let tuple_variant_arms = generate_arms(include_inner, include_dest, dest_constraint);
        let source_enum_type = if include_inner {
            quote! {
                $enum_:ident<$generic:ident, $token_type:ident>
            }
        } else {
            quote! {
                $enum_:ident<$token_type:ident>
            }
        };

        let macro_arm_inner = include_inner.then_some(quote!(($inner:ident))).unwrap_or_default();

        let dest_enum_type = match (include_dest, dest_constraint) {
            (true, true) => quote! {
                , $dest_enum:ident<$dest_generic:ident, $dest_constraint:ident>
            },
            (true, false) => quote! {
                , $dest_enum:ident<$dest_generic:ident>
            },
            (false, _) => quote!(),
        };

        quote! {
            ($value:expr, #source_enum_type #macro_arm_inner #dest_enum_type => $body:block) => {
                match $value {
                    #(#tuple_variant_arms)*
                }
            };
        }
    };

    if all_unit_variants {
        let no_dest = generate_macro_arms(false, false, false);
        let dest_no_constraint = generate_macro_arms(false, true, false);
        let dest_constraint = generate_macro_arms(false, true, true);

        // Generate the macro definition
        quote! {
                #[doc(hidden)]
                #[macro_export]
                macro_rules! #internal_matcher_name {
                    #no_dest
                    #dest_no_constraint
                    #dest_constraint
                }
                pub use #internal_matcher_name as #matcher_name;
        }
    } else {
        let no_inner_no_dest = generate_macro_arms(false, false, false);
        let inner_no_dest = generate_macro_arms(true, false, false);
        let inner_dest_no_constraint = generate_macro_arms(true, true, false);
        let inner_dest_constraint = generate_macro_arms(true, true, true);
        let no_inner_dest_no_constraint = generate_macro_arms(false, true, false);
        let no_inner_dest_constraint = generate_macro_arms(false, true, true);

        // Generate the macro definition
        quote! {
                #[doc(hidden)]
                #[macro_export]
                macro_rules! #internal_matcher_name {
                    #no_inner_no_dest
                    #inner_no_dest
                    #inner_dest_no_constraint
                    #inner_dest_constraint
                    #no_inner_dest_no_constraint
                    #no_inner_dest_constraint
                }
                pub use #internal_matcher_name as #matcher_name;
        }
    }
}
